Какова хорошая стратегия загрузки сущности, на которую ссылаются Poco, когда она отключена от контекста в EF4? - PullRequest
1 голос
/ 13 сентября 2010

Я запустил приложение веб-проекта ASP.net, чтобы узнать, как можно использовать EF4. В моем проекте я определил 3 слоя (DAL => Entity Framework 4, BLL => Business Logic Layer, UI). BLL и DAL совместно используют POCO, созданные с использованием функции шаблона EF4.

Например, у меня есть класс 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. Также я включил ленивую загрузку для контекста БД.

Когда я хочу отобразить информацию о пользователях, у меня есть следующий сценарий:

  • UI => создает экземпляр класса из BLL (UsersManager) и вызывает метод GetUserByEmail (string email), который возвращает User.

  • BLL => в методе GetUserByEmail (строковая электронная почта) типа UsersManager. Я создаю экземпляр класса из DAL (UsersDataManager) и вызываю метод GetUserByEmailFromDAL (строчная электронная почта), который возвращает User.

  • DAL => в GetUserByEmailFromDAL (строка электронной почты) я создаю контекст, запрашиваю пользователя и возвращаю его.

проблема в том, что, поскольку только 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 присоединен к контексту БД. Есть ли механизм или стратегия, которую можно использовать для загрузки свойств навигации, как в моем сценарии?

спасибо.

1 Ответ

0 голосов
/ 13 сентября 2010

Ваша традиционная трехуровневая архитектура Presentation / Business / Data не требует ленивой загрузки. Пользовательский интерфейс никогда не должен напрямую вызывать выполнение запроса.

Если вы хотите, чтобы уровень пользовательского интерфейса мог лениво загружать связанные объекты, вам придется отображать контекст из DAL через бизнес-уровень. Это не может быть хорошей идеей, потому что, если вы разрешите это, вы можете непреднамеренно закончить запросами в пользовательском интерфейсе, которые загружают намного больше данных, которые вы намереваетесь. Вы также нарушаете разделение интересов.

Ваш способ загрузки связанных данных по запросу по параметру выше - лучший подход.

...