Asp.net core 2.0. Экземпляр типа объекта «X» не может быть отслежен, поскольку уже отслеживается другой экземпляр со значением ключа «Id: x».

Я работал над проектом сайта магазина, используя основные спа-шаблоны 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);

person Mark Perry    schedule 31.08.2017    source источник


Ответы (1)


Я считаю, что вы можете позволить EF обрабатывать отслеживание.

  public virtual bool Create(T item)
    {
        try
        {
            _context.Add(item);
            _context.SaveChanges();
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

Это позволяет вам сохранить всю структуру объекта, не беспокоясь о прикреплении элементов.

var newProduct = new Product();
newProduct.Categories.Add(cat);
newProduct.Attributes.Add(att);
newProduct.Variations.Add(vari);
Create(newProduct);
person Lee Reitz    schedule 31.08.2017
comment
к сожалению нет. Я вставляю списки существующих сущностей в новый продукт, EF пытается повторно добавить их все и терпит неудачу из-за уже существующего идентификатора (я полагаю, что по умолчанию для всех свойств установлено значение «Добавить»), поэтому я использовал присоединение подход, который работает, если ни один из ProductVariations не имеет ProductAttribute общего с Product, поэтому мне нужно предотвратить второй экземпляр чего-либо отслеживаемого. - person Mark Perry; 01.09.2017