首页 / C# / c# – 我应该将存储库接口与域模型分离
c# – 我应该将存储库接口与域模型分离
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c# – 我应该将存储库接口与域模型分离,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3702字,纯文字阅读大概需要6分钟。
内容图文
![c# – 我应该将存储库接口与域模型分离](/upload/InfoBanner/zyjiaocheng/781/be56cc93e4284e3d88efca0efae537c2.jpg)
假设我有一些需要一些IEnumerable< Foo>的DDD服务.执行一些计算.我想出了两个设计:
>使用IFooRepository接口抽象数据访问,这是非常典型的
public class FooService
{
private readonly IFooRepository _fooRepository;
public FooService(IFooRepository fooRepository)
=> _fooRepository = fooRepository;
public int Calculate()
{
var fooModels = _fooRepository.GetAll();
return fooModels.Sum(f => f.Bar);
}
}
>不要依赖IFooRepository抽象并注入IEnumerable< Foo>直
public class FooService
{
private readonly IEnumerable<Foo> _foos;
public FooService(IEnumerable<Foo> foos)
=> _foos = foos;
public int Calculate()
=> _foos.Sum(f => f.Bar);
}
在我看来,第二种设计似乎更好,因为FooService现在不关心数据来自何处,而Calculate变成纯域逻辑(忽略IEnumerable可能来自不纯的源的事实).
使用第二种设计的另一个理由是,当IFooRepository通过网络执行异步IO时,通常需要使用async-await,如:
public class AsyncDbFooRepository : IFooRepository
{
public async Task<IEnumerable<Foo>> GetAll()
{
// Asynchronously fetch results from database
}
}
但是当您需要async all the way down时,FooService现在被迫将其签名更改为异步任务< int>计算().这似乎违反了dependency inversion principle.
但是,第二种设计也存在问题.首先,您必须依赖DI容器(此处使用Simple Injector作为示例)或组合根来解析数据访问代码,如:
public class CompositionRoot
{
public void ComposeDependencies()
{
container.Register<IFooRepository, AsyncDbFooRepository>(Lifestyle.Scoped);
// Not sure if the syntax is right, but it demonstrates the concept
container.Register<FooService>(async () => new FooService(await GetFoos(container)));
}
private async Task<IEnumerable<Foo>> GetFoos(Container container)
{
var fooRepository = container.GetInstance<IFooRepository>();
return await fooRepository.GetAll();
}
}
同样在我的特定场景中,AsyncDbFooRepository需要构造某种运行时参数,这意味着您需要一个abstract factory来构造AsyncDbFooRepository.
使用抽象工厂,现在我必须在AsyncDbFooRepository下管理所有依赖项的生命周期(AsyncDbFooRepository下的对象图并不简单).如果我选择第二种设计,我有一种预感,我正在使用DI错误.
总之,我的问题是:
>我在第二次设计中错误地使用了DI吗?
>如何为我的第二个设计令人满意地编写我的依赖项?
解决方法:
async / await的一个方面是它根据定义需要在你正确陈述时“一直向下”应用.但是,您无法阻止使用任务< T>注入IEnumerable< T>时,如您在第二个选项中所建议的那样.您将必须注入任务< IEnumerable< T>>进入构造函数以确保异步检索数据.当注入IEnumerable< T>时它或者意味着在枚举集合时线程被阻塞 – 或者 – 在对象图构造期间必须加载所有数据.
然而,在对象图构造期间加载数据是有问题的,因为我解释了here的原因.除此之外,由于我们在这里处理数据集合,这意味着必须在每个请求中从数据库中提取所有数据,即使不是可能需要或甚至使用所有数据.这可能会导致性能损失.
Am I using DI incorrectly in my second design?
这很难说. IEnumerable< T>是一个流,所以你可以认为它是一个工厂,这意味着注入一个IEnumerable< T>在对象构造期间不需要加载运行时数据.只要满足该条件,就注入IEnumerable< T>.可能没问题,但仍然无法使系统异步.
然而,当注入IEnumerable< T>时你最终可能会有歧义,因为注入IEnumerable< T>可能并不十分清楚.该集合是否是一个懒惰评估的流?它是否包含T的所有元素.是T运行时数据还是服务?
为了防止这种混淆,将这个运行时信息加载到抽象之后通常是最好的事情.为了使您的生活更轻松,您还可以使存储库抽象通用:
public interface IRepository<T> where T : Entity
{
Task<IEnumerable<T>> GetAll();
}
这允许您拥有一个通用实现,并为系统中的所有实体进行一次注册.
How can I compose my dependencies satisfactorily for my second design?
你不能.为此,您的DI容器必须能够异步解析对象图.例如,它需要以下API:
Task<T> GetInstanceAsync<T>()
但Simple Injection没有这样的API,也没有任何其他现有的DI Container,这是有充分理由的.原因是对象构造必须是simple,快速和reliable,并且在对象图构造期间进行I / O时会丢失.
因此,不仅您的第二个设计不合需要,在对象构造期间加载数据时也不可能这样做,而不会破坏系统的异步性并导致线程在使用DI容器时阻塞.
内容总结
以上是互联网集市为您收集整理的c# – 我应该将存储库接口与域模型分离全部内容,希望文章能够帮你解决c# – 我应该将存储库接口与域模型分离所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。