Как реализовать метод добавления связанных данных в ядро ​​EF? - PullRequest
0 голосов
/ 01 апреля 2019

Как реализовать метод добавления связанных данных в EF core? Какие объекты нужно отправить в метод add и что он должен вернуть? Мне нужно добавить связанные данные в БД, например:

 {
        "productId": 0,
        "number": "xxx",
        "amount": 5.65,
        "primeCost": 20.33,
        "productTypeId": 0,
        "parameters": [
            {
                "id": 0,
                "name": "Type",
                "value": null
            },
            {
                "id": 3,
                "name": "steel grade",
                "value": "CK45"
            },
            {
                "id": 4,
                "name": "diameter",
                "value": "40"
            }
        ]
    }

Это мои модельные классы:

public class Product //: BaseObject
{
    public int Id { get; set; }

    public string Name { get; set; }
    public string Number { get; set; }
    public double Amount { get; set; }
    public double PrimeCost { get; set; }

    [ForeignKey("ProductTypeId")]
    public int  ProductTypeId {  get; set; }
    public virtual ProductType ProductType { get; set; }

    public ICollection<ProductParameter> ProductParameters { get; set; } = new List<ProductParameter>();
}
public class ProductType //: BaseObject
{   
    public int Id { get; set; }

    public string NameType { get; set; }

    public ICollection<Parameter> Parameters { get; set; } = new List<Parameter>();
    public ICollection<Product> Products { get; set; } = new List<Product>();
}
 public class Parameter //: BaseObject
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("ProductTypeId")]
    public int ProductTypeId { get; set; }
    public ProductType ProductType { get; set; }

    public ICollection<ProductParameter> ProductParameters { get; set; } = new List<ProductParameter>();
}
 public class ProductParameter //: BaseObject
{
    public int Id { get; set; }

    public int ProductId { get; set; }
    public virtual Product Product { get; set; }

    public int ParameterId { get; set; }
    public virtual Parameter Parameter { get; set; }


    public string Value { get; set; }

}

Это мои классы DTO:

public class ProductDTO
{   

    public int ProductId { get; set; }
    public string Number { get; set; }
    public double Amount { get; set; }
    public double PrimeCostEUR { get; set; }

    public int ProductTypeId { get; set; }
    public string NameType { get; set; }

    public ICollection<ParameterDTO> Parameters { get; set; } = new List<ParameterDTO>();
}
public class ParameterDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }

}

Моя реализация метода добавления связанных данных в БД:

public async Task<IEnumerable<ProductDTO>> AddProducts(ProductDTO ProductDTO, 
        List<ParameterDTO> ParameterDTO)
    {
        var EntryProduct = await _context.Products.FindAsync(ProductDTO.ProductId);

        if (EntryProduct == null)
        {

            _context.Products.Add(new Product
            {
                Id = ProductDTO.ProductId,
                Number = ProductDTO.Number,
                Amount = ProductDTO.Amount,
                PrimeCostEUR = ProductDTO.PrimeCostEUR,
            });
            _context.SaveChanges();

            foreach (var i in ParameterDTO)
            {
                var EntryParameter = await _context.Parameters.FindAsync(i.Id);
                if (EntryParameter != null)
                {
                    _context.ProductParameters.Add(
                      new ProductParameter
                      {
                          ProductId = ProductDTO.ProductId,
                          ParameterId = i.Id,
                          Value = i.Value
                      });
                    _context.SaveChanges();
                }
            }

        }

        return ProductDTO;
    }

Я получаю следующее исключение: ошибка компилятора:

Код серьезности Описание Состояние подавления строки файла проекта Ошибка CS0266 Невозможно неявно преобразовать тип «GdmStore.DTO.ProductDTO» в «System.Collections.Generic.IEnumerable». Существует явное преобразование (вам не хватает приведения?)

1 Ответ

1 голос
/ 01 апреля 2019

Ваш метод ожидает возврата IEnumerable, но вы возвращаете только один DTO для продукта, который был передан.

Подпись должна быть:

public async Task<ProductDTO> AddProducts(ProductDTO ProductDTO, List<ParameterDTO> ParameterDTO)

Учитывая, что ProductDTO имеет коллекцию ParameterDTO, нужен ли второй аргумент? (Похоже, что параметры будут отправлены дважды)

С вашими определениями сущностей я вижу несколько проблем:

[ForeignKey("ProductTypeId")]
public int  ProductTypeId {  get; set; }
public virtual ProductType ProductType { get; set; }

должно быть

[ForeignKey("ProductType")] // FK to the reference property.
public int  ProductTypeId {  get; set; }
public virtual ProductType ProductType { get; set; }

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

Как Product, так и Parameter не должны иметь ссылок на ProductType, из того, что я вижу, вероятно, он должен быть только в Product, чтобы избежать проблем денормализации. (Продукт с параметрами с различными установленными типами продукта.)

При работе со свойствами навигации я рекомендую удалить свойство FK из сущности и использовать свойства отображения (EF6) / shadow. (EF Core)

Например:

 public class ProductParameter 
{
    public int Id { get; set; }

    public virtual Product Product { get; set; } // No ProductId/ParameterId
    public virtual Parameter Parameter { get; set; }

    public string Value { get; set; }
}

modelBuilder.Entity<Product>()
    .HasMany(x => x.ProductParameters)
    .WithOne(x => x.Product)
    .HasForeignKey("ProductId"); // Sets up a shadow property.

Проблема с отображением ФК заключается в том, что существует 2 источника правды для справки. ProductParameter.ProductId против ProductParameter.Product.Id. Обычно они указывают на одно и то же значение, но код может зависеть от одного пути от другого и приводить к ошибкам согласованности, если один изменяется без другого.

Используйте асинхронные операции экономно. Если вы извлекаете одну запись по идентификатору или выполняете какую-либо другую относительно быструю операцию, не используйте асинхронную синхронизацию, так как при регистрации продолжения происходит снижение производительности. (Быстрее сделать только синхронный вызов) Async ориентирован на операции, которые должны занять некоторое время. (Т. Е. Больше секунды)

Наконец, код может работать, но он не очень эффективно использует EF, настраивая все эти сущности по отдельности, и вы, как правило, не хотите вызывать SaveChanges несколько раз, чтобы гарантировать, что данные фиксируются все вместе или вообще ничего, если есть проблема.

   var EntryProduct = _context.Products.Find(ProductDTO.ProductId);

    if (EntryProduct != null)
        return ProductDTO;

    var product = new Product
    {
        Id = ProductDTO.ProductId,
        Number = ProductDTO.Number,
        Amount = ProductDTO.Amount,
        PrimeCostEUR = ProductDTO.PrimeCostEUR,
    };

    var parameterIds = ParameterDTO.Select(x => x.Id).ToList();
    var parametersToAdd = context.Parameters
        .Where(x => parameterIds.Contains(x.ParameterId))
        .Select(x => new ProductParameter
        {
            Product = product,
            Parameter = x
        }).ToList();

    product.ProductParameters.AddRange(parametersToAdd);
    await _context.SaveChangesAsync();

    return ProductDTO;

Я не рекомендую использовать переменную уровня модуля для DbContext (_context), так как контекст должен быть недолговечным, чтобы помочь избежать потенциальных проблем, которые один рабочий процесс намеревается сохранить, в то время как другой код может этого не делать. Если он внедрен контейнером IoC и ограничен временем жизни, соответствующим запросу, то это не должно вызывать никаких проблем. Просто будьте осторожны с контекстами, оставленными открытыми дольше, чем нужно.

...