我有一个带有很多孩子的实体的WCF项目。我有一个业务层和一个数据访问层,在数据访问层中我有检索并保存到我的数据库的存储库。我对EF的理解是你在需要的时候创建和销毁DataContext。举个例子,假设我有一个Person实体和一个Book实体(这不是我的应用程序,只是试图说明问题)。
假设Person看起来如下。
Person
string Name
vitual ICollection<Book> Books
书可能是这样的
Book
string Title
Person PersonLending
现在在我的BLL中,我想阅读人员表,然后为该人分配一本书,但该人已存在于数据库中,因此BLL调用人员实体的存储库。
var person = repository.GetPerson("John Doe");
我的存储库有这段代码。
using(var context = new MyContext())
{
return (from p in context.Person
where p.Name == person
select p).FirstOrDefault());
}
现在在BLL中我创建了一本新书并将此人分配给它。
var book = new Book();
book.PersonLending = person;
book.Title = "New Book";
repository.SaveBook();
最后在存储库中,我试着保存这本书。
using(var context = new MyContext())
{
context.Book.Add(book);
context.SaveChanges();
}
现在发生的事情是我在表中得到两个Person行。我的理解是,这是由第一个上下文被破坏引起的,第二个上下文不知道Person已经存在。
我猜有两个问题。
我尝试在Person上将EntityState设置为Unchanged,但它似乎不起作用。
修改
我已经改变了每个请求创建一个新的DataContext(AfterReceiveRequest和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属性时,手动尝试打开它会产生更多错误。我真的认为这可以解决问题。
我也尝试过使用IContractBehaviour,结果相同。所以要么我做错了,要么我错过了一些明显的东西。
PS:我在创建原始帖子之前尝试将状态设置为Unchanged。 PPS:如果有人想知道,我的datafactory只有这两种方法
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;
}
答案 0 :(得分:1)
您的理解绝对正确。将数据传回服务后,新上下文既不知道Book
也不知道Person
。在Book
上调用Add会影响将对象图中的每个未知实体标记为Added
。这是分离场景的一个非常大的问题。
解决方案不是共享上下文这是处理问题的最糟糕方式,因为它引入了a lot of other problems,最后它仍然无效。每个服务调用使用新的上下文。
试试这个:
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();
}
这个问题是more complex,一旦你开始发送更复杂的关系变化的对象图。您最终将首先加载对象图并将更改合并到附加实体中。