问题描述
我有一个WCF项目,其中的实体有很多孩子.我有一个业务层和一个数据访问层,在数据访问层中,我有存储库,这些存储库可以检索并保存到我的数据库中.我对EF的理解是,您可以在需要时创建和销毁DataContext.举例来说,假设我有一个Person实体和一个Book实体(这不是我的应用程序,只是为了说明问题).
I have a WCF project with a entities with lots of children. I have a business layer and a data access layer, in the data access layer I have repositories that retrieve and save to my database. My understanding of EF is that you create and destroy the DataContext as and when you need it. As an example, lets say I have the a Person entity, and a Book entity (this is not my application, but just to try and illustrate the problem).
假设人物如下.
Person
string Name
vitual ICollection<Book> Books
使用Book可能是这样的
With Book maybe something like this
Book
string Title
Person PersonLending
现在在我的BLL中,我想读取人员表,然后为该人员分配一本书,但是该人员已经存在于数据库中,因此BLL调用人员实体的存储库.
Now in my BLL I want to read the person table and then assign a book to that person, but the person already exists in the database, so the BLL calls to the repository for a person entity.
var person = repository.GetPerson("John Doe");
我的存储库中有此代码.
My repository has this code.
using(var context = new MyContext())
{
return (from p in context.Person
where p.Name == person
select p).FirstOrDefault());
}
现在,在BLL中,我创建一本新书并将其分配给该人.
Now in the BLL I create a new book and assign this person to it.
var book = new Book();
book.PersonLending = person;
book.Title = "New Book";
repository.SaveBook();
最后,在存储库中,我尝试将书保存回去.
Finally in the repository I try to save back the book.
using(var context = new MyContext())
{
context.Book.Add(book);
context.SaveChanges();
}
现在发生的事情是我在表中得到了两个Person行.我的理解是,这是由于第一个上下文被破坏而第二个上下文不知道Person已经存在造成的.
Now what happens is I get two Person rows in the table. My understanding is that this is caused by the first context being destroyed, and the second context not knowing that Person already exists.
我猜有两个问题.
- 在WCF中处理DataContext的最佳实践是什么?从一个类到另一个类,再向下到存储库中,应该只传递一个数据上下文.
- 或者有一种方法可以保存此文件.
我尝试将EntityState设置为对人不变",但似乎不起作用.
I have tried setting the EntityState to Unchanged on Person, but it doesn't seem to work.
我已更改了每个请求(AfterReceiveRequest和BeforeSendReply)以创建一个新的DataContext的方法.
I have changed things to create a new DataContext per request (AfterReceiveRequest and BeforeSendReply).
public class EFWcfDataContextAttribute : Attribute, IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase){}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters){}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (var endpoint in channelDispatcher.Endpoints)
{
endpoint.DispatchRuntime.MessageInspectors.Add(new EFWcfDataContextInitializer());
//endpoint.DispatchRuntime.InstanceContextInitializers.Add(new EFWcfDataContextInitializer());
}
}
}
初始化器
public class EFWcfDataContextInitializer : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
instanceContext.Extensions.Add(new EFWcfDataContextExtension(new MyDataContext()));
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
WcfDataContextFactory.Dispose();
}
}
和扩展名
public class EFWcfDataContextExtension : IExtension<InstanceContext>
{
public ICoreDataContext DataContext { get; private set; }
public EFWcfDataContextExtension(ICoreDataContext coreDataContext)
{
if(DataContext != null)
throw new Exception("context is not null");
DataContext = coreDataContext;
}
public void Attach(InstanceContext owner){}
public void Detach(InstanceContext owner) {}
}
这似乎带来了一个全新的问题.我通过调用OperationContext.Current.InstanceContext.Extensions.Find().DataContext来获得当前上下文,但是现在看来这两个上下文会相互影响.在相同的请求下,第一个将返回空记录,第二个将成功.它们都处于唯一的会话中,并且当它们都被创建时,它们都为null并被创建为新的DataContext.当我第一次检查Database.Connection属性时,将其关闭,然后手动尝试将其打开会产生更多错误.我真的以为这可以解决问题.
This seems to give a brand new problem. I get the current context by invoking OperationContext.Current.InstanceContext.Extensions.Find().DataContext, but it now seems that the two context influence each other. On the same request the first one will return a null record and the second one will succeed. They are both in unique sessions, and when they are both created they are null and created as new DataContext. When I check the Database.Connection property on the first it is closed, and manually trying to open it creates more errors. I really thought this would solve the issue.
我也尝试使用IContractBehaviour进行此操作,结果相同.所以我做错了什么,或者我错过了明显的事情.
I have also tried doing this with a IContractBehaviour, with the same result. So either I am doing something wrong or I am missing something obvious.
PS:在发表原始帖子之前,我尝试将状态设置为不变".PPS:万一有人怀疑,我的数据工厂只提供了这两种方法
PS: I tried setting the state to Unchanged before I made the original post.PPS: In case anyone wonders, my datafactory simply has these two methods
public static void Dispose()
{
ICoreDataContext coreDataContext = OperationContext.Current.InstanceContext.Extensions.Find<EFWcfDataContextExtension>().DataContext;
coreDataContext.Dispose();
coreDataContext = null;
}
public static ICoreDataContext GetCurrentContext()
{
var context = OperationContext.Current.InstanceContext.Extensions.Find<EFWcfDataContextExtension>().DataContext;
if (context != null)
{
if (context.Database.Connection.State == ConnectionState.Closed)
context.Database.Connection.Open();
}
return context;
}
推荐答案
您的理解是绝对正确的.一旦将数据传递回服务,新的上下文将不知道Book
或Person
.在Book
上调用Add的作用是将对象图中的每个未知实体标记为Added
.这是独立方案的很大问题.
Your understanding is absolutely correct. Once you pass the data back to the service the new context doesn't know neither the Book
or Person
. Calling Add on the Book
has effect of marking every unknown entity in the object graph as Added
. This is very big problem of detached scenarios.
解决方案不共享上下文,因为它引入了,最后仍然无法正常工作.每个服务调用都使用一个新的上下文.
The solution is not sharing the context That is the worst way to deal with the problem because it introduces a lot of other problems and at the end it will still not work. Use a new context per each service call.
尝试一下:
using(var context = new MyContext())
{
context.Book.Attach(book);
context.Entry(book).State = EntityState.Added;
context.SaveChanges();
}
或者这个:
using(var context = new MyContext())
{
context.Book.Add(book);
context.Entry(book.PersonLending).State = EntityState.Unchanged;
context.SaveChanges();
}
此问题是更复杂,然后您开始发送关系发生变化的更复杂的对象图.您将首先加载对象图,然后将更改合并到附加的实体中.
This problem is more complex and once you start to send more complicated object graphs with changes in relations. You will end up in loading object graph first and merging changes into attached entities.
这篇关于实体框架4.1代码优先,N层节省两倍的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!