【问题标题】:Entity Framework 4.1 Code First with N-Tier saving doubleEntity Framework 4.1 Code First 与 N 层节省双倍
【发布时间】:2011-06-10 19:51:23
【问题描述】:

我有一个 WCF 项目,其中的实体有很多孩子。我有一个业务层和一个数据访问层,在数据访问层我有用于检索并保存到我的数据库的存储库。我对 EF 的理解是,您可以在需要时创建和销毁 DataContext。例如,假设我有一个 Person 实体和一个 Book 实体(这不是我的应用程序,只是为了尝试说明问题)。

假设 Person 如下所示。

Person
  string Name
  vitual ICollection<Book> Books

Book 可能是这样的

Book
 string Title
 Person PersonLending

现在在我的 BLL 中,我想读取 person 表,然后将一本书分配给那个人,但是这个人已经存在于数据库中,所以 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 的最佳做法是什么?是否应该只有一个数据上下文从一个类传递到另一个类并向下传递到存储库。
  2. 或者有什么办法可以保存。

我尝试将 EntityState 在 Person 上设置为 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 来获取当前上下文,但现在看来这两个上下文相互影响。在同一个请求上,第一个将返回一个空记录,第二个将成功。它们都在唯一的会话中,当它们都被创建时,它们都是空的并被创建为新的 DataContext。当我第一次检查 Database.Connection 属性时,它会关闭,然后手动尝试打开它会产生更多错误。我真的认为这会解决问题。

我也尝试过使用 IContractBehaviour 执行此操作,结果相同。所以要么我做错了什么,要么我遗漏了一些明显的东西。

PS:在发布原始帖子之前,我尝试将状态设置为未更改。 PPS:如果有人想知道,我的数据工厂只有这两种方法

  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;
    }

【问题讨论】:

    标签: wcf entity-framework-4.1 datacontext


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

    【讨论】:

    猜你喜欢
    • 2023-04-02
    • 2012-05-15
    • 2011-08-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-24
    • 2011-09-19
    • 2011-09-10
    • 2011-12-11
    相关资源
    最近更新 更多