ASP.NET 5/ASP.NET Core 1 中的关注点分离和 n 层架构
Posted
技术标签:
【中文标题】ASP.NET 5/ASP.NET Core 1 中的关注点分离和 n 层架构【英文标题】:Separation of concerns and n-tiered architecture in ASP.NET 5/ASP.NET Core 1 【发布时间】:2015-12-12 06:03:08 【问题描述】:由于我想使用新的内置依赖注入,我正在努力寻找一种优雅的方式将我的 DAL 层与 ASP.NET 5 中的 MVC/UI 层分开。
例如,我有一个 ASP.NET 5 项目、一个业务层项目和一个数据访问项目,其中我有各种实体框架代码,例如实体和上下文。在 ASP.NET 5 中设置上下文并以数据库为目标,主要文档建议我在 StartUp.cs 类中执行类似的操作
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<BookContext>(options =>
options.UseSqlServer(Configuration.Get("Data:ConnectionString"));
);
这意味着我现在必须在基本上是我的 UI 层的地方引用我的 DAL,根据周围的各种专家和博客文章,多年来这一直是不好的做法。
我解决此问题的一种方法是创建两个新项目,一个是 CompositeRoot 项目,其中包含工厂类以生成我的业务类,然后访问 DAL 以及一个带有配置类的实用程序项目,其中有一个 @ 987654324@ 属性,我可以将其传递到我的上下文中,然后我使用内置的 DI 将所有内容连接起来并避免在我的 UI 层中引用我的 DAL。但是我遇到了最新版本的实体框架(beta 7)的问题,因为现在似乎无法在上下文的构造函数或可覆盖的OnConfiguration
方法中指定连接字符串。另外,到目前为止,所有文档似乎根本不关心这种混合问题。这就是我们现在做事的方式吗?相信开发人员不会做“坏”的事情,比如直接在 UI 中引用 DAL 类?还是人们正在采用一种模式来通过这种新的内置 DI/ASP.NET 5 配置来保持稳定?
【问题讨论】:
“这意味着我现在必须在基本上是我的 UI 层的地方引用我的 DAL”。这是不正确的。这段代码不在你的 UI 层;这是您的组合根层。您将层(逻辑工件)误认为程序集(部署工件),并且您隐式选择在一个程序集中包含两个层。如需更深入的解释,请阅读此答案:***.com/a/9505530/264697。 JK,是的,我认为这是可以接受的,而且不是太宽泛,因为我指的是特定框架的特定问题。谢谢史蒂文,我会调查你的帖子。我认为我的主要问题是 Microsoft 已强制他们的项目模板包含所有 MVC 项(控制器/视图)以及 StartUp 类中的组合,这意味着如果您的 EF 代码,您必须对 DAL 进行项目级别的引用包含在那里,您没有单独的组合根。 为什么说不能分开呢?为什么你不能创建一个名为 Database 的单独项目。然后 BookContext 只是对该项目的引用。最后,options 参数覆盖了你设置的连接字符串。 以防万一您不知道如何设置您的控制台应用程序:docs.asp.net/en/latest/dnx/console.html#creating-a-console-app 【参考方案1】:如果您请 10 位土木建筑师为您建造一座桥梁,您最终将拥有 10 种不同的架构。没有一个会是一样的,也没有一个会比另一个更好。
无论他们应用了多少最佳实践和设计模式,每个架构师都会证明他们的想法是正确的。有些人会过分热心,而另一些人会保持简单并完成工作。除其他外,预算、交付日期和工艺将直接影响您决定的架构类型。
同样的规则也适用于软件架构师。
我看到我相当一部分的架构师有着世界上最好的意图,只是意识到 UI 层依赖于 DAL。也许这背后的原因是:
他们没想到 他们并不真正关心它 它有利于 DI,因为你可以看到每一层,这反过来又使它 易于将接口映射到它们的实现。回到 MVC 5,我有以下几层:
-Contoso.Core (Class Library)
-Contoso.Data (Class Library)
-Contoso.Service (Class Library)
-Contoso.Web (asp.net MVC 5)
-Contoso.Web.Configuration (Class Library)
Web.Configuration
层依赖于Core
、Data
和Service
。这一层是我配置 DI 的地方。
Web
层不依赖于Data
层,为了开始工作,我使用的是Bootstrapper Nuget Package。
也许您可以使用 ASP.NET 5 实现类似的目标
我希望 Microsoft(或任何人)使用解耦方法甚至像 Onion 架构示例一样创建更适合企业级示例的工作项目模板。
最后,我认为合理的解耦架构可能对某些人来说太过分了,而对另一些人来说却不够......
请随时分享您的发现,因为我很想知道您是如何做到的。
【讨论】:
【参考方案2】:这真的是个大问题吗?
您的 UI 层将依赖于您的数据层,并且在某处会有一些参考。
StartUp.cs 添加了各种服务和配置,所以这些引用大部分都在一个地方。
【讨论】:
【参考方案3】:您可以使用Service Locator 来解决这个问题。
例如,您的 root/core/abstractions 项目中有一个接口:
public interface IServiceConfiguration
void ConfigureServices(IServiceCollection services, IConfigurationRoot configuration);
在您的 StartUp.cs 中,找到所有实现 IServiceConfiguration 的类型并使用它来注册您的外部服务。
public void ConfigureServices(IServiceCollection services)
var currentAssembly = typeof(Startup).Assembly;
// Limit to project assemblies
var @namespace = currentAssembly.FullName.Split(',')[0].Split('.')[0];
var assemblies = currentAssembly.GetReferencedAssemblies()
.Where(a => a.Name.StartsWith(@namespace, StringComparison.Ordinal))
.Select(a => Assembly.Load(a));
foreach (var assembly in assemblies)
// Assembly.ExportedTypes is not supported in dnxcore50 so you need to take it off your frameworks node in project.json.
foreach (var config in assembly.ExportedTypes.Where(t => typeof(IServiceConfiguration).IsAssignableFrom(t) && !t.IsAbstract))
((IServiceConfiguration)Activator.CreateInstance(config)).ConfigureServices(services, Configuration);
// More service registrations..
并且在您的 DAL 中,您可以添加一个将注册 EF 的类:
public sealed class EntityFrameworkServiceConfiguration : IServiceConfiguration
public void ConfigureServices(IServiceCollection services)
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<BookContext>(options =>
options.UseSqlServer(Configuration.Get("Data:ConnectionString"));
);
虽然不是每个人都需要这种类型的解耦。在某些方面,在 StartUp.cs 中注册所有内容更有意义。无论如何,您都会在 Web 项目中引用您的 DAL(除非一切都通过动态发现),这意味着它已经了解有关您的 DAL 的一些信息。
【讨论】:
【参考方案4】:我在上面发表了评论,但我认为很多人可能有同样的问题。
添加全新的 ClassLibraryPackage
编辑你的 project.json 文件
"version": "1.0.0-*",
"description": ":)",
"authors": [ "JJVC" ],
"tags": [ "" ],
"projectUrl": "JJVCblog.com",
"licenseUrl": "",
"frameworks":
"dnx451":
"frameworkAssemblies":
,
"dependencies":
"EntityFramework.Commands": "7.0.0-rc1-final"
,
"commands":
"ef": "EntityFramework.Commands"
,
"dependencies":
"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final",
"Microsoft.AspNet.Identity": "3.0.0-rc1-final",
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-final"
首先做你的代码
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole,string>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
optionsBuilder.UseSqlServer(@"server=localhost;Database=Whatever;Integrated Security=True;MultipleActiveResultSets=True;Connect Timeout=60;");
DbSet<ForgotPasswordResetHistory> ForgotPasswordResetHistory get; set;
protected override void OnModelCreating(ModelBuilder builder)
base.OnModelCreating(builder);
base.OnModelCreating(builder);
public class ForgotPasswordResetHistory
[Key]
public int ForgotPasswordResetHistoryId get; set;
public bool UserTriedToResetPassword get; set;
public DateTime LinkExpirationDate get; set;
public string SecretCodeForTheLink get; set;
public DateTime PasswordRecoveryRequestDate get; set;
public ApplicationUser ApplicationUser get; set;
public int Test get; set;
在文件夹的上下文中启动 cmd 提示符。 dnu 恢复 dnx ef 迁移 dnx ed 数据库更新
现在转到您的“网络”项目并引用您的数据库。在您的网络项目“project.json”中,您应该会看到它在依赖项下被调用。
"ConsoleApp2.Database": "1.0.0-*"
祝你好运!
【讨论】:
【参考方案5】:我今天在为 MVC 6 做演示时遇到了同样的问题。经过一些研究/反复试验,当我删除时
“EntityFramework.SqlServer”:“7.0.0-beta8”
来自 project.json 文件的一切对我来说都很好。
【讨论】:
【参考方案6】:我最近参加了 2016 年全球 Azure 训练营,我们从事的示例项目(纯粹从 Azure 的角度来看)很好地展示了这些关注点的分离。我没有花很多时间剖析这个项目,但是如果有人想看看这是否是一个合适的 N-Tier 拆分,请回帖。链接是http://opsgilitytraining.blob.core.windows.net/public/modern-cloud-apps-student.zip。分隔方式如下:
Contoso.Apps.SportsLeague.Web Contoso Sports League 电子商务应用程序 Contoso.Apps.SportsLeague.Admin Contoso Sports League 呼叫中心管理应用程序 Contoso.Apps.SportsLeague.WorkerRole 处理订单收据生成 Contoso.Apps.SportsLeague.Data 数据层 Contoso.Apps.SportsLeague.Offers API 用于返回可用产品列表 Contoso.Apps.PaymentGateway 用于支付处理的 API【讨论】:
【参考方案7】:之所以会有UI层对数据层的引用,是因为我们需要在IoC容器中注册DbContext(如果你使用EF的话)。
您可以使用 MEF(管理可扩展性框架)绕过此项目/包参考。检查 .NET Core 中 MEF 的实现,因为它与以前的 .NET Framework 有所不同。
在 MEF 中,您可以通过约定或配置来实现。
【讨论】:
【参考方案8】:您可以在业务层和数据访问层实现 IServiceCollection 的扩展方法。
然后在 Web Startup 中,您可以调用业务层上的扩展方法,进而调用数据层上的扩展方法
所以这段代码可以在数据层扩展中:
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<BookContext>(options =>
options.UseSqlServer(Configuration.Get("Data:ConnectionString"));
);
这会将事情分开,网络只需要使用启动文件中的语句来引用业务层
【讨论】:
我认为他试图表达的观点是 UI/Web 不应该知道或关心 EF。以上是关于ASP.NET 5/ASP.NET Core 1 中的关注点分离和 n 层架构的主要内容,如果未能解决你的问题,请参考以下文章
如何仅为 ASP.NET 5 (ASP.NET Core) 中的受保护操作添加令牌验证