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