实现一个支持实体对象延迟加载的存储库?

时间:2010-11-02 00:39:47

标签: c# design-patterns domain-driven-design repository-pattern

是否有实现使用活动目录(或任何其他数据源而不是关系数据库)作为数据源的存储库,并且可以获取能够延迟加载其关联的实体对象?

一个例子:

我们将要开发的各种应用程序中经常使用两个类

class User
{
   public string DisplayName {get; set;}
   public string UserID {get; set;}
   public string Email {get; set;}
   public string WorkPhone {get; set;}
   // etc.
   public string IList<Group> Groups {get; private set;}
   public User Manager {get; set;}
}

class Group
{
   public string DisplayName {get; set;}
   public string Email {get; set;}
   // etc.
   public IList<User> Members {get; private set;}
   public User Owner {get; private set;}
}

我希望能够在将来的所有应用程序中重用这两个类。对于某些应用程序,UserGroup的数据源现在将是活动目录,但在将来,我们可能希望使用某些数据库或数据库和活动目录的混合。为了重复代码,我会使用the repository pattern设计两个存储库MySqlDBRepositoryActiveDirectoryRepository,以便我检索和存储UserGroup。< / p>

假设我们已经拥有一个活动目录库,可用于获取文本中表示的用户信息,即您可以以字符串格式获取用户名,电子邮件,经理ID。我需要实现一个可以与Entity框架或NHibernate大致相同的方式使用的存储库,即。

 var user = _activeDirectoryRepository.GetUserByUserID("bryan");

 foreach(var group in user.Groups) 

     Console.WriteLine(group.Manager.DisplayName);

最重要的是,user.groupsgroup.Manager都应该自动延迟加载。我该如何实施_activeDirectoryRepository.GetUserByUserID("bryan")

3 个答案:

答案 0 :(得分:1)

是的,这是解决问题的好方法。

定义用于表示存储库操作的接口,例如:

public interface IAccountRepository 
{ 
   User GetUserByAccountName(string accountName);

   List<User> GetUsers(Group group); 
   List<Group> GetGroups(User user); 

   // ... etc...
} 

然后创建业务逻辑层以将存储库映射到对象:

public class Accounts
{ 
   IAccountRepository _repository; 

   public Accounts(IAccountRepository repository) 
   { 
      _repository = repository; 
   } 

   public List<User> GetUsers(Group group) 
   { 
      return _repository.GetUsers(group); 
   } 

   public List<Group> GetGroups(User user) 
   { 
      return _repository.GetGroups(user); 
   } 

   public User GetUserByAccountName(string accountName)
   {
       return _repository.GetUserByAccountName(accountName); 
   }



   // etc...
} 

然后使用:

static void Main(string[] args) 
{ 
   // Load from the MSSQL repository. 
   Accounts accounts = new Accounts(new MSSQLAccountRepository());

   User user = accounts.GetUserByAccountName("someuser"); 

   //...

}

另见:Repository pattern tutorial in C#

答案 1 :(得分:1)

  

我希望能够在所有应用程序中重用这两个类。

好的,我建议重新评估这个想法。

Reuse is [often] a fallacy

将这些类移动到链接到多个应用程序的可重用库中,您获得的增益究竟是什么?

将这些类移动到与多个应用程序相关联的可重用库中,痛苦究竟是什么?

我非常有信心,如果你沿着这条路走下去,你将会遇到相当更多的痛苦而不是获得。

  

我不想将用户,组类定义(源代码)从一个应用程序复制到另一个应用程序。

为什么呢?这真的让你获得了什么?

通过点击“添加参考”与“复制和放大”,您真实希望节省多少时间?粘贴代码文件目录并单击“添加现有项”?

您认为在首次更改此库并在某个应用程序中创建错误时,您认为自己会丢失多少时间而不是您进行更改的应用程序?您需要花多少时间来管理链接到多个不同应用程序的多个版本的共享库?你认为你浪费了多少时间来过度概括这个库的概念,而不仅仅是为手头的应用程序做了什么?延长用户和用户需要多少额外时间如果您在需要更多实体的域中使用它而不是放入库中,请对该组进行分组?

如果你继续这样做,我愿意打赌你会失去比你获得的更多。

答案 2 :(得分:1)

经过一番思考,我发现这个解决方案可能会很好。

ActiveDirectoryUserRepository一起,我可以创建一个继承自ProxyUser的内部类User,它引用ActiveDirectoryUserRepositoryProxyUser应该在第一次调用时返回Groups属性。


// User.cs
public class User
{
    public virtual string Name { get; set; }
    public virtual string DisplayName {get; set;}
    public virtual string Email { get; set; }

    public virtual IList<Group> Groups { get; set; }
}

// Group.cs
public class Group
{
    public virtual string Name { get; set; }
    public virtual string Email { get; set; }

    public virtual IList<User> Members { get; set; }
}

//  ProxyUser.cs. It should be in the same assembly as ActiveDirectoryUserRepository 

internal class ProxyUser : User
{

   private ActiveDirectoryUserRepository _repository ;
   internal ProxyUser(ActiveDirectoryUserRepository repository)
   {
       _repository = repository;
   }

   internal string IList<string> GroupNames { get; set; }

   private IList<Group> _groups;
   public override IList<Group> Groups 
   { 
      get
      {
         if (_groups == null)
         {
            if (GroupNames != null && GroupNames.Count > 0)
            {
                _groups = new List<Group>();
                foreach(string groupName in GroupNames)
                   _groups.Add(_repository.FindGroupByName(groupName);
            }
         }
         return _groups;
       }

       set
       {
           _groups = value;
       }
    }
}

// ProxyGroup.cs

internal class ProxyGroup : Group
{
    // This class should be similar to ProxyUser
}

// ActiveDirectoryUserRepository.cs

public class ActiveDirectoryUserRepository
{
    public User FindUserByName(string name)
    {
         DirectorySearcher searcher = new DirectorySearcher();

         // ...
         // set up the filter and properties of the searcher
         // ...

         Result result = searcher.FindOne();
         User user = new ProxyUser(this)
         {
             Name = result.Properties["displayName"][0],
             Email = result.Properties["email"][0],
             GroupNames = new List<string>()
         };

         foreach(string groupName in result.Properties["memberOf"])
         {
             user.GroupNames.Add(groupName);
         }

         return user;
    }

    public Group FindGroupByName(string name)
    {
        // Find the Group, which should be similar to FindUserByName
    }
}