2010-09-13 2 views
1

J'ai commencé une application de projet Web ASP.net pour apprendre comment EF4 peut être utilisé. Dans mon projet, j'ai défini 3 couches (DAL => Entity Framework 4, BLL => Business Logic Layer, UI). Les POCO de partage BLL et DAL sont générés à l'aide de la fonction modèle de EF4.Quelle est une bonne stratégie pour charger une entité référencée Poco lorsqu'elle est déconnectée du contexte dans EF4?

Par exemple, j'ai « utilisateur » classe Poco qui ressemble à ceci:

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 
} 

Parce que toutes les propriétés sont marquées par EF4 virtuel devrait créer une classe proxy afin d'accéder à toutes les données du POCO. J'ai également activé le chargement paresseux pour le contexte db.

Quand je veux afficher un utilisateur détails j'ai le scénario de mise en jachère:

  • UI => instancier une classe de BLL (UsersManager) et appelle la méthode GetUserByEmail (e-mail de chaîne) qui retourne un utilisateur. BLL => dans la méthode GetUserByEmail (email de chaîne) de type UsersManager j'instancie une classe de DAL (UsersDataManager) et appelle une méthode GetUserByEmailFromDAL (string email) qui retourne un utilisateur. DAL => dans le GetUserByEmailFromDAL (email de chaîne) j'instancie un contexte, interroge l'utilisateur et le renvoie.

le problème est que parce que seul le DAL connaît le contexte et il est disposé après sa sortie la fonction des relations de navigation pocos sont Nulled et je n'ai pas accès à l'une de leurs propriétés.

si je fais myUser.Role.Name j'obtiens une erreur comme ceci:

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. 

afin de contourner le problème, j'ai décidé de modifier mes méthodes afin de le leur dire à la charge avide des propriétés de navigation quand j'en ai besoin Après cette modification dans ma méthode DAL ressemble à ceci:

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

avec cette modification un problème encore exists..i n'ont pas accès aux entités liées entités de navigation ... donc, par exemple, si un type de rôle a une collection d'autorisations si je voulais quelque chose comme ceci:

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

chargement Lazy fonctionne lorsque le Poco est attaché à un contexte db. Existe-t-il un mécanisme ou une stratégie pouvant être utilisé pour charger les propriétés de navigation comme dans mon scénario?

merci.

Répondre

0

Votre architecture traditionnelle à trois niveaux Presentation/Business/Data ne se prête pas à un chargement paresseux. L'interface utilisateur ne doit jamais provoquer directement une requête à exécuter.

Si vous souhaitez que votre couche d'interface utilisateur puisse charger des entités liées paresseusement, vous devez faire remonter le contexte de la couche d'accès de la couche de gestion à votre couche de gestion. Ce n'est peut-être pas une bonne idée, car si vous autorisez cela, vous risquez par inadvertance de vous retrouver avec des requêtes dans l'interface utilisateur qui chargent beaucoup plus de données que vous souhaitez. Vous brisez également votre séparation des préoccupations.

Votre méthode ci-dessus de chargement des données relatives à la demande par paramètre est une meilleure approche.

+0

donc afin de charger les propriétés de navigation d'un rôle (comme dans mon exemple) je devrais demander pour le rôle lui-même .. quelque chose comme Role myUsersRole = RoleManager.GetRoleWithId (user1.Role.id); et dans l'appel DAL une méthode similaire à celle pour le UsersDataManager (..) Pouvez-vous également donner quelques exemples d'arhitectures qui peuvent être utilisés avec chargement paresseux (afin de les google)? merci Dave! –

+0

@Sorin: Oui, je pense que vous êtes sur la bonne voie. L'architecture à trois niveaux que vous utilisez met la responsabilité sur le DAL et BLL pour exposer * exactement * ce dont l'interface utilisateur a besoin et rien de plus. J'utilise un chargement paresseux avec un modèle Repository dans mes projets ASP.NET MVC. C'est une architecture "plus plate" et mieux adaptée au chargement paresseux. Vous devez toujours faire attention de ne pas abuser du chargement paresseux, ou vous pouvez rencontrer des problèmes de performance. –

Questions connexes