implement a repository that supports lazy loaing for the entity objects?
Is there anyway to implement an repository that uses active directory (or any other data source than relational db) as the data source and can get entity objects that has the ability to lazy load their associations?
An example:
Two classes will be frequently used in various applications we are going to develop
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;}
}
I want to be able to reuse these two classes in all our future applications. For some application, the data source for User
and Group
would now be active directory, but in the future, we may wanna use some database or a mixture of database and active directory. In order for code reuse, I would employ the repository pattern开发者_开发技巧 to design two repositories MySqlDBRepository
and ActiveDirectoryRepository
for me to retrieve and store User
s and Group
s.
Suppose we already have a library for active directory, which can be used to get user information represented in texts, i.e. you can get user's name, email, manager id all in string format. I need to implement a repository that can be used in much the same way as Entity framework or NHibernate, i.e.
var user = _activeDirectoryRepository.GetUserByUserID("bryan");
foreach(var group in user.Groups)
Console.WriteLine(group.Manager.DisplayName);
Most importantly, user.groups
and group.Manager
both should be lazy-loading automatically. How am I supposed to implement _activeDirectoryRepository.GetUserByUserID("bryan")
?
Yes, that is a good way of approaching your problem.
Define an interface to represent a repository's actions, for example:
public interface IAccountRepository
{
User GetUserByAccountName(string accountName);
List<User> GetUsers(Group group);
List<Group> GetGroups(User user);
// ... etc...
}
Then create a business logic layer to map repository onto objects:
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...
}
Then to use:
static void Main(string[] args)
{
// Load from the MSSQL repository.
Accounts accounts = new Accounts(new MSSQLAccountRepository());
User user = accounts.GetUserByAccountName("someuser");
//...
}
Also see: Repository pattern tutorial in C#
I want to be able to reuse these two classes in all our applications.
Ok, I'm going to suggest re-evaluating this whole idea.
Reuse is [often] a fallacy.
What exactly is the gain you get from moving these classes into a reusable library linked to several applications?
What exactly is the pain you'll experience moving these classes into a reusable library linked to several applications?
I'm pretty confident that if you go this route you will experience considerably more pain than gain.
I don't want to copy the User, Group class definitions (source code) from one app to another.
Why? What does this really gain you?
How much time do you honestly expect to save by clicking Add Reference versus copy & pasting a directory of code files and clicking Add Existing Item?
How much time do you think you'll lose the first time you change this library and create a bug in some application other than the one you made the change for? How much time will you spend managing multiple versions of a shared library linked to several different applications? How much time do you think you'll waste over-generalizing the concepts for this library, versus just doing what you need for the application at hand? How much extra time will it take to extend the User & Group when you use it in a domain that requires more of the Entities than what you put in the library?
If you go forward with this I'd be willing to lay wager that you'll lose more than you gain.
After some thought, I found this solution might be good.
Along with ActiveDirectoryUserRepository
, I can create a internal class ProxyUser
that inherits from User
, which takes a reference to the ActiveDirectoryUserRepository
. The ProxyUser
should return the Groups
property the first time it is invoked.
// 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
}
}
精彩评论