, как упомянуто в командах, я хотел бы поделиться моим обновленным и рабочим проектом для дальнейшего использования ....
MainWindow, которое генерирует тестовые данные и выполняет чтение / запись в DB
public partial class MainWindow : Window
{
private List<Product> productList_;
public MainWindow()
{
GenerateProducts();
InitializeComponent();
}
private void InsertFirst_Click(object sender, RoutedEventArgs e)
{
DbHandler.StoreProduct(productList_[0]);
}
private void InsertSecond_Click(object sender, RoutedEventArgs e)
{
DbHandler.StoreProduct(productList_[1]);
}
private void Read_Click(object sender, RoutedEventArgs e)
{
var productList = DbHandler.GetAllProducts();
}
private void GenerateProducts()
{
Manufacturer manufactuer1 = new Manufacturer() { Name = "mainManuf 1" };
Supplier supplier1 = new Supplier() { Name = "first Supplier" };
Supplier supplier2 = new Supplier() { Name = "second Supplier" };
Supplier supplier3 = new Supplier() { Name = "third Supplier" };
Product firstProduct = new Product() { Name = "Product 1", Manufacturer = manufactuer1, PossibleSupplier = new List<Supplier>() { supplier1, supplier2 } };
Product secondProduct = new Product() { Name = "Product 2", Manufacturer = manufactuer1, PossibleSupplier = new List<Supplier>() { supplier2, supplier3 } };
productList_ = new List<Product>() { firstProduct, secondProduct };
}
}
DataStructure: Из-за того, что между Продуктом и Поставщиком существует связь между многими, я должен добавить
[ForeignKey("Product")]
public ICollection<Guid> ProductId { get; set; }
public virtual ICollection<Product> Product { get; set; }
к классу Поставщика. Я также решил добавить коллекцию Продуктов к своему производителю, чтобы сделать некоторые запросы запросов более удобными
public class Product
{
[Key]
public Guid Id { get { return _id; } set { _id = value; } }
private Guid _id = Guid.NewGuid();
public string Name { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public virtual ICollection<Supplier> PossibleSupplier { get; set; }
}
public class Supplier
{
[Key]
public Guid Id { get { return _id; } set { _id = value; } }
private Guid _id = Guid.NewGuid();
public string Name { get; set; }
[ForeignKey("Product")]
public ICollection<Guid> ProductId { get; set; }
public virtual ICollection<Product> Product { get; set; }
}
public class Manufacturer
{
[Key]
public Guid Id { get { return _id; } set { _id = value; } }
private Guid _id = Guid.NewGuid();
public string Name { get; set; }
//only nice for reverse object from Man --> Product
[ForeignKey("Product")]
public ICollection<Guid> ProductId { get; set; }
public virtual ICollection<Product> Product { get; set; }
}
Перед добавлением нового продукта в БД важно загрузить возможных БД / Поставщиков из БД и присвойте их текущему продукту.
Добавление новых продуктов теперь работает нормально, но, как вы можете видеть, загрузка и назначение возможных поставщиков не очень хороши. Поэтому я постараюсь внести некоторые изменения в этот процесс в ближайшие дни .... Я вернусь, если найду «решение».
public static class DbHandler
{
public static List<Product> GetAllProducts()
{
using (ProductDbContext dbObject = new ProductDbContext())
{
//loading with childs and their reverse objects to products
var productList = dbObject.Products.Include("Manufacturer").Include("Manufacturer.Product").Include("PossibleSupplier").Include("PossibleSupplier.Product").Where(i => i.Id != null).ToList();
//loding with childs but without reverse objects
//var productList = dbObject.Products.Include("Manufacturer").Include("PossibleSupplier").Where(i => i.Id != null).ToList();
return productList;
}
}
public static bool StoreProduct(Product product)
{
using (ProductDbContext dbObject = new ProductDbContext())
{
try
{
//this does not solve the loading problem, even when property _id is changed to "private Guid _id = new Guid();
//dbObject.Entry(product).State = product.Id == new Guid() ? EntityState.Added : EntityState.Modified;
//dbObject.Entry(product.Manufacturer).State = product.Manufacturer.Id == new Guid() ? EntityState.Added : EntityState.Modified;
//foreach (var supplier in product.PossibleSupplier)
//{
// dbObject.Entry(supplier).State = supplier.Id == new Guid() ? EntityState.Added : EntityState.Modified;
//}
//Therefore loading must be done manually
Guid manufacturerId = product.Manufacturer.Id;
//Need to check if the manufacturer already exists in the db, if it does
//make sure your project references the EXISTING entity within your context
var checkManuf = dbObject.Manufacturers.Where(x => x.Id == manufacturerId).FirstOrDefault();
if (checkManuf != null)
product.Manufacturer = checkManuf;
List<Supplier> dbSuppliers = new List<Supplier>();
foreach (var posSupplier in product.PossibleSupplier)
{
var checkSupplier = dbObject.Suppliers.FirstOrDefault(x => x.Id == posSupplier.Id);
if (checkSupplier != null)
{
dbSuppliers.Add(checkSupplier);
}
}
foreach (var dbSup in dbSuppliers)
{
product.PossibleSupplier.Remove(product.PossibleSupplier.Single(i => i.Id == dbSup.Id));
product.PossibleSupplier.Add(dbSup);
}
dbObject.Products.Add(product);
dbObject.SaveChanges();
}
catch (Exception ex)
{
//
return false;
}
}
return true;
}
}
public class ProductDbContext : DbContext
{
public ProductDbContext()
{
Database.SetInitializer<ProductDbContext>(new DropCreateDatabaseIfModelChanges<ProductDbContext>());
this.Database.Connection.ConnectionString = sqlConnection.ConnectionString;
}
public DbSet<Product> Products { get; set; }
public DbSet<Supplier> Suppliers { get; set; }
public DbSet<Manufacturer> Manufacturers { get; set; }
private static SqlConnectionStringBuilder sqlConnection = new SqlConnectionStringBuilder()
{
DataSource = "localhost\\MSSQLSERVER2019", // update me
UserID = "", // update me
Password = "", // update me
InitialCatalog = "ProductDb",
IntegratedSecurity = true
};
}
Односторонние швы с использованием EntityStates, но они делают не работает должным образом -> я получил дублированные записи в БД.
Я загрузил текущее состояние проекта в общий ресурс - имя файла SqlTestporject_20200414_2027.zip
Br ,
-------------------------- Обновление 2020-04-15 ----------- ---------------
Я закончил тем, что написал методы, которые обрабатывают решение между обновлением / вставкой для каждого отдельного дочернего элемента, потому что я не нашел способ обновления возможных офлайн-изменений уже существующих dbEntries путем одновременного добавления несуществующих dbEntries. Основная проблема заключалась в том, что я получил дублированные записи в БД при добавлении второго продукта. Странно было то, что это событие дубликатов нарушает уникальность PK без ошибок / исключений ....
Поэтому мне нужно вызывать методы AddOrUpdate (), пока я не достигну последнего потомка для моего полного структура данных.
public static Product AddOrUpdateProduct(Product product)
{
using (ProductDbContext dbObject = new ProductDbContext())
{
try
{
product.Manufacturer = AddOrUpdateManufacturer(dbObject, product.Manufacturer);
List<Supplier> dbSupplierList = new List<Supplier>();
foreach (var supplier in product.PossibleSupplier)
{
dbSupplierList.Add(AddOrUpdateSupplier(dbObject, supplier));
}
product.PossibleSupplier.Clear();
product.PossibleSupplier = dbSupplierList;
if (product.Id == new Guid())
{
//add new product
dbObject.Products.Add(product);
dbObject.SaveChanges();
return product;
}
else
{
//update existing product
var dbProduct = dbObject.Products.Single(x => x.Id == product.Id);
dbProduct.Name = product.Name;
dbObject.SaveChanges();
return dbProduct;
}
}
catch (Exception)
{
throw;
}
}
}
private static Supplier AddOrUpdateSupplier(ProductDbContext dbObject, Supplier supplier)
{
supplier.Address = AddOrUpdateAdress(dbObject, supplier.Address);
if (supplier.Id == new Guid())
{
//add new product
dbObject.Suppliers.Add(supplier);
dbObject.SaveChanges();
return supplier;
}
else
{
//update existing product
var dbSupplier = dbObject.Suppliers.Single(x => x.Id == supplier.Id);
dbSupplier.Name = supplier.Name;
dbObject.SaveChanges();
return dbSupplier;
}
}
private static Manufacturer AddOrUpdateManufacturer(ProductDbContext dbObject, Manufacturer manufacturer)
{
manufacturer.Address = AddOrUpdateAdress(dbObject, manufacturer.Address);
if (manufacturer.Id == new Guid())
{
//add new product
dbObject.Manufacturers.Add(manufacturer);
dbObject.SaveChanges();
return manufacturer;
}
else
{
//update existing product
var dbManufacturer = dbObject.Manufacturers.Single(x => x.Id == manufacturer.Id);
dbManufacturer.Name = manufacturer.Name;
dbObject.SaveChanges();
return dbManufacturer;
}
}
private static Address AddOrUpdateAdress(ProductDbContext dbObject, Address address)
{
if (address.Id == new Guid())
{
//add new product
dbObject.Addresses.Add(address);
dbObject.SaveChanges();
return address;
}
else
{
//update existing product
var dbAddress = dbObject.Addresses.Single(x => x.Id == address.Id);
dbAddress.Street = address.Street;
dbAddress.HouseNumber = address.HouseNumber;
dbAddress.PLZ = address.PLZ;
dbAddress.City = address.City;
dbObject.SaveChanges();
return dbAddress;
}
}
Эту версию можно найти здесь - файл SqlTestporject_20200415_1033.zip .
Дополнительно я хотел бы поделиться следующей ссылкой. Возможно глава Пример 4.18: Создание универсального c метода, который может применять состояние через любой график может помочь другим в реализации более удобного решения.