Клонирование объекта и сохранение клона в базу данных приводит к тому, что исходный объект теряет реляционные данные. - PullRequest
0 голосов
/ 25 мая 2018

Когда я пытаюсь клонировать Product -объект и сохранить клон в базе данных, исходный объект теряет все свои реляционные данные, такие как ProductPropertyOptionForProducts, IdentifierForProducts и InCategories.

Это модель продукта:

public class Product
{
    public int Id { get; set; }
    public int ProductGroupId { get; set; }
    public int ProductGroupSortOrder { get; set; }

    [Required, MaxLength(30), MinLength(4)]     public string Title { get; set; }
    [MaxLength(200)]                            public string Info { get; set; }
    [MaxLength(4000)]                           public string LongInfo { get; set; }
    [Required, DataType(DataType.Currency)]     public decimal Price { get; set; }
                                                public int Weight { get; set; }
                                                public int ProductTypeId { get; set; }
    public ICollection<ProductImage> Images { get; set; }

    // Selected property options for this product
    public ICollection<PropertyOptionForProduct> ProductPropertyOptionForProducts { get; set; }

    // A product can have multiple identifiers (EAN, ISBN, product number, etc.)
    public ICollection<IdentifierForProduct> IdentifierForProducts { get; set; }

    public ProductType Type { get; set; }
    public ICollection<FrontPageProduct> InFrontPages { get; set; }
    public ICollection<ProductInCategory> InCategories { get; set; }
}

Некоторые из родственных моделей:

public class ProductInCategory
// A linking table for which products belongs to which categories
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public int ProductCategoryId { get; set; }
    public int SortOrder { get; set; }

    // Nav.props.:
    public Product Product { get; set; }
    public ProductCategory ProductCategory { get; set; }
}

public class PropertyOptionForProduct
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public int ProductPropertyId { get; set; }
    public int ProductPropertyOptionId { get; set; }

    // Nav.props.
    public Product Product { get; set; }
    public ProductPropertyOption ProductPropertyOption { get; set; }
}

public class IdentifierForProduct
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public int ProductIdentifierId { get; set; }
    [StringLength(30), MaxLength(30)]
    public string Value { get; set; }

    public ProductIdentifier ProductIdentifier { get; set; }
    public Product Product { get; set; }
}

Оригинал загружается так:

public async Task<Product> GetProduct(int Id)
{
    Product DbM = await _context.Products
        .Include(ic => ic.InCategories)
            .ThenInclude(pc => pc.ProductCategory)
        .Include(t => t.Type)
            .ThenInclude(iit => iit.Identifiers) //ProductIdentifiersInTypes
                .ThenInclude(i => i.Identifier) // ProductIdentifiers
                    .ThenInclude(ifp => ifp.ProductIdentifiers) // IdentifiersForProducts
        .Include(t => t.Type)
            .ThenInclude(pit => pit.Properties) // ProductPropertiesInTypes
                .ThenInclude(p => p.Property) // ProductProperties
                    .ThenInclude(po => po.Options) // ProductPropertyOptions
        .Include(p => p.ProductPropertyOptionForProducts)
        .Where(p => p.Id == Id)
        .SingleOrDefaultAsync();
    return DbM;
}

Thisэто метод клонирования:

private async Task<Product> MakeClone(Product Original)
{
    Product Clone = new Product
    {
        ProductGroupId = Original.ProductGroupId,
        ProductGroupSortOrder = Original.ProductGroupSortOrder + 1,
        IdentifierForProducts = Original.IdentifierForProducts,
        Images = Original.Images,
        InCategories = Original.InCategories,
        Info = Original.Info,
        InFrontPages = Original.InFrontPages,
        LongInfo = Original.LongInfo,
        Price = Original.Price,
        ProductPropertyOptionForProducts = Original.ProductPropertyOptionForProducts,
        ProductTypeId = Original.ProductTypeId,
        Title = Original.Title,
        Type = Original.Type,
        Weight = Original.Weight
    };
    _context.Add(Clone);
    await _context.SaveChangesAsync();
    return Clone; // and go to the Edit-view.
}

Теперь клон обладает всеми свойствами исходного продукта, но в оригинале удалены все его реляционные данные.В базе данных похоже, что реляционные данные клона заменили исходные.

ОБНОВЛЕНИЕ

В соответствии с ответом Георга, я изменил свой MakeClone() -методк этому:

private Product MakeClone(Product Original)
{
    List<IdentifierForProduct> identifiers = Original
            .IdentifierForProducts
            .Select(i => CloneIdentifierForProduct(i))
            .ToList();
    List<PropertyOptionForProduct> propertyOptions = Original
            .ProductPropertyOptionForProducts
            .Select(o => ClonePropertyOptionForProduct(o))
            .ToList();
    List<ProductInCategory> inCategories = Original
            .InCategories
            .Select(c => CloneProductInCategory(c))
            .ToList();
    List<FrontPageProduct> inFrontPages = Original
            .InFrontPages
            .Select(f => CloneFrontPageProduct(f))
            .ToList();
    List<ProductImage> images = Original.Images.Select(i => CloneProductImage(i)).ToList();
    Product Clone = new Product
    {
        ProductGroupId = Original.ProductGroupId,
        ProductGroupSortOrder = Original.ProductGroupSortOrder + 1,
        Info = Original.Info,
        LongInfo = Original.LongInfo,
        Price = Original.Price,
        ProductTypeId = Original.ProductTypeId,
        Title = Original.Title,
        Type = Original.Type,
        Weight = Original.Weight,
        IdentifierForProducts = identifiers,
        ProductPropertyOptionForProducts = propertyOptions,
        InCategories = inCategories,
        InFrontPages = inFrontPages,
        Images = images
    };
    _context.Add(Clone);
    // fix FKs
    foreach (var ifp in Clone.IdentifierForProducts) ifp.ProductId = Clone.Id;
    foreach (var ofp in Clone.ProductPropertyOptionForProducts) ofp.ProductId = Clone.Id;
    foreach (var pic in Clone.InCategories) pic.ProductId = Clone.Id;
    foreach (var fpp in Clone.InFrontPages) fpp.ProductId = Clone.Id;
    foreach (var pi in Clone.Images) pi.ProductId = Clone.Id;
    // Lagre klonen i databasen:
    _context.SaveChangesAsync();
    return Clone;
}

... и добавлены отдельные методы для клонирования каждой из связанных данных (я не думаю, что мне нужно показывать все пять методов):

private IdentifierForProduct CloneIdentifierForProduct(IdentifierForProduct ifp)
{
    IdentifierForProduct IFP = new IdentifierForProduct
    {
        Product = ifp.Product,
        ProductId = ifp.ProductId,
        ProductIdentifier = ifp.ProductIdentifier,
        ProductIdentifierId = ifp.ProductIdentifierId,
        Value = ifp.Value
    };
    return IFP;
}

Теперь я получаю ArgumentNullException на создание дочерних списков.

Может ли это быть связано с тем фактом, что, например, IdentifierForProduct также имеет дочернее свойство (которое я также хочу клонировать)

1 Ответ

0 голосов
/ 25 мая 2018

Как заметил Стив, вы просто устанавливаете ссылку на свойства коллекции оригинала, а не клонируете коллекцию.Если отношение коллекции не определено как многие: многие, это удаляет связанные сущности из оригинала и добавляет их в клон.

Например, чтобы клонировать коллекцию IdentifierForProducts, необходимо клонироватькаждый элемент, а затем добавить их в коллекцию клона.

Product clone = new Product {
    IdentifierForProducts = Original.IdentifierForProducts.Select(ifp => MakeClone(ifp)).ToList(),
    // other properties ....
};

// fix FKs after cloning
foreach (var ifp in clone.IdentifierForProducts) {
    ifp.ProductId = clone.Id;
}

с MakeClone<IdentifierForProduct>(IdentifierForProduct original) аналогично MakeClone<Product>(Product original).

...