英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论