Ваш метод ожидает возврата 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 и ограничен временем жизни, соответствующим запросу, то это не должно вызывать никаких проблем. Просто будьте осторожны с контекстами, оставленными открытыми дольше, чем нужно.