问题描述
使用LINQ到SQL作为基于存储库解决方案的基础.我的实现如下:
iRepository
FindAll FindByID Insert Update Delete
然后,我有用于查询结果的扩展方法:
WhereSomethingEqualsTrue() ...
我的问题如下:
我的用户存储库具有N角色.我会创建一个角色存储库来管理角色吗?我担心如果我走这条路线,我最终会创建数十个存储库(几乎每张表几乎1个除外).每个表的存储库是常见的吗?
推荐答案
如果要构建一个特定于一个实体(表)的存储库,以便每个实体都有上面列出的iRepository界面中的方法列表,那么您真正在做的是实现主动记录模式.
您应该绝对不是每个表有一个存储库.您需要识别域模型中的聚合以及要在其上执行的操作.用户和角色通常密切相关,通常您的应用程序将与他们进行同时执行操作 - 这需要一个以用户为中心的单个存储库,并且是密切相关的实体集.
我从您的帖子中猜测您看到此示例.这个示例的问题在于,所有存储库都在基本级别共享相同的CRUD功能,但他并没有超越这一功能并实施任何域功能.该示例中的所有存储库看起来都一样 - 但实际上,实际存储库看起来并不相同(尽管它们仍然应该是连接的),但每个储物都将与每个相关的特定域操作.
您的存储量域操作应该更像:
userRepository.FindRolesByUserId(int userID) userRepository.AddUserToRole(int userID) userRepository.FindAllUsers() userRepository.FindAllRoles() userRepository.GetUserSettings(int userID)
等...
这些是您的应用程序要在基础数据上执行的特定操作,存储库应提供.将其视为存储库表示您将在域上执行的原子操作集.如果您选择通过通用存储库共享某些功能,并使用扩展方法扩展特定的存储库,那是一种可能对您的应用程序工作的方法.
一个好的经验法则是,您的应用程序需要实例化多个存储库以完成操作.确实会出现需求,但是如果您的应用程序中的每个事件处理程序都在杂耍六个存储库,只是为了获取用户的输入并正确实例化了输入所代表的实体,那么您可能会遇到设计问题.
.其他推荐答案
每个表的存储库是常见的吗?
不,但是您仍然可以有几个依据.您应该围绕聚合建立一个存储库.
另外,您也许可以从所有存储库中抽象一些功能...而且,由于您使用的是Linq-to-SQL,因此您可能可以...
您可以实现基本存储库,该存储库以通用方式实现所有这些共同的功能.
以下示例仅用于证明这一点.它可能需要很多改进...
interface IRepository<T> : IDisposable where T : class { IEnumerable<T> FindAll(Func<T, bool> predicate); T FindByID(Func<T, bool> predicate); void Insert(T e); void Update(T e); void Delete(T e); } class MyRepository<T> : IRepository<T> where T : class { public DataContext Context { get; set; } public MyRepository(DataContext context) { Context = Context; } public IEnumerable<T> FindAll(Func<T,bool> predicate) { return Context.GetTable<T>().Where(predicate); } public T FindByID(Func<T,bool> predicate) { return Context.GetTable<T>().SingleOrDefault(predicate); } public void Insert(T e) { Context.GetTable<T>().InsertOnSubmit(e); } public void Update(T e) { throw new NotImplementedException(); } public void Delete(T e) { Context.GetTable<T>().DeleteOnSubmit(e); } public void Dispose() { Context.Dispose(); } }
其他推荐答案
对我来说,存储库模式是关于在数据访问方法上放一个薄包装器.在您的情况下,linq to sql,但在其他情况下手工滚动.我发现自己正在做的是为此创建一个非常简单的存储库(例如Bruno列表,您已经拥有).这是为了找到事物并进行CRUD操作的原因.
但是,正如约翰内斯所述,我的服务水平更涉及总体根源.我将拥有一个使用诸如getExistinguser(int ID)之类的方法的UserService.这将在内部调用userrepository.getById()方法以检索用户.如果您的业务流程要求GetExistinguser()返回的用户类几乎总是需要填充user.isinroles()属性,那么只需使userService依赖于userrepository 和 rolereposority.在伪代码中,它看起来像这样:
public class UserService { public UserService(IUserRepository userRep, IRoleRepository roleRep) {...} public User GetById(int id) { User user = _userService.GetById(id); user.Roles = _roleService.FindByUser(id); return user; }
将使用您的linq构建UserRep和Rolerep,以下是这样:
public class UserRep : IUserRepository { public UserRep(string connectionStringName) { // user the conn when building your datacontext } public User GetById(int id) { var context = new DataContext(_conString); // obviously typing this freeform but you get the idea... var user = // linq stuff return user; } public IQueryable<User> FindAll() { var context = // ... same pattern, delayed execution } }
我个人将在内部进行存储库课程,并将用户服务和其他XXXXXXSERVICE类公开,因此请您的消费者保持诚实.因此,我再次看到存储库与与数据存储的交谈的行为更加紧密相关,但是您的服务层与您的业务流程的需求更加一致.
我经常发现自己确实过度思考了Linq对对象和所有这些东西的灵活性,并使用Iquerable et al ,而不仅仅是构建吐出我实际需要的服务方法.用户在适当的情况
public IList<User> ActiveUsersInRole(Role role) { var users = _userRep.FindAll(); // IQueryable<User>() - delayed execution; var activeUsersInRole = from users u where u.IsActive = true && u.Role.Contains(role); // I can't remember any linq and i'm type pseudocode, but // again the point is that the service is presenting a simple // interface and delegating responsibility to // the repository with it's simple methods. return activeUsersInRole; }
所以,这有点乱七八糟.不确定我是否真的有帮助,但我的建议是避免使用扩展方法过于幻想,只需添加另一层即可使每个运动部件都非常简单.为我工作.
问题描述
Using LINQ TO SQL as the underpinning of a Repository-based solution. My implementation is as follows:
IRepository
FindAll FindByID Insert Update Delete
Then I have extension methods that are used to query the results as such:
WhereSomethingEqualsTrue() ...
My question is as follows:
My Users repository has N roles. Do I create a Roles repository to manage Roles? I worry I'll end up creating dozens of Repositories (1 per table almost except for Join tables) if I go this route. Is a Repository per Table common?
推荐答案
If you are building your Repository to be specific to one Entity (table), such that each Entity has the list of methods in your IRepository interface that you listed above, then what you are really doing is an implementation of the Active Record pattern.
You should definitely not have one Repository per table. You need to identify the Aggregates in your domain model, and the operations that you want to perform on them. Users and Roles are usually tightly related, and generally your application would be performing operations with them in tandem - this calls for a single repository, centered around the User and it's set of closely related entities.
I'm guessing from your post that you've seen this example. The problem with this example is that all the repositories are sharing the same CRUD functionality at the base level, but he doesn't go beyond this and implement any of the domain functions. All the repositories in that example look the same - but in reality, real repositories don't all look the same (although they should still be interfaced), there will be specific domain operations associated with each one.
Your repository domain operations should look more like:
userRepository.FindRolesByUserId(int userID) userRepository.AddUserToRole(int userID) userRepository.FindAllUsers() userRepository.FindAllRoles() userRepository.GetUserSettings(int userID)
etc...
These are specific operations that your application wants to perform on the underlying data, and the Repository should provide that. Think of it as the Repository represents the set of atomic operations that you would perform on the domain. If you choose to share some functionality through a generic repository, and extend specific repositories with extension methods, that's one approach that may work just fine for your app.
A good rule of thumb is that it should be rare for your application to need to instantiate multiple repositories to complete an operation. The need does arise, but if every event handler in your app is juggling six repositories just to take the user's input and correctly instantiate the entities that the input represents, then you probably have design problems.
其他推荐答案
Is a Repository per Table common?
No, but you can still have several repositiories. You should build a repository around an aggregate.
Also, you might be able to abstract some functionality from all the repositories... and, since you are using Linq-to-Sql, you probably can...
You can implement a base repository which in a generic way implements all this common functionality.
The following example serves only to prove this point. It probably needs a lot of improvement...
interface IRepository<T> : IDisposable where T : class { IEnumerable<T> FindAll(Func<T, bool> predicate); T FindByID(Func<T, bool> predicate); void Insert(T e); void Update(T e); void Delete(T e); } class MyRepository<T> : IRepository<T> where T : class { public DataContext Context { get; set; } public MyRepository(DataContext context) { Context = Context; } public IEnumerable<T> FindAll(Func<T,bool> predicate) { return Context.GetTable<T>().Where(predicate); } public T FindByID(Func<T,bool> predicate) { return Context.GetTable<T>().SingleOrDefault(predicate); } public void Insert(T e) { Context.GetTable<T>().InsertOnSubmit(e); } public void Update(T e) { throw new NotImplementedException(); } public void Delete(T e) { Context.GetTable<T>().DeleteOnSubmit(e); } public void Dispose() { Context.Dispose(); } }
其他推荐答案
To me the repository pattern is about putting a thin wrapper around your data access methodology. LINQ to SQL in your case, but NHibernate, hand-rolled in others. What I've found myself doing is create a repository-per-table for that is extremely simple (like bruno lists and you already have). That is responsible for finding things and doing CRUD operations.
But then I have a service level that deals more with aggregate roots, as Johannes mentions. I would have a UserService with a method like GetExistingUser(int id). This would internally call the UserRepository.GetById() method to retrieve the user. If your business process requires the user class returned by GetExistingUser() to pretty much always need the User.IsInRoles() property to be filled, then simply have the UserService depend upon both the UserRepository and RoleRepository. In pseudo code it could look something like this:
public class UserService { public UserService(IUserRepository userRep, IRoleRepository roleRep) {...} public User GetById(int id) { User user = _userService.GetById(id); user.Roles = _roleService.FindByUser(id); return user; }
The userRep and roleRep would be constructed with your LINQ to SQL bits something like this:
public class UserRep : IUserRepository { public UserRep(string connectionStringName) { // user the conn when building your datacontext } public User GetById(int id) { var context = new DataContext(_conString); // obviously typing this freeform but you get the idea... var user = // linq stuff return user; } public IQueryable<User> FindAll() { var context = // ... same pattern, delayed execution } }
Personally I would make the repository classes internally scoped and have the UserService and other XXXXXService classes public so keep your consumers of the service API honest. So again I see repositories as more closely linked to the act of talking to a datastore, but your service layer being more closely aligned to the needs of your business process.
I've often found myself really overthinking the flexibility of Linq to Objects and all that stuff and using IQuerable et al instead of just building service methods that spit out what I actually need. User LINQ where appropriate but don't try to make the respository do everything.
public IList<User> ActiveUsersInRole(Role role) { var users = _userRep.FindAll(); // IQueryable<User>() - delayed execution; var activeUsersInRole = from users u where u.IsActive = true && u.Role.Contains(role); // I can't remember any linq and i'm type pseudocode, but // again the point is that the service is presenting a simple // interface and delegating responsibility to // the repository with it's simple methods. return activeUsersInRole; }
So, that was a bit rambling. Not sure if I really helped any, but my advise is to avoid getting too fancy with extension methods, and just add another layer to keep each of the moving parts pretty simple. Works for me.