代码之家  ›  专栏  ›  技术社区  ›  Sorin Antohi

当与EF4中的上下文断开连接时,加载POCO引用实体的好策略是什么?

  •  1
  • Sorin Antohi  · 技术社区  · 14 年前

    我启动了一个ASP.NET Web项目应用程序来学习如何使用EF4。在我的项目中,我定义了3个层(dal=>实体框架4,bll=>业务逻辑层,ui)。 使用EF4的模板功能生成的BLL和DAL共享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)实例化一个类,并调用返回用户的getuserbyemail from dal(string email)方法。

    • dal=>在getuserbyemailfromdal(字符串email)中,我实例化了一个上下文,为用户查询并返回它。

    问题是,因为只有DAL知道上下文,并且在它退出函数后才进行处理,所以POCOS导航关系为空,并且我对它们的任何属性都没有访问权。

    如果我执行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连接到数据库上下文时,延迟加载工作正常。是否有一种机制或策略可以像在我的场景中那样用于加载导航属性?

    谢谢您。

    1 回复  |  直到 14 年前
        1
  •  0
  •   Dave Swersky    14 年前

    传统的表示/业务/数据三层体系结构不适合延迟加载。用户界面不应直接导致执行查询。

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

    上述按参数按需加载相关数据的方法是一种更好的方法。