使用仓储模式在Entity Framework中添加带有相关子对象的记录。

huangapple go评论149阅读模式
英文:

Adding record with related child object in Entity Framework using Repository Pattern

问题

我在添加一个带有对现有对象的关系的实体到数据库时遇到了问题。我进行了大量搜索,但找不到合适的解决方案。我将尽量简单地描述这个问题。

public class Store : IEntity
{
    public int StoreId { get; set; }
    public string StoreName { get; set; }

    public virtual Address Address { get; set; }

    public virtual Contractor Contractor { get; set; }
}

public class Product : IEntity
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public decimal Price { get; set; }
    public virtual Store Store { get; set; }
}

在仓库中,我是这样添加记录的,这是一个通用类:

public TEntity Add(TEntity entity)
{
    using (var context = new TContext())
    {
        var addedEntity = context.Entry(entity);
        addedEntity.State = EntityState.Added;
        context.SaveChanges();
        return entity;
    }
}

现在,当我尝试像这样添加新记录时:

var store = storeManager.GetBy(x => x.StoreId == 1);

var product = new Product() { ProductName = "Bananas", Store = store }; 

productManager.Add(product);

productManager.GetAll().ForEach(x => Console.WriteLine(x.ProductName + " " + x.Store.StoreId));

Store关系被添加为新的商店,并获得了新的ID。有没有人有办法解决这个问题?

数据库示例:

StoreId StoreName Address_AddressId Contractor_ContractorId
1       NULL      1                1
2       NULL      2                2
3       NULL      3                3
4       NULL      4                4
5       NULL      5                5
6       NULL      6                6
7       NULL      7                7

这是我在Stack Overflow上的第一个问题。

英文:

I have trouble with adding an entity to database which contains an relation to existing object. I searched alot and couldn't find proper solution for this. I will describe this as simple as I can.

public class Store : IEntity
{
    public int StoreId { get; set; }
    public string StoreName { get; set; }

    public virtual Address Address { get; set; }

    public virtual Contractor Contractor { get; set; }
}

    public class Product : IEntity
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public decimal Price { get; set; }
    public virtual Store Store { get; set; }
}

And in repository im adding records like this. This is generic class

        public TEntity Add(TEntity entity)
    {
        using (var context = new TContext())
        {
            var addedEntity = context.Entry(entity);
            addedEntity.State = EntityState.Added;
            context.SaveChanges();
            return entity;
        }
    }

Now when i try to add new record like this

var store = storeManager.GetBy(x => x.StoreId == 1);

var product = new Product() { ProductName = "Bananas", Store = store }; 

productManager.Add(product);

productManager.GetAll().ForEach(x => Console.WriteLine(x.ProductName + " " + x.Store.StoreId));

Store relation is added as new store and it get's new ID. Does someone have idea how i can solve this?

Example from database:

StoreId	StoreName	Address_AddressId	Contractor_ContractorId
1	NULL	1	1
2	NULL	2	2
3	NULL	3	3
4	NULL	4	4
5	NULL	5	5
6	NULL	6	6
7	NULL	7	7

It's my first question on stackoverflow.

答案1

得分: 1

你遇到问题的最可能原因是在插入操作时创建了新的上下文实例。因此,这个新的上下文不仅会获得一个新的产品,还会获得来自另一个上下文的商店,但这个新创建的上下文不知道数据库中已经存在该商店。

一个一般性的问题是不正确地管理数据库上下文的生命周期。Entity Framework 实例与用于接收它们的上下文相关联,你不能将一个上下文中的实体直接放入另一个上下文中。

在每个管理器操作中创建新的上下文,而应该在多个管理器之间共享数据库上下文的实例。

public class StoreManager
{
    public StoreManager(Context context)
    {
        this.context = context;
    }

    public TEntity Add(TEntity entity)
    {
        var addedEntity = context.Entry(entity);
        addedEntity.State = EntityState.Added;
        context.SaveChanges();
        return entity;
    }
}

协调层首先必须创建上下文,并确保在两个管理器之间共享。

var context = new DbContext();

var storeManager = new StoreManager(context);
var productManager = new ProductManager(context);

var store = storeManager.GetBy(x => x.StoreId == 1);
var product = new Product() { ProductName = "Bananas", Store = store };

productManager.Add(product);

productManager.GetAll().ForEach(x => Console.WriteLine(x.ProductName + " " +
    x.Store.StoreId));

通常,所有这些都在单个范围内创建,例如,在请求范围内,以便单个 Web 请求具有单个数据库上下文,并且每个存储库都获取到相同的上下文实例。

你也可以遵循官方的教程

英文:

The most probable cause of your issue is that you are creating a new instance of your context for the insert opreration. Because of that, this new context not only gets a new product but also a store, which is received from another context, but this newly created context doesn't have any idea the store is already in the database.

A general issue then is incorrect managing the lifecycle of your database contexts. EF instances are tied to contexts that were used to receive them and you can't just put an entity from a context into another context.

Instead of creating a new context in each of your manager operations, you should share the instance of the database context between multiple managers.

public class StoreManager
{
     public StoreManager( Context context )
     {
         this.context = context;
     }

   public TEntity Add(TEntity entity)
   {
        var addedEntity = context.Entry(entity);
        addedEntity.State = EntityState.Added;
        context.SaveChanges();
        return entity;
    }
}

The orchestration has to first create the context and make sure it's shared between the two managers

var context = new DbContext();

var storeManager   = new StoreManager( context );
var productManager = new ProductManager( context );

var store = storeManager.GetBy(x => x.StoreId == 1);
var product = new Product() { ProductName = "Bananas", Store = store }; 

productManager.Add(product);

productManager.GetAll().ForEach(x => Console.WriteLine(x.ProductName + " " + 
    x.Store.StoreId));

Usually, all these are created in a single scope, e.g. in a request scope, so that a single web request has a single database context and each repositories get the very same instance of the context.

You can also follow an official tutorial.

huangapple
  • 本文由 发表于 2020年1月7日 00:05:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/59615287.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定