在为我的 ASP.NET 项目实现存储库模式时,我遇到了一些无法解决的问题。所以我有几个关于如何以正确的方式实现存储库模式的问题。
根据我的经验,我认为只有在我的应用程序中没有行为的类/模型,在它们的存储库旁边并不是好的 OOP。但是,这就是我实现存储库模式的方式。我只是在任何需要存储库实例的地方制作,以执行一些操作。这种方法的结果是我所有的域类都没有行为。
它们只是没有方法的保存数据的对象。我的老师对我说,我用的是瘦模型,我应该努力做胖模型。 作为对该反馈的回应,我在类中实现了一些业务逻辑,但遇到了一些问题:
场景:
我的 User 类有一个 Friends 列表,其中包含 User 对象,代表某个用户的 friend 。向用户添加新 friend 时,域类方法会检查“ friend ”是否已存在于 friend 列表中。如果没有,他将被添加到列表中。这些更改需要发送到数据库以进行持久化。
我知道这必须在每个域类的存储库中完成,但是对存储库方法的调用属于应用程序架构中的什么地方?
现在我在域类方法本身中调用存储库方法,以持久保存对数据库的更改:
public void AddFriend(User friend)
{
foreach(User f in Friends)
{
if(f.Username == friend.Username)
{
throw new Exception(String.Format("{0} is already a friend.", friend.Username));
}
}
Friends.Add(friend);
userRepo.AddFriend(this.Id, friend.Id);
}
这是一个好方法吗,出于某种原因我认为它不是。关于单元测试,我们需要使用这种方法进行一些依赖注入(inject),这对我来说它不是一个独立的类(良好的可测试单元)。我读过一些人的帖子,说他们使用额外的服务层或其他东西。我认为这里需要这种抽象,但是某个服务层是什么样的?里面有什么,有哪些方法等?
我看到其他一些学生在域类中使用静态方法,这些方法提供添加新对象、更新对象、删除对象和获取所有对象等功能。
例子:
public class Tram
{
private static TramRepository Repo = new TramRepository(new DBTram());
public static void AddTram(int tramID, TramType type, int lineNr)
{
Tram tram = new Tram(tramID, type, TramStatus.depot, lineNr, true, null);
Repo.AddTram(tram);
}
public static List<Tram> GetAll()
{
Repo.GetAll();
}
}
我发现在域类中使用一种方法将新实体添加到数据库是一件很奇怪的事情,该实体本身就是该实体。同样对于 GetAll() 方法,我认为在类本身中有一个获取所有电车的方法很奇怪。所以一个电车对象可以得到所有的电车。我认为这是实现存储库模式的一种奇怪方式。我说得对吗?
那么,这里需要什么样的抽象呢?是否必须有一个额外的层?如果是这样,这一层是什么样子的? (示例代码)或者我搜索的方向错误,是否有另一种解决方案可以解决存储库模式的单元测试问题?
我每次都会遇到这个架构问题,确保我需要它来回答。
这个问题我很难解释清楚,希望大家理解。
最佳答案
你的问题绝对正常,但不要指望找到绝对的答案。欢迎来到软件行业!
这是我的看法:
- Is it good OOP to have an application that relies on an architecture that, next to their repositories, only has models/classes that hold values with no behaviour?
我认为您尝试实现存储库模式,但您错过了更高的架构 View 。大多数应用程序至少在 3 层中解耦: View (演示)、业务和数据访问。 存储库模式发生在 DataAccess 中,这是您可以找到纯数据对象的地方。 但是这个数据访问层被业务层使用,你会在业务层中找到一个域模型、具有业务行为和数据的类。 单元测试工作必须在业务层的域模型上进行。 这些测试不应该关心数据是如何存储的。
- Where do I call repository methods in the architecture of the application?
同样没有绝对的答案,但通常使用像业务服务这样的东西是有意义的。这些服务可以安排不同域对象之间的流,并将它们加载并保存在存储库中。 这基本上就是您在 AddFriend 类中所做的,它属于业务层。
Regarding to unit testing, we need with this approach some dependancy injection, which says to me that it is not a independant class
业务服务通常依赖于存储库,这是单元测试的一个非常常见的情况。 Tram 类可以持有业务行为,并且仍然是独立的。 AddTram 业务服务需要一个存储库,并且依赖注入(inject)允许对其进行测试。
- Methods that insert, update and delete new entities in a database, are they supposed to be in a class itself?
对于这一点,我可以明确而响亮:请不要那样做,是的,CRUD 操作属于 Tram Repository。这当然不是业务逻辑。这就是为什么在您的示例中您需要两个不同层中的两个类:
"Because i have seen some other students making use of static methods in a domain class which provides these functionality"
使用静态方法显然不是一个好主意,这意味着任何人都可以通过您的对象存储数据,即使它应该处理业务案例。再次重申,数据存储不是业务案例,而是技术必需品。
现在公平地说,所有这些概念都需要在上下文中进行讨论才能有意义,并且需要根据每个新项目进行调整。这就是您的工作既艰巨又令人兴奋的原因:背景为王。
我还写了一个blog article以 MVVM 为中心,但我认为它可以帮助理解我的答案。
关于c# - 如何以正确的方式实现存储库模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41767133/
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123
我主要使用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
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha