Я работал над проектом сайта магазина, используя основные спа-шаблоны asp.net, поставляемые с последней версией VS2017, и столкнулся с проблемой, с которой раньше не сталкивался, возможно, потому, что до сих пор мои приложения были довольно простыми!
Я знаю, в чем проблема и где, я просто не могу это исправить. У меня есть модель продукта, в которой есть набор «Атрибутов» и набор «Вариаций» (различный размер цвета и т. Д.), И эти варианты также имеют атрибуты, поэтому, если один и тот же атрибут отображается в варианте (VAttributes), как уже в основных "Атрибутах" получаю ошибку
InvalidOperationException: экземпляр типа объекта «ProductAttribute» не может быть отслежен, поскольку другой экземпляр со значением ключа «Id:2» уже отслеживается. При присоединении существующих сущностей убедитесь, что присоединен только один экземпляр сущности с заданным значением ключа.
Лучший ответ, который я нашел, был здесь: https://stackoverflow.com/a/19695833/6749293
К сожалению, даже при вышеописанной проверке у меня вылезла ошибка, я даже пытался составить список прикрепляемых атрибутов, и если ваттрибут совпадал с одним из пунктов в списке, я его не прикреплял. На самом деле я обнаружил, что даже если я не присоединяю (_context.attach()) ни один из vAttributes, он все равно выдает ошибку!.
Вот код, о котором идет речь:
public async Task<Product> Create(Product product)
{
try
{
foreach (var variation in product.Variations)
{
foreach (var vAttr in variation.VAttributes)
{
bool isDetached = _context.Entry(vAttr).State == EntityState.Detached;
if (isDetached)
_context.Attach(vAttr);
}
}
foreach (var attribute in product.Attributes)
{
bool isDetached = _context.Entry(attribute).State == EntityState.Detached;
if (isDetached)
_context.Attach(attribute);
}
foreach (var category in product.Categories)
{
_context.Attach(category);
_context.Attach(category).Collection(x => x.Children);
}
_context.Products.Add(product);
await Save();
return product;
}
catch (Exception)
{
throw;
}
}
Модели для трех объектов следующие:
public class Product
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
public string StockRef { get; set; }
public DateTime? LastModified { get; set; }
//image needed
public ICollection<ProductCategory> Categories { get; set; }
public ICollection<ProductAttribute> Attributes { get; set; }
public ICollection<ProductVariation> Variations { get; set; }
public Product()
{
Attributes = new List<ProductAttribute>();
Variations = new List<ProductVariation>();
Categories = new List<ProductCategory>();
}
}
Вариация:
public class ProductVariation
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime? LastModified { get; set; }
public virtual ICollection<ProductAttribute> VAttributes { get; set; }
//needs images
public decimal VPrice { get; set; }
public string VStockRef { get; set; }
}
Наконец атрибут:
public class ProductAttribute
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("AttributeCategory")]
public int AttributeCategoryId { get; set; }
public virtual AttributeCategory AttributeCategory { get; set; }
}
Большая часть помощи, которую я нашел при поиске, была больше связана с тем, что репо были введены как синглтоны, или методы HttpPut, где код проверял существование без .AsNoTracking(), или это была ошибка, что у них каким-то образом был второй экземпляр, где я знаю о втором экземпляре, я просто не знаю, как предотвратить его отслеживание!
РЕДАКТИРОВАТЬ: я обнаружил, что добавление внешнего ключа в модель ProductVariation к создаваемому продукту не удалось, поскольку это был всего лишь временный ключ!? в любом случае удалил его из вариационной модели, поэтому обновил мой код. Также подумал, что я добавлю одну из моих более ранних неудачных попыток, которая привела ко всем циклам foreach.
_context.AttachRange(product.Attributes);
_context.AttachRange(product.Categories);
_context.AttachRange(product.Variations);
_context.Add(product);