代码之家  ›  专栏  ›  技术社区  ›  Shuo

实现一个支持实体对象的懒惰的存储库?

  •  4
  • Shuo  · 技术社区  · 15 年前

    是否仍然需要实现一个使用Active Directory(或关系数据库以外的任何其他数据源)作为数据源的存储库,并且能够获取能够延迟加载其关联的实体对象?

    一个例子:

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

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

    我希望能够在将来的所有应用程序中重用这两个类。对于某些应用程序,数据源 User Group 现在将是Active Directory,但在将来,我们可能要使用一些数据库或数据库和Active Directory的混合。为了代码重用,我将使用 the repository pattern 设计两个存储库 MySqlDBRepository ActiveDirectoryRepository 供我检索和存储 用户 S和 S.

    假设我们已经有了一个用于Active Directory的库,它可以用来获取文本中表示的用户信息,也就是说,您可以获取字符串格式的用户名、电子邮件、管理器ID。我需要实现一个存储库,它可以像实体框架或nhibernate那样使用,即

     var user = _activeDirectoryRepository.GetUserByUserID("bryan");
    
     foreach(var group in user.Groups) 
    
         Console.WriteLine(group.Manager.DisplayName);
    

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

    3 回复  |  直到 15 年前
        1
  •  1
  •   Community Mohan Dere    9 年前

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

    定义一个接口来表示存储库的操作,例如:

    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#

        2
  •  1
  •   quentin-starin    15 年前

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

    好吧,我建议你重新评估一下这个想法。

    Reuse is [often] a fallacy .

    到底是什么 获得 将这些类移动到一个链接到多个应用程序的可重用库中,会有什么好处?

    到底是什么 疼痛 您将体验到将这些类移动到链接到多个应用程序的可重用库中吗?

    我很有信心,如果你走这条路,你会体验到 相当地 痛苦大于收获。

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

    为什么?这对你有什么好处?

    通过单击“添加引用”和“复制和粘贴代码文件目录”并单击“添加现有项”,您真的希望节省多少时间?

    当你第一次更改这个库并在某个应用程序中创建一个bug,而不是为其进行更改时,你认为你会损失多少时间?您将花费多少时间来管理链接到多个不同应用程序的共享库的多个版本?您认为要花费多少时间来概括这个库的概念,而不是仅仅做手头上的应用程序所需的工作?当您在需要更多实体的域中使用用户组时,扩展该用户组需要多少额外时间,而不是将其放入库中?

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

        3
  •  1
  •   Shuo    15 年前

    经过思考,我发现这个解决方案可能是好的。

    随着 ActiveDirectoryUserRepository ,我可以创建一个内部类 ProxyUser 继承自 User ,它引用了 活动DirectoryUserRepository . 这个 代理服务器 应该返回 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
        }
    }