Ale*_*lex 33 .net c# extension-methods iqueryable repository-pattern
我有存储库(例如ContactRepository,UserRepository等),它封装了对域模型的数据访问.
当我在寻找数据时,例如
生日是1960年以后的联系人
(等等),
我开始实现诸如FirstNameStartsWith(字符串前缀)和YoungerThanBirthYear(int year)之类的存储库方法,基本上遵循了许多示例.
然后我遇到了一个问题 - 如果我必须结合多个搜索怎么办?我的每个存储库搜索方法(如上所述)仅返回一组有限的实际域对象.为了寻找更好的方法,我开始在IQueryable <T>上编写扩展方法,例如:
public static IQueryable<Contact> FirstNameStartsWith(
this IQueryable<Contact> contacts, String prefix)
{
return contacts.Where(
contact => contact.FirstName.StartsWith(prefix));
}
Run Code Online (Sandbox Code Playgroud)
现在我可以做一些事情
ContactRepository.GetAll().FirstNameStartsWith("tex").YoungerThanBirthYear(1960);
Run Code Online (Sandbox Code Playgroud)
然而,我发现自己正在编写扩展方法(并且发明了疯狂的类,例如全面的ContactsQueryableExtensions,并且通过在适当的存储库中包含所有内容而失去了"良好的分组".
这真的是这样做的方式,还是有更好的方法来实现同样的目标?
Cha*_*ert 12
在我目前的工作开始之后,我最近一直在考虑这个问题.我已经习惯了Repositories,他们会像你建议的那样只使用裸骨库来完成IQueryable路径.
我觉得回购模式是合理的,并且在描述您希望如何使用应用程序域中的数据方面做了一个半有效的工作.但是,您所描述的问题肯定会发生.除了简单的应用程序之外,它变得凌乱,快速.
或许有方法可以重新思考为什么要以这么多方式询问数据?如果没有,我真的觉得混合方法是最好的方法.为您重复使用的东西创建repo方法.这实际上是有道理的东西.干,等等.但那些一次性的?为什么不利用IQueryable和你可以用它做的性感的东西呢?正如你所说,愚蠢的是为它创建一个方法,但这并不意味着你不需要数据.DRY真的不适用吗?
要做到这一点需要纪律,但我认为这是一条合适的道路.
@Alex - 我知道这是一个老问题,但我要做的就是让Repository只做一些简单的事情.这意味着,获取表或视图的所有记录.
然后,在SERVICES层(你正在使用一个n层解决方案,对吧?:))我会在那里处理所有'特殊'查询的东西.
好的,示例时间.
ContactRepository.cs
public IQueryable<Contact> GetContacts()
{
return (from q in SqlContext.Contacts
select q).AsQueryable();
}
Run Code Online (Sandbox Code Playgroud)
很好,很简单.SqlContext是你的实例EF Context..它有一个Entity叫它的Contacts..它基本上是你的sql Contacts类.
这意味着,该方法基本上是这样做的:SELECT * FROM CONTACTS...但它没有使用该查询访问数据库..它现在只是一个查询.
好吧..下一层.. KICK ...直到我们走(启人?)
ContactService.cs
public ICollection<Contact> FindContacts(string name)
{
return FindContacts(name, null)
}
public ICollection<Contact> FindContacts(string name, int? year)
{
IQueryable<Contact> query = _contactRepository.GetContacts();
if (!string.IsNullOrEmpty(name))
{
query = from q in query
where q.FirstName.StartsWith(name)
select q;
}
if (int.HasValue)
{
query = from q in query
where q.Birthday.Year <= year.Value
select q);
}
return (from q in query
select q).ToList();
}
Run Code Online (Sandbox Code Playgroud)
完成.
让我们回顾一下.首先,我们从简单的" 从联系人那里获取所有内容 "查询开始.现在,如果我们提供了名称,请添加过滤器以按名称过滤所有联系人.接下来,如果我们提供了一年,那么我们按年度过滤生日.等等.最后,我们点击DB(使用此修改后的查询),看看我们得到的结果.
笔记:-
祝好运 :)
我意识到这已经很老了,但是最近我一直在处理这个问题,并且得出了与Chad相同的结论:只要遵守一点纪律,扩展方法和存储库方法的混合似乎效果最好。
我在(实体框架)应用程序中遵循的一些一般规则:
如果该方法仅用于订购,我更喜欢的是操作上写扩展方法IQueryable<T>或IOrderedQueryable<T>(杠杆基础提供。)例如,
public static IOrderedQueryable<TermRegistration> ThenByStudentName(
this IOrderedQueryable<TermRegistration> query)
{
return query
.ThenBy(reg => reg.Student.FamilyName)
.ThenBy(reg => reg.Student.GivenName);
}
Run Code Online (Sandbox Code Playgroud)
现在,我可以ThenByStudentName()在存储库类中根据需要使用。
如果该方法涉及通过原始参数进行查询,则通常需要an ObjectContext且不易实现static。我将这些方法留在存储库中,例如
public Student GetById(int id)
{
// Calls context.ObjectSet<T>().SingleOrDefault(predicate)
// on my generic EntityRepository<T> class
return SingleOrDefault(student => student.Active && student.Id == id);
}
Run Code Online (Sandbox Code Playgroud)
但是,如果该方法改为EntityObject使用其导航属性查询a ,则通常可以static很轻松地将其实现,并将其实现为扩展方法。例如
public static TermRegistration GetLatestRegistration(this Student student)
{
return student.TermRegistrations.AsQueryable()
.OrderByTerm()
.FirstOrDefault();
}
Run Code Online (Sandbox Code Playgroud)
现在,我可以方便地编写代码,someStudent.GetLatestRegistration()而无需在当前范围内使用存储库实例。
如果该方法返回一些IEnumerable,ICollection或者IList,那么我想使它static如果可能的话,并把它留在仓库,即使它使用的导航性能。 例如
public static IList<TermRegistration> GetByTerm(Term term, bool ordered)
{
var termReg = term.TermRegistrations;
return (ordered)
? termReg.AsQueryable().OrderByStudentName().ToList()
: termReg.ToList();
}
Run Code Online (Sandbox Code Playgroud)
这是因为我的GetAll()方法已经存在于存储库中,并且有助于避免混乱的扩展方法。
不将这些“集合获取器”实现为扩展方法的另一个原因是,它们将需要更多冗长的命名才能有意义,因为未暗示返回类型。例如,最后一个示例将变为GetTermRegistrationsByTerm(this Term term)。
我希望这有帮助!
| 归档时间: |
|
| 查看次数: |
5264 次 |
| 最近记录: |