使用Entity Framework添加和更新实体

时间:2022-11-26 02:07:32

In my last project I have used Entity Framework 5 Code First. I completed my project but had a lot of pain during the development process.

在我的上一个项目中,我使用了Entity Framework 5 Code First。我完成了我的项目,但在开发过程中遇到了很多痛苦。

I tried to explain my pain below:

我试着解释下面的痛苦:

I had several data classes in my data access logic layer like Product, ProductCategory, Order, Company, Manufacturer etc... Each class has some references to some other classes. For example, a Product instance has a ProductCategory property.

我的数据访问逻辑层有几个数据类,如Product,ProductCategory,Order,Company,Manufacturer等......每个类都有一些其他类的引用。例如,Product实例具有ProductCategory属性。

Inside Add and Update methods of my Data Access Object classes I set the states of each property (other than the primitive types) to Unchanged or Modified in the context. Below is some part of an update method of some dao class:

在我的数据访问对象类的Add和Update方法中,我在上下文中将每个属性的状态(基本类型除外)设置为Unchanged或Modified。下面是一些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;
    }
}

This code goes like this. Some of my add or update methods are about 30 lines of code. I think I am doing something wrong, adding or updating an entity should not be this much pain but when I don't set the states of the objects I either get exception or duplicate entries in the database. Do I really have to set the state of each property that maps to database?

这段代码是这样的。我的一些添加或更新方法大约有30行代码。我认为我做错了什么,添加或更新实体应该不是那么痛苦但是当我没有设置对象的状态时,我要么在数据库中得到异常或重复的条目。我是否真的必须设置映射到数据库的每个属性的状态?

2 个解决方案

#1


11  

You can replace your code by:

您可以通过以下方式替换代码:

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

The reason why this is the same (unless the related entities were already attached to the context in another state than Unchanged before) is that Attach puts entity including all related entities in the object graph into the context in state Unchanged. Setting the state to Modified afterwards for entity only changes the state of the product alone (not the related entities) from Unchanged to Modified.

这是相同的(除非相关实体已经附加到另一个状态中的上下文而不是之前的Unchanged)是因为Attach将包含对象图中所有相关实体的实体放入状态Unchanged中的上下文中。之后为实体将状态设置为Modified仅将产品的状态(不是相关实体)从Unchanged更改为Modified。

#2


3  

Ok, you're just doing something wrong then. In addition to my comment, I've created a sample for your which shows that EF doesn't create duplicates by default.

好的,那你只是做错了。除了我的评论之外,我还为您创建了一个示例,其中显示EF默认情况下不会创建重复项。

I have two classes:

我有两节课:

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; }
}

One context:

一个背景:

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);
    }
}

Then one initializer class (this could inherit from other strategies if you want):

然后是一个初始化类(如果需要,可以继承其他策略):

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

The main program:

主要方案:

    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));
    }

This results in the following console output:

这导致以下控制台输出:

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!

Also, when looking in SQL Server Management Studio, this solution has only created (and updated) one product and one category.

此外,在查看SQL Server Management Studio时,此解决方案仅创建(和更新)一个产品和一个类别。

Ofcourse, you should work with repositories to retrieve, update and delete your data and a unit of work. These have been left out of the example.

当然,您应该使用存储库来检索,更新和删除您的数据和工作单元。这些已被排除在示例之外。

So, if you do not post any code, we can't help you much further :-)

所以,如果您不发布任何代码,我们无法帮助您更多:-)

#1


11  

You can replace your code by:

您可以通过以下方式替换代码:

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

The reason why this is the same (unless the related entities were already attached to the context in another state than Unchanged before) is that Attach puts entity including all related entities in the object graph into the context in state Unchanged. Setting the state to Modified afterwards for entity only changes the state of the product alone (not the related entities) from Unchanged to Modified.

这是相同的(除非相关实体已经附加到另一个状态中的上下文而不是之前的Unchanged)是因为Attach将包含对象图中所有相关实体的实体放入状态Unchanged中的上下文中。之后为实体将状态设置为Modified仅将产品的状态(不是相关实体)从Unchanged更改为Modified。

#2


3  

Ok, you're just doing something wrong then. In addition to my comment, I've created a sample for your which shows that EF doesn't create duplicates by default.

好的,那你只是做错了。除了我的评论之外,我还为您创建了一个示例,其中显示EF默认情况下不会创建重复项。

I have two classes:

我有两节课:

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; }
}

One context:

一个背景:

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);
    }
}

Then one initializer class (this could inherit from other strategies if you want):

然后是一个初始化类(如果需要,可以继承其他策略):

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

The main program:

主要方案:

    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));
    }

This results in the following console output:

这导致以下控制台输出:

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!

Also, when looking in SQL Server Management Studio, this solution has only created (and updated) one product and one category.

此外,在查看SQL Server Management Studio时,此解决方案仅创建(和更新)一个产品和一个类别。

Ofcourse, you should work with repositories to retrieve, update and delete your data and a unit of work. These have been left out of the example.

当然,您应该使用存储库来检索,更新和删除您的数据和工作单元。这些已被排除在示例之外。

So, if you do not post any code, we can't help you much further :-)

所以,如果您不发布任何代码,我们无法帮助您更多:-)