实体框架4.1代码首先使用N-Tier节省双倍

时间:2011-06-09 19:21:57

标签: wcf entity-framework-4.1 datacontext

我有一个带有很多孩子的实体的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已经存在。

我猜有两个问题。

  1. 在WCF中处理DataContext的最佳做法是什么?是否应该只有一个datacontext从一个类传递到另一个类并进入存储库。
  2. 或者有办法进行此保存。
  3. 我尝试在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;
        }
    

1 个答案:

答案 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,一旦你开始发送更复杂的关系变化的对象图。您最终将首先加载对象图并将更改合并到附加实体中。