Bo *_*sen 7 c# asp.net-mvc entity-framework onion-architecture
我目前正在重构ASP.NET MVC项目以使用洋葱arcitecture,因为它似乎适合未来发展的需要.
我已经设置了我认为需要使用的图层,我的解决方案现在看起来像这样:
所以,基本上我已经理解,该ClientName.Core
项目根本不应该参考其他项目.本ClientName.Infrastructure
应具有的一个参考ClientName.Core
.Interfaces文件夹ClientName.Core
定义项目中的服务,ClientName.Infrastructure
我的DbContext和域实体是分开的,因此只有实体在核心项目中.
我把头靠在墙上的是,ClientName.Infrastructure
项目不应该将域实体返回到调用它的客户端.这将在核心项目和任何"违反"洋葱原则的UI之间创建引用.正如我所读到的,解决这个问题的方法是让基础设施服务返回DTO.但是,如果我PersonDto
从PersonService
类中返回ie PersonDto
,则ClientName.Core
项目需要知道该对象,因为这是接口所在的位置.
所以问题是:我在哪里放置DTO/ViewModel /用于UI /客户端的其他模型?我是否创建了一个单独的类库来保存这些模型,并让UI,基础结构和核心项目都引用它?
非常感谢任何帮助/提示,因为我对此有点困惑;-)
提前致谢.
编辑
根据Euphorics的回答,我在这里编写示例代码只是为了检查我是否正确,可能还有一些后续问题.
所以基本上,在我的ClientName.Core
层中,我有我的实体,其中包含业务逻辑,即a Person
和Firm
实体可能如下所示:
(Exists in ClientName.Core/Entities)
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Firm Firm { get; set; }
}
(Exists in ClientName.Core/Entities)
Public class Firm
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Person> Employees { get; set; }
public IEnumerable<Person> GetEmployeesByName(string name)
{
return Employees.Where(x => x.Name.Equals(name));
}
public void AddEmployee(Person employee)
{
Employees.Add(employee);
}
public void CreateFirm(Firm firm)
{
// what should happen here? Using entity framework, I have no reference to the DbContext here...
}
}
(Exists in ClientName.Infrastructure/Services)
public class FirmService : IFirmService
{
private readonly IDbContext _context;
public FirmService(IDbContext context)
{
_context = context;
}
public Firm GetById(int firmId)
{
return _context.Firms.Find(firmId);
}
// So basically, this should not call any domain business logic?
public void CreateFirm(CreateFirmFormViewModel formViewModel)
{
Firm firm = new Firm()
{
Name = formViewModel.Name;
}
_context.Firms.Add(firm);
_context.SaveChanges();
}
public IEnumerable<Person> GetEmployeesByName(int firmId, string name)
{
Firm firm = _context.Firms.Find(firmId);
return firm.GetEmployeesByName(name);
}
}
Run Code Online (Sandbox Code Playgroud)
我是否正确,每个读取查询应该直接在实体上定义(可能作为实体扩展,因为我使用实体框架),任何创建/更新/删除只会发生在基础设施层的服务中?
read(即IEnumerable<Person> GetEmployeesByName(int firmId, string name)
方法)方法是否也应该在FirmService
接口/类上?
再次感谢 :-)
首先,您不是Person
从返回PersonService
,而是从IPersonService
。这是巨大的概念差异。PersonService 只实现IPersonService
定义的内容。它不关心它使用什么接口或对象。
简而言之,Core 项目应该包括业务实体中的所有业务逻辑。如果您没有任何实际逻辑,那将只是简单的 DTO。然后,Infrastructure
将负责从数据库中保存/加载这些实体。如果它使用自己的实体创建自己的模型,或者它重用由 core is 定义的模型,那么它的Infrastructure
实现细节。同时,UI
(或Web)项目将与项目中的实体一起Core
工作,但它不会关心持久化是如何发生的。它只是想“做生意”。
这里的关键点是核心(或业务逻辑)是可自动测试的,无需涉及UI
或数据库代码。只要你能做到这一点,一些 DTO 在哪里并不重要。
编辑:
这开始变得困难了。您遇到了相互冲突的设计要求。一方面,您有明确的域设计。另一方面,您希望您的模型可以使用 EF 持久化。有一点很清楚:您将妥协并扭曲您的域模型,以便它可以在 EF 中使用。
现在是具体问题。第一个是创建公司。在这里,您在域所理解的“新公司”之间存在冲突。例如。确保新公司具有有效名称。另一方面,您希望创建实例并使 EF 知道它。我这样做的方法是CreateFirm
从Firm
类中删除并更改CreateFirm
,IFirmService
以便它只接受创建新公司所需的参数Firm
(例如,只是名称)并返回新创建的公司。此外,存储库不应调用域的想法是错误的。例如,存储库可能会调用域来验证它创建的公司是否有效,如果不是,则不要将其添加到 EF 中。
我看到的第二个问题是在公司中保留员工集合。虽然这对域来说很好,但对于持久性来说却是灾难(除非你认为在这种情况下延迟加载很好)。再次。没有简单的方法如何满足“公司拥有员工集合”的领域需求以及将实体持久化到数据库中的能力。第一个选择是不拥有此集合并将其移动到IFirmService
可以正确实现的地方。第三种选择是最极端的。那就是Firm
在使所有“get”成为抽象方法的同时创建一个抽象类,在基础设施项目中创建它的实现,并使用具体实例将具有的 EF 上下文来实现 get 方法。此外,这可能只有在具体实例Firm
是在基础设施中创建的,这是我上面建议的。
我个人喜欢第三个选项,因为它使方法保持良好和有凝聚力。但一些英孚纯粹主义者可能不同意我的观点。
归档时间: |
|
查看次数: |
2952 次 |
最近记录: |