Реализовать репозиторий, который поддерживает отложенную загрузку для объектов сущностей? - PullRequest
4 голосов
/ 02 ноября 2010

Есть ли способ реализовать хранилище, в котором в качестве источника данных используется активный каталог (или любой другой источник данных, отличный от реляционного db), и который может получать объекты сущностей, способные лениво загружать свои ассоциации?

Пример:

Два класса будут часто использоваться в различных приложениях, которые мы будем разрабатывать

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 теперь будет активный каталог, но в будущем мы можем захотеть использовать некоторую базу данных или смесь базы данных и активного каталога.Для повторного использования кода я бы использовал шаблон хранилища , чтобы спроектировать два хранилища MySqlDBRepository и ActiveDirectoryRepository, чтобы я мог получить и сохранить User s и Group s.

Предположим, у нас уже есть библиотека для активного каталога, которую можно использовать для получения информации о пользователе, представленной в текстах, т.е. вы можете получить имя пользователя, адрес электронной почты, идентификатор менеджера - все в строковом формате.Мне нужно реализовать репозиторий, который можно использовать почти так же, как Entity Framework или 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 ]

1 голос
/ 03 ноября 2010

Я хочу иметь возможность использовать эти два класса во всех наших приложениях.

Хорошо, я собираюсь предложить переоценку всей этой идеи.

Повторное использование [часто] является ошибкой .

Какой именно выигрыш вы получаете от перемещения этих классов в библиотеку многократного использования, связанную с несколькими приложениями?

Что такое боль , с которой вы столкнетесь при переносе этих классов в библиотеку многократного использования, связанную с несколькими приложениями?

Я совершенно уверен, что если вы пойдете этим путем, вы будете испытывать значительно * на 1020 * больше боли, чем усиления.

Я не хочу копировать определения классов User (Group) (исходный код) из одного приложения в другое.

Почему? Что это действительно дает тебе?

Сколько времени вы честно ожидаете сэкономить, нажав «Добавить ссылку» вместо «Копировать и вставить каталог файлов кода» и «Добавить существующий элемент»?

Как вы думаете, сколько времени вы потеряете при первом изменении этой библиотеки и создании ошибки в каком-либо приложении, отличном от того, для которого вы внесли изменение? Сколько времени вы потратите на управление несколькими версиями общей библиотеки, связанной с несколькими различными приложениями? Как вы думаете, сколько времени вы потратите чрезмерно на общие концепции этой библиотеки, вместо того, чтобы просто делать то, что вам нужно для приложения под рукой? Сколько дополнительного времени потребуется для расширения User & Group, когда вы используете его в домене, который требует больше Entities, чем то, что вы положили в библиотеку?

Если вы пойдете вперед с этим, я буду готов сделать ставку, что вы потеряете больше, чем заработаете.

1 голос
/ 13 ноября 2010

Подумав, я обнаружил, что это решение может быть хорошим.

Наряду с ActiveDirectoryUserRepository я могу создать внутренний класс ProxyUser, который наследуется от User, который принимает ссылку на ActiveDirectoryUserRepository.ProxyUser должен возвращать свойство 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
    }
}
1 голос
/ 02 ноября 2010

Да, это хороший способ решения вашей проблемы.

Определите интерфейс для представления действий репозитория, например:

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"); 

   //...

}

Также см .: Учебник по шаблону репозитория на C #

...