我最近学习了 ASP.NET MVC(我喜欢它)。我正在与一家使用依赖项注入(inject)在每个请求中加载存储库实例的公司合作,并且我熟悉该存储库的使用。
但现在我正在编写我自己的几个 MVC 应用程序。我不完全了解我公司使用存储库的方式和原因,我正在尝试确定实现数据访问的最佳方法。
我正在使用 C# 和 Entity Framework (所有最新版本)。
我看到了三种处理数据访问的通用方法。
每次访问数据时,using 语句中的常规数据库上下文。这很简单,而且工作正常。但是,如果两个位置需要在一次请求中读取相同的数据,则必须读取两次数据。 (每个请求只有一个存储库,两个地方都会使用同一个实例,我知道第二次读取只会返回第一次读取的数据。)
一个典型的 repository pattern .由于我不明白的原因,这种典型模式涉及为数据库中使用的每个表创建一个包装类。这对我来说似乎是错误的。事实上,由于它们也是作为接口(interface)实现的,从技术上讲,我会为每个表创建两个包装器类。 EF 为我创建表。我认为这种方法没有意义。
还有一个generic repository pattern其中创建了一个存储库类来为所有实体对象提供服务。这对我来说更有意义。但这对其他人有意义吗?上面的链接是最好的方法吗?
我很想从其他人那里得到一些关于这个主题的意见。您是在编写自己的存储库,使用上述存储库之一,还是在做一些完全不同的事情。请分享。
最佳答案
我混合使用了#2 和#3,但如果可能的话我更喜欢严格的通用存储库(甚至比#3 的链接中建议的更严格)。 #1 不好,因为它在单元测试中表现不佳。
如果您有一个较小的域或需要限制您的域允许查询哪些实体,我想 #2- 或 #3 定义了实体特定的存储库接口(interface),它们本身实现了通用存储库-是有意义的。但是,我发现为我要查询的每个实体编写一个接口(interface)和一个具体的实现是很累人的,也没有必要。有什么好public interface IFooRepository : IRepository<Foo> (同样,除非我需要将开发人员限制在一组允许的聚合根中)?
我只是用 Add 定义了我的通用存储库接口(interface), Remove , Get , GetDeferred , Count , 和 Find方法(Find 返回一个允许 LINQ 的 IQueryable 接口(interface)),创建一个具体的通用实现,然后收工。我严重依赖Find因此 LINQ。如果我需要多次使用特定查询,我会使用扩展方法并使用 LINQ 编写查询。
这涵盖了我 95% 的持久性需求。如果我需要执行某种一般无法完成的持久性操作,我会使用自制的 ICommand应用程序接口(interface)。例如,假设我正在使用 NHibernate,我需要在我的域中执行一个复杂的查询,或者我可能需要执行一个批量命令。 API 大致如下所示:
// marker interface, mainly used as a generic constraint
public interface ICommand
{
}
// commands that return no result, or a non-query
public interface ICommandNoResult : ICommand
{
void Execute();
}
// commands that return a result, either a scalar value or record set
public interface ICommandWithResult<TResult> : ICommand
{
TResult Execute();
}
// a query command that executes a record set and returns the resulting entities as an enumeration.
public interface IQuery<TEntity> : ICommandWithResult<IEnumerable<TEntity>>
{
int Count();
}
// used to create commands at runtime, looking up registered commands in an IoC container or service locator
public interface ICommandFactory
{
TCommand Create<TCommand>() where TCommand : ICommand;
}
现在我可以创建一个接口(interface)来表示特定的命令。
public interface IAccountsWithBalanceQuery : IQuery<AccountWithBalance>
{
Decimal MinimumBalance { get; set; }
}
我可以创建一个具体的实现并使用原始 SQL、NHibernate HQL 等,然后将其注册到我的服务定位器。
现在在我的业务逻辑中我可以做这样的事情:
var query = factory.Create<IAccountsWithBalanceQuery>();
query.MinimumBalance = 100.0;
var overdueAccounts = query.Execute();
您还可以将规范模式与 IQuery 一起使用构建有意义的、用户输入驱动的查询,而不是拥有一个包含数百万个令人困惑的属性的接口(interface),但前提是您不会发现规范模式本身令人困惑;)。
最后一个难题是当您的存储库需要执行特定的存储库前后操作时。现在,您可以非常轻松地为特定实体创建通用存储库的实现,然后覆盖相关方法并执行您需要执行的操作,并更新您的 IoC 或服务定位器注册并完成它。
但是,有时这种逻辑是交叉的,难以通过覆盖存储库方法来实现。所以我创建了IRepositoryBehavior ,这基本上是一个事件接收器。 (以下只是我头脑中的一个粗略定义)
public interface IRepositoryBehavior
{
void OnAdding(CancellableBehaviorContext context);
void OnAdd(BehaviorContext context);
void OnGetting(CancellableBehaviorContext context);
void OnGet(BehaviorContext context);
void OnRemoving(CancellableBehaviorContext context);
void OnRemove(BehaviorContext context);
void OnFinding(CancellableBehaviorContext context);
void OnFind(BehaviorContext context);
bool AppliesToEntityType(Type entityType);
}
现在,这些行为可以是任何东西。审计、安全检查、软删除、强制域约束、验证等。我创建一个行为,将其注册到 IoC 或服务定位器,然后修改我的通用存储库以接收已注册的 IRepositoryBehavior 的集合。 s,并根据当前存储库类型检查每个行为,并将操作包装在每个适用行为的前/后处理程序中。
这是一个软删除行为示例(软删除意味着当有人要求删除一个实体时,我们只是将其标记为已删除,因此它不能再次返回,但实际上从未被物理删除)。
public SoftDeleteBehavior : IRepositoryBehavior
{
// omitted
public bool AppliesToEntityType(Type entityType)
{
// check to see if type supports soft deleting
return true;
}
public void OnRemoving(CancellableBehaviorContext context)
{
var entity = context.Entity as ISoftDeletable;
entity.Deleted = true; // when the NHibernate session is flushed, the Deleted column will be updated
context.Cancel = true; // set this to true to make sure the repository doesn't physically delete the entity.
}
}
是的,这基本上是 NHibernate 事件监听器的简化和抽象实现,但这就是我喜欢它的原因。 A) 我可以在不将 NHibernate 引入图片的情况下对行为进行单元测试 B) 我可以在 NHibernate 之外使用这些行为(假设存储库是包装 REST 服务调用的客户端实现) C) NH 的事件监听器可能是一个真正的痛苦;)
关于c# - ASP.NET MVC 的最佳存储库模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10925257/
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来