创建一个新的 MVC 项目并且喜欢数据层中存储库的想法,所以我已经实现了它们。我还创建了一个服务层来处理所有业务逻辑和验证,该层又使用适当的存储库。像这样(我用的是Simple Injector来注入(inject))
DAL 层
public class MyRepository {
private DbContext _context;
public MyRepository(DbContext context) {
_context = context;
}
public MyEntity Get(int id)
{
return _context.Set<MyEntity>().Find(id);
}
public TEntity Add(MyEntity t)
{
_context.Set<MyEntity>().Add(t);
_context.SaveChanges();
return t;
}
public TEntity Update(MyEntity updated, int key)
{
if (updated == null)
return null;
MyEntity existing = _context.Set<MyEntity>().Find(key);
if (existing != null)
{
_context.Entry(existing).CurrentValues.SetValues(updated);
_context.SaveChanges();
}
return existing;
}
public void Delete(MyEntity t)
{
_context.Set<MyEntity>().Remove(t);
_context.SaveChanges();
}
}
服务层
public class MyService {
private MyRepository _repository;
public MyService(MyRepository repository) {
_repository = repository;
}
public MyEntity Get(int id)
{
return _repository.Get(id);
}
public MyEntity Add(MyEntity t)
{
_repository.Add(t);
return t;
}
public MyEntity Update(MyEntity updated)
{
return _repository.Update(updated, updated.Id);
}
public void Delete(MyEntity t)
{
_repository.Delete(t);
}
}
现在这很简单,所以我可以使用以下代码来更新对象。
MyEntity entity = MyService.Get(123);
MyEntity.Name = "HELLO WORLD";
entity = MyService.Update(entity);
或者这个来创建一个对象
MyEntity entity = new MyEntity();
MyEntity.Name = "HELLO WORLD";
entity = MyService.Add(entity);
// entity.Id is now populated
现在说我需要根据另一个项目的创建 ID 更新一个项目,我可以使用上面的代码一切正常,但是如果发生错误会怎样?我需要某种交易/回滚。这是工作单元模式要解决的问题吗?
所以我想我需要在我的 UnitOfWork 对象中包含 DbContext,所以我创建了一个这样的对象?
public class UnitOfWork : IDisposable {
private DbContext _context;
public UnitOfWork(DbContext context) {
_context = context;
}
public Commit() {
_context.SaveChanges();
}
public Dispose() {
_context.Dispose();
}
}
好吧,这很简单。 UnitOfWork 也保存上下文(无论如何我在所有存储库上使用相同的上下文)并且它调用 SaveChanges() 方法。然后我会从我的存储库中删除 SaveChanges() 方法调用。所以要添加我会做以下事情:
UnitOfWork uow = new UnitOfWork(new DbContext()); // i would inject this somehow
MyEntity entity = new MyEntity();
MyEntity.Name = "HELLO WORLD";
entity = MyService.Add(entity);
uow.Commit();
但是如果我需要创建一个对象然后根据该 Id 更新其他对象,这将不起作用,因为在我调用 uow 上的 Commit 之前不会创建 Id。示例
UnitOfWork uow = new UnitOfWork(new DbContext()); // i would inject this somehow
MyEntity entity = new MyEntity();
MyEntity.Name = "HELLO WORLD";
entity = MyService.Add(entity);
// entity.Id is NOT populated
MyEntity otherEntity = MyService.Get(123);
otherEntity.OtherProperty = entity.Id;
MyService.Update(otherEntity);
uow.Commit(); // otherEntity.OtherProperty is not linked.....?
所以我觉得这个 UnitOfWork 类不对...也许我想念某些东西。
我需要能够添加一个实体并获取该 Id 并将其用于另一个实体,但如果发生错误,我想像 ado.net 事务一样“回滚”。
使用 Entity Framework and Repositories 是否可以实现此功能?
最佳答案
我必须首先声明,没有唯一正确的方法来解决这个问题。我只是在这里展示我可能会做的事情。
首先, DbContext本身实现了工作单元模式。打电话SaveChanges 确实创建了一个数据库事务,以便在出现问题时回滚对数据库执行的每个查询。
现在,您的当前设计存在一个主要问题:您的存储库调用 SaveChanges在 DbContext 上.这意味着你制作 XXXRepository负责提交您对工作单元所做的所有修改,而不仅仅是对您的存储库负责的 XXX 实体的修改。
另一件事是DbContext本身也是一个存储库。所以抽象出 DbContext在另一个存储库中使用只会在现有抽象上创建另一个抽象,IMO 的代码太多了。
此外,您可能需要访问 YYY 存储库中的 XXX 实体和 XXX 存储库中的 YYY 实体,因此为了避免循环依赖,您最终会得到无用的 MyRepository : IRepository<TEntity>。只是复制了所有 DbSet方法。
我会删除整个存储库层。我会使用 DbContext直接在服务层内。当然,您可以将所有不想在服务层中复制的复杂查询分解为因素。像这样的东西:
public MyService()
{
...
public MyEntity Create(some parameters)
{
var entity = new MyEntity(some parameters);
this.context.MyEntities.Add(entity);
// Actually commits the whole thing in a transaction
this.context.SaveChanges();
return entity;
}
...
// Example of a complex query you want to use multiple times in MyService
private IQueryable<MyEntity> GetXXXX_business_name_here(parameters)
{
return this.context.MyEntities
.Where(z => ...)
.....
;
}
}
使用这种模式,由于 DbContext.SaveChanges,对服务类的每个公共(public)调用都在事务内执行具有交易性。
现在,对于第一个实体插入后需要 ID 的示例,一个解决方案是不使用 ID,而是使用实体本身。所以你让 Entity Framework 和它自己的工作单元模式实现来处理它。
所以代替:
var entity = new MyEntity();
entity = mydbcontext.Add(entity);
// what should I put here?
var otherEntity = mydbcontext.MyEntities.Single(z => z.ID == 123);
otherEntity.OtherPropertyId = entity.Id;
uow.Commit();
你有:
var entity = new MyEntity();
entity = mydbcontext.Add(entity);
var otherEntity = mydbcontext.MyEntities.Single(z => z.ID == 123);
otherEntity.OtherProperty = entity; // Assuming you have a navigation property
uow.Commit();
如果您没有导航属性,或者如果您有更复杂的用例要处理,解决方案是在您的公共(public)服务方法中使用 good gold transaction:
public MyService()
{
...
public MyEntity Create(some parameters)
{
// Encapuslates multiple SaveChanges calls in a single transaction
// You could use a ITransaction if you don't want to reference System.Transactions directly, but don't think it's really useful
using (var transaction = new TransactionScope())
{
var firstEntity = new MyEntity { some parameters };
this.context.MyEntities.Add(firstEntity);
// Pushes to DB, this'll create an ID
this.context.SaveChanges();
// Other commands here
...
var newEntity = new MyOtherEntity { xxxxx };
newEntity.MyProperty = firstEntity.ID;
this.context.MyOtherEntities.Add(newEntity);
// Pushes to DB **again**
this.context.SaveChanges();
// Commits the whole thing here
transaction.Commit();
return firstEntity;
}
}
}
如果需要,您甚至可以在事务范围内调用多个服务方法:
public class MyController()
{
...
public ActionResult Foo()
{
...
using (var transaction = new TransactionScope())
{
this.myUserService.CreateUser(...);
this.myCustomerService.CreateOrder(...);
transaction.Commit();
}
}
}
关于c# - Entity Framework 6 和工作单元……何时何地?是不是很像ado.net中的交易?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26055497/
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
如何在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
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
我很好奇.NET将如何影响Python和Ruby应用程序。用IronPython/IronRuby编写的应用程序是否会非常特定于.NET环境,以至于它们实际上将变得特定于平台?如果他们不使用任何.NET功能,那么IronPython/IronRuby相对于非.NET同类产品的优势是什么? 最佳答案 我不能说任何关于IronRuby的东西,但是大多数Python实现(如IronPython、Jython和PyPy)都试图尽可能忠实于CPython实现。不过,IronPython正在迅速成为这方面的佼佼者之一,并且在PlanetPyth
我几天前在我的rubyonrails2.3.2上安装了Sphinx和Thinking-Sphinx,基本搜索效果很好。这意味着,没有任何条件。现在,我想用一些条件过滤搜索。我有公告模型,索引如下所示:define_indexdoindexestitle,:as=>:title,:sortable=>trueindexesdescription,:as=>:description,:sortable=>trueend也许我错了,但我注意到只有当我将:sortable=>true语法添加到这些属性时,我才能将它们用作搜索条件。否则它找不到任何东西。现在,我还在使用acts_as_tag