【问题标题】:What is a good strategy to load a Poco referenced entity when disconected from context in EF4?在 EF 4 中与上下文断开连接时加载 Poco 引用实体的好策略是什么?
【发布时间】:2010-09-13 14:03:41
【问题描述】:

我启动了一个 ASP.net Web 项目应用程序来了解如何使用 EF4。在我的项目中,我定义了 3 层(DAL => Entity Framework 4,BLL => 业务逻辑层,UI)。 BLL 和 DAL 共享使用 EF4 的模板特性生成的 POCO。

例如,我有如下所示的“用户”Poco 类:

 public partial class User
{
    #region Primitive Properties

    public virtual System.Guid Id
    {
        get;
        set;
    }

    public virtual string Name
    {
        get;
        set;
    }

    public virtual string Password
    {
        get;
        set;
    }

    public virtual string Email
    {
        get;
        set;
    }

    public virtual bool MarkedForDeletion
    {
        get;
        set;
    }

    #endregion
    #region Navigation Properties

    public virtual Role Role
    {
        get { return _role; }
        set
        {
            if (!ReferenceEquals(_role, value))
            {
                var previousValue = _role;
                _role = value;
                FixupRole(previousValue);
            }
        }
    }
    private Role _role;

    public virtual ICollection<Article> Articles
    {
        get
        {
            if (_articles == null)
            {
                var newCollection = new FixupCollection<Article>();
                newCollection.CollectionChanged += FixupArticles;
                _articles = newCollection;
            }
            return _articles;
        }
        set
        {
            if (!ReferenceEquals(_articles, value))
            {
                var previousValue = _articles as FixupCollection<Article>;
                if (previousValue != null)
                {
                    previousValue.CollectionChanged -= FixupArticles;
                }
                _articles = value;
                var newValue = value as FixupCollection<Article>;
                if (newValue != null)
                {
                    newValue.CollectionChanged += FixupArticles;
                }
            }
        }
    }
    private ICollection<Article> _articles;

    public virtual Status Status
    {
        get { return _status; }
        set
        {
            if (!ReferenceEquals(_status, value))
            {
                var previousValue = _status;
                _status = value;
                FixupStatus(previousValue);
            }
        }
    }
    private Status _status;

    #endregion
    #region Association Fixup

    private void FixupRole(Role previousValue)
    {
        if (previousValue != null && previousValue.Users.Contains(this))
        {
            previousValue.Users.Remove(this);
        }

        if (Role != null)
        {
            if (!Role.Users.Contains(this))
            {
                Role.Users.Add(this);
            }
        }
    }

    private void FixupStatus(Status previousValue)
    {
        if (previousValue != null && previousValue.Users.Contains(this))
        {
            previousValue.Users.Remove(this);
        }

        if (Status != null)
        {
            if (!Status.Users.Contains(this))
            {
                Status.Users.Add(this);
            }
        }
    }

    private void FixupArticles(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (Article item in e.NewItems)
            {
                item.Author = this;
            }
        }

        if (e.OldItems != null)
        {
            foreach (Article item in e.OldItems)
            {
                if (ReferenceEquals(item.Author, this))
                {
                    item.Author = null;
                }
            }
        }
    }

    #endregion
}

因为所有的属性都标有虚拟 EF4 应该创建一个代理类以便访问 POCO 的所有数据。我还为 db 上下文启用了延迟加载。

当我想显示用户详细信息时,我遇到了以下情况:

  • UI=> 从 BLL (UsersManager) 实例化一个类并调用返回用户的方法 GetUserByEmail(string email)。

  • BLL=> 在 UsersManager 类型的 GetUserByEmail(string email) 方法中,我从 DAL(UsersDataManager) 实例化一个类并调用返回 User 的 GetUserByEmailFromDAL(string email) 方法。

    李>
  • DAL=> 在 GetUserByEmailFromDAL(string email) 我实例化一个上下文,查询用户并返回它。

问题在于,因为只有 DAL 知道上下文并且它在退出函数后被释放,所以 POCO 的导航关系为空,我无法访问它们的任何属性。

如果我执行 myUser.Role.Name 我会收到如下错误:

Role = 'user1.Role' threw an exception of type 'System.ObjectDisposedException'
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

为了绕过这个问题,我决定修改我的方法,以便告诉它们在我需要导航属性时急切加载它们。修改后,我在 DAL 中的方法如下所示:

 public User User(string email, bool loadRelationships)
        {
            User user = null;
            if (!loadRelationships)
            {
                user = (from p in dbContext.Users where p.Email.Equals(email) select p).FirstOrDefault<User>();
            }
            else {
                user = (from p in dbContext.Users.Include("Role").Include("Status") select p).FirstOrDefault<User>();
            }

            return user;
        }

通过此修改,问题仍然存在..我无权访问相关实体导航实体...例如,如果我想要这样的角色类型具有权限集合:

User user1 = UserManager("test@test.com");
foreach(Permission perm in user1.Role.Permissions)
Console.WriteLine(perm.Name); => here i'd get an error like the one mentioned earlier.

当 Poco 附加到数据库上下文时,延迟加载有效。是否有一种机制或策略可用于加载导航属性,例如我的场景?

谢谢。

【问题讨论】:

    标签: navigation load entity-framework-4 properties poco


    【解决方案1】:

    您的传统演示/业务/数据三层架构不适合延迟加载。 UI 不应直接导致执行查询。

    如果您希望您的 UI 层能够延迟加载相关实体,则必须通过您的业务层从 DAL 显示上下文。这可能不是一个好主意,因为如果您允许这样做,您可能会无意中在 UI 中遇到查询,这些查询会加载您想要的更多数据。你也打破了你的关注点分离。

    您上面通过参数按需加载相关数据的方法是一种更好的方法。

    【讨论】:

    • 所以为了加载角色的导航属性(如我的示例中),我应该查询角色本身.. 类似 Role myUsersRole = RoleManager.GetRoleWithId(user1.Role.id);并在 DAL 中调用与 UsersDataManager(..) 类似的方法,您能否提供一些可与延迟加载一起使用的架构示例(以便谷歌搜索)?谢谢你戴夫!
    • @Sorin:是的,我认为你走在正确的轨道上。您正在使用的三层架构将责任放在 DAL 和 BLL 上,以准确公开 UI 需要的内容,仅此而已。我在我的 ASP.NET MVC 项目中使用带有存储库模式的延迟加载。这是一个“更扁平”的架构,更适合延迟加载。您仍然需要注意不要过度使用延迟加载,否则可能会遇到性能问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    • 2020-11-29
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    相关资源
    最近更新 更多