C# 使用实体框架添加和更新实体

C# 使用实体框架添加和更新实体,c#,entity-framework-5,dao,C#,Entity Framework 5,Dao,在我的上一个项目中,我首先使用了EntityFramework5代码。我完成了我的项目,但在开发过程中经历了很多痛苦 我试图在下面解释我的痛苦: 我的数据访问逻辑层中有几个数据类,如产品、产品类别、订单、公司、制造商等。。。每个类都有一些对其他类的引用。例如,产品实例具有ProductCategory属性 在我的数据访问对象类的Add和Update方法中,我在上下文中将每个属性(基本类型除外)的状态设置为Unchanged或Modified。下面是某个dao类的更新方法的某些部分: contex

在我的上一个项目中,我首先使用了EntityFramework5代码。我完成了我的项目,但在开发过程中经历了很多痛苦

我试图在下面解释我的痛苦:

我的数据访问逻辑层中有几个数据类,如产品、产品类别、订单、公司、制造商等。。。每个类都有一些对其他类的引用。例如,产品实例具有ProductCategory属性

在我的数据访问对象类的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;
    }
}

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

好的,那么您只是做错了什么。 除了我的评论之外,我还为您创建了一个示例,显示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);
    }
}
此外,在SQLServerManagementStudio中查看时,此解决方案仅创建(并更新)了一个产品和一个类别

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


因此,如果您不发布任何代码,我们将无法为您提供更多帮助:-)

您可以用以下代码替换您的代码:

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

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

是否可以添加用于创建数据的代码?如果类中的引用正确,EF应该只创建一次所有实例。是的,它一次创建所有实例。事实上,这就是问题所在。让我再解释一下这个问题。假设我有一个Product实例,其中包含一个ProductType实例。假设产品实例已在过去持久化。当我尝试更新该产品实例时,如果我不将ProductType属性的状态设置为Unchanged,EF将创建一个重复的ProductType实例(我不希望出现这种情况)。如果从另一个(尚未处理的)上下文检索实体,这将不起作用。你会得到一个例外。@MichaelLogutov:你是对的,但我几乎可以肯定问题中的场景是一个分离的场景(即,先前的上下文已经被释放)。否则无论如何都会容易得多,因为您通常根本不需要摆弄任何实体状态,因为更改跟踪将自动跟踪正确的状态。
    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!
context.Products.Attach(entity);
context.Entry(entity).State = System.Data.EntityState.Modified;