2013-07-22 5 views
9

В моем последнем проекте я использовал Entity Framework 5 Code First. Я завершил свой проект, но у меня была большая боль в процессе разработки.Добавление и обновление объектов с помощью платформы Entity Framework

Я попытался объяснить мою боль ниже:

У меня было несколько классов данных в моем доступе к данному логическому уровню, как продукт, ProductCategory, заказ, компания, производитель и т.д ... Каждый класс имеет некоторые ссылки на некоторые другие классы , Например, экземпляр продукта имеет свойство ProductCategory.

Внутри Добавление и обновление методов классов доступа к данным Я установил состояния каждого свойства (кроме примитивных типов) в неизмененное или измененное в контексте. Ниже приведена часть метода обновления некоторых классов dao:

context.Entry(entity).State = System.Data.EntityState.Modified; 
if (entity.Category != null) 
    context.Entry(entity.Category).State = System.Data.EntityState.Unchanged; 

if (entity.Manufacturer != null) 
    context.Entry(entity.Manufacturer).State = System.Data.EntityState.Unchanged; 

foreach (var specificationDefinition in entity.SpecificationDefinitions) 
{ 
    context.Entry(specificationDefinition).State = System.Data.EntityState.Unchanged; 
    foreach (var specificationValue in specificationDefinition.Values) 
    { 
     context.Entry(specificationValue).State = System.Data.EntityState.Unchanged; 
    } 
} 

Этот код выглядит следующим образом. Некоторые из моих методов добавления или обновления составляют около 30 строк кода. Я думаю, что я делаю что-то неправильно, добавление или обновление объекта не должно быть такой болью, но когда я не устанавливаю состояния объектов, я получаю исключения или дубликаты записей в базе данных. Должен ли я действительно устанавливать состояние каждого свойства, которое сопоставляется с базой данных?

+0

Можете ли вы добавить код, в котором вы создаете данные? Если ваши ссылки в ваших классах верны, EF должен создавать только все экземпляры один раз. – Nullius

+0

Да, он создает все экземпляры одновременно. На самом деле это проблема. Позвольте мне снова объяснить эту проблему. Скажем, у меня есть экземпляр продукта с экземпляром ProductType внутри него. Предположим, что экземпляр Product уже сохраняется в прошлом. Когда я пытаюсь обновить экземпляр Product, EF создает дубликат экземпляра ProductType (который не подходит для случая), если я не устанавливаю состояние атрибута ProductType в Unchanged. – Furkan

ответ

11

Вы можете заменить ваш код:

context.Products.Attach(entity); 
context.Entry(entity).State = System.Data.EntityState.Modified; 

Причина, почему это то же самое (если соответствующие объекты уже не были привязаны к контексту в другом состоянии, чем Unchanged), то Attach ставит entityвключая все связанные объекты в графе объектов в контексте в состоянии. Установка состояния на Modified в дальнейшем для entity изменяет только состояние продукта (не связанных с ним объектов) от Unchanged до Modified.

+0

Это не будет работать, если объект был получен из другого (еще не удалённого) контекста. Вы получите исключение. –

+0

@MichaelLogutov: Вы правы, но я почти уверен, что сценарий в вопросе является отстраненным сценарием (т. Е. Более ранний контекст уже установлен). В противном случае это намного проще, так как вам обычно не приходится возиться со всеми состояниями сущностей вообще, потому что отслеживание изменений будет отслеживать правильные состояния автоматически. – Slauma

3

Итак, вы просто делаете что-то не так. В дополнение к моему комментарию, я создал образец для вашего, который показывает, что по умолчанию EF не создает дубликаты.

У меня есть два класса:

public class Product 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public ProductCategory Category { get; set; } 
    public decimal Price { get; set; } 
} 

public class ProductCategory 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

Один контекст:

public class MyContext : DbContext 
{ 
    public DbSet<Product> Products { get; set; } 
    public DbSet<ProductCategory> ProductCategories { get; set; } 

    public MyContext() 
     : base("name=MyContext") 
    { 
    } 

    public MyContext(string nameOrConnectionString) 
     : base(nameOrConnectionString) 
    { 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     Database.SetInitializer<MyContext>(null); 

     // Table mappings 
     modelBuilder.Entity<Product>().ToTable("Product"); 
     modelBuilder.Entity<ProductCategory>().ToTable("ProductCategory"); 

     base.OnModelCreating(modelBuilder); 
    } 
} 

Тогда один инициализатор класса (это может наследоваться от других стратегий, если вы хотите):

public class InitDb<TContext> : DropCreateDatabaseAlways<TContext> 
    where TContext : DbContext 
{ 
} 

The основная программа:

static void Main(string[] args) 
    { 
     var prodCat = new ProductCategory() 
     { 
      Name = "Category 1" 
     }; 

     var prod = new Product() 
     { 
      Name = "Product 1", 
      Category = prodCat, 
      Price = 19.95M 
     }; 

     using (var context = new MyContext()) 
     { 
      var initializer = new InitDb<MyContext>(); 
      initializer.InitializeDatabase(context); 

      Console.WriteLine("Adding products and categories to context."); 
      context.ProductCategories.Add(prodCat); 
      context.Products.Add(prod); 

      Console.WriteLine(); 
      Console.WriteLine("Saving initial context."); 
      context.SaveChanges(); 
      Console.WriteLine("Context saved."); 

      Console.WriteLine(); 
      Console.WriteLine("Changing product details."); 
      var initProd = context.Products.Include(x => x.Category).SingleOrDefault(x => x.Id == 1); 
      PrintProduct(initProd); 
      initProd.Name = "Product 1 modified"; 
      initProd.Price = 29.95M; 
      initProd.Category.Name = "Category 1 modified"; 
      PrintProduct(initProd); 

      Console.WriteLine(); 
      Console.WriteLine("Saving modified context."); 
      context.SaveChanges(); 
      Console.WriteLine("Context saved."); 

      Console.WriteLine(); 
      Console.WriteLine("Getting modified product from database."); 
      var modProd = context.Products.Include(x => x.Category).SingleOrDefault(x => x.Id == 1); 
      PrintProduct(modProd); 

      Console.WriteLine(); 
      Console.WriteLine("Finished!"); 
      Console.ReadKey(); 
     } 


    } 

    static void PrintProduct(Product prod) 
    { 
     Console.WriteLine(new string('-', 10)); 
     Console.WriteLine("Id  : {0}", prod.Id); 
     Console.WriteLine("Name : {0}", prod.Name); 
     Console.WriteLine("Price : {0}", prod.Price); 
     Console.WriteLine("CatId : {0}", prod.Category.Id); 
     Console.WriteLine("CatName : {0}", prod.Category.Name); 
     Console.WriteLine(new string('-', 10)); 
    } 

Это приводит к следующему выходу консоли:

Adding products and categories to context. 

Saving initial context. 
Context saved. 

Changing product details. 
---------- 
Id  : 1 
Name : Product 1 
Price : 19,95 
CatId : 1 
CatName : Category 1 
---------- 
---------- 
Id  : 1 
Name : Product 1 modified 
Price : 29,95 
CatId : 1 
CatName : Category 1 modified 
---------- 

Saving modified context. 
Context saved. 

Getting modified product from database. 
---------- 
Id  : 1 
Name : Product 1 modified 
Price : 29,95 
CatId : 1 
CatName : Category 1 modified 
---------- 

Finished! 

Кроме того, при поиске в SQL Server Management Studio, это решение только создал (и обновляется) один продукт и одну категорию.

Конечно, вы должны работать с репозиториями для извлечения, обновления и удаления ваших данных и единицы работы. Они были исключены из примера.

Так что, если вы не размещать код, мы не можем помочь вам гораздо дальше :-)

+0

Выше ваш код является первым подходом EF-кода? – Thomas

+0

@Thomas: Это действительно код в первую очередь. – Nullius

Смежные вопросы