“Blog.Core-master”示例程序中接口及其具体实现类的注入操作,是通过第3方依赖注入中间件“Autofac”来以反射方式把Service.dll 程序集中所有接口及其具体实现类的实例依赖注入内置容器中。.Net(Core).x框架是中的内置依赖注入容器是不支持程序集注入的。
从最佳实践角度来说,.Net(Core).x框架中的内置容器的功能不如第3方依赖注入中间件“Autofac”强大,但是除非十分必要,不要在基于.Net(Core).x框架的程序中集成 “Autofac”,这虽然使依赖注入的定义相对比较麻烦和出现一些重复性代代码,但简化了整个程序的实现,提升了程序的健壮性和容错性。
011 MessageModel、MessageModel<T>、BlogViewModels、HtmlHelper、CachingAttribute、IBlogArticleServices、BlogArticleServices、CustomProfile、AutoMapperConfig、AutoMapperSetup、AutofacModuleRegister
MessageModel:把从指定“Api”控制器行方法中获取(指定实体/模型类的所有实例)执行结果及其状态数据信息存储到该类中的属性员中,为客户端页面的渲染提供数据支撑。
MessageModel<T>:该类以泛型定义形式,把从指定“Api”控制器行方法中获取(指定实体/模型类的所有实例)执行结果及其状态数据信息存储到该类中的属性员中,为客户端页面的渲染提供数据支撑。
说明:
该类的泛型定义中包含同名的属性成员与方法成员,所以小写了属性成员的第1个字母。
BlogViewModels:通过该实体类及其属性成员实例,直接为当前程序与前端页面进行数据交互操作及其前端页面的渲染显示提供数据支撑。
HtmlHelper:该类中的方法成员把富文本控件中所显示内容中所包含的“HTML”标签去除后,返回不包含有“HTML”标签的字符串。
CachingAttribute:如果1个方法中的数据被缓存到的分布式数据库中,则在该方法中标记上该特性,就会为所缓存的数据直接设定绝对过期时间,默认值:30(分钟)。
IBlogArticleServices:通过继承于该接口的具体实现类中的方法成员,以异步方式实现了当前程序与“[Blog].[BlogArticle]”表的CURD交互操作。
BlogArticleServices:继承了IBlogArticleServices接口,通过类中的方法成员,以异步方式实现了当前程序与“[Blog].[BlogArticle]”表的CURD交互操作。
using AutoMapper;
using Common.Filter;
using IServices;
using Model.Models;
using Model.ViewModels;
using Services.Base;
namespace Blog.Core.Services
{
/// <summary>
/// 【博客文章服务--类】
/// <remarks>
/// 摘要:
/// 通过类中的方法成员,以异步方式实现了当前程序与“[Blog].[BlogArticle]”表的CURD交互操作。
/// </remarks>
/// </summary>
public class BlogArticleServices : BaseServices<BlogArticle>, IBlogArticleServices
{
#region 拷贝构造方法与变量
IMapper _mapper;
public BlogArticleServices(IMapper mapper)
{
this._mapper = mapper;
}
#endregion
#region 方法
/// <param name="id">1个指定的整型编号值。</param>
/// <summary>
/// 【通过编号获取博客文章实例】
/// <remarks>
/// 摘要:
/// 通过1个指定的整型编号值,获取博客视图类的1个指定实例(由“AutoMapper”中间件映射赋值得到)。
/// </remarks>
/// <returns>
/// 返回:
/// 博客视图类的1个指定实例(由于“AutoMapper”中间件映射赋值得到)。
/// </returns>
/// </summary>
public async Task<BlogViewModels> GetBlogDetails(int id)
{
// 此处想获取上一条下一条数据,因此将全部数据list出来,有好的想法请提出
//var bloglist = await base.Query(a => a.IsDeleted==false, a => a.bID);
var blogArticle = (await base.Query(a => a.Id == id && a.Category == "技术博文")).FirstOrDefault();
BlogViewModels models = null;
if (blogArticle != null)
{
models = _mapper.Map<BlogViewModels>(blogArticle);
//要取下一篇和上一篇,以当前id开始,按id排序后top(2),而不用取出所有记录
//这样在记录很多的时候也不会有多大影响
var nextBlogs = await base.Query(a => a.Id >= id && a.IsDeleted == false && a.Category == "技术博文", 2, "bID");
if (nextBlogs.Count == 2)
{
models.Next = nextBlogs[1].Title;
models.NextId = nextBlogs[1].Id;
}
var prevBlogs = await base.Query(a => a.Id <= id && a.IsDeleted == false && a.Category == "技术博文", 2, "bID desc");
if (prevBlogs.Count == 2)
{
models.Previous = prevBlogs[1].Title;
models.PreviousId = prevBlogs[1].Id;
}
blogArticle.Traffic += 1;
await base.Update(blogArticle, new List<string> { "Traffic" });
}
return models;
}
/// <summary>
/// 【获取所有博客文章实例】
/// <remarks>
/// 摘要:
/// 获取“[Blog].[BlogArticle]”表中的所有实例,并缓存到分页式缓存数据库中,并设定其设对过期时间为:10(分钟)。
/// </remarks>
/// <returns>
/// 返回:
/// 列表实例,该实例存储着博客文章实体的所有的实例。
/// </returns>
/// </summary>
[Caching(AbsoluteExpiration = 10)]
public async Task<List<BlogArticle>> GetBlogs()
{
var bloglist = await base.Query(a => a.Id > 0, a => a.Id);
return bloglist;
}
#endregion
}
}
CustomProfile:在实体实例与视图实例进行数据相互赋值操作时,将根据当前类所定义的赋值映射规则,通过“AutoMapper”中间件,实现实体实例与视图实例相互赋值操作。
AutoMapperConfig:该类中的方法成员实例化自定义的赋值映射规则,返回该自定义赋值映射规则实例。
AutoMapperSetup:该类中的方法成员用于把自定义赋值映射规则实例,注入到内置容器中。
using Extensions.AutoMapper;
using Microsoft.Extensions.DependencyInjection;
namespace Extensions.ServiceExtensions
{
/// <summary>
/// 【赋值映射规则注入--类】
/// </summary>
/// <remarks>
/// 摘要:
/// 该类中的方法成员用于把自定义赋值映射规则实例,注入到内置容器中。
/// </remarks>
public static class AutoMapperSetup
{
///<param name="services">.Net(Core)框架内置依赖注入容器实例。</param>
/// <summary>
/// 【添加赋值映射规则注入】
/// <remarks>
/// 摘要:
/// 该方法用于把自定义赋值映射规则实例,注入到内置容器中。
/// </remarks>
/// </summary>
public static void AddAutoMapperSetup(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
//下面的2行语句很好的体现了依赖注入中的依赖倒置关系。
//AddAutoMapper:依赖于Nuget--Model--AutoMapper.Extensions.Microsoft.DependencyInjection。
//把自定义赋值映射规则实例,注入到内置容器中。
services.AddAutoMapper(typeof(AutoMapperConfig));
//获取自定义赋值映射规则实例。
AutoMapperConfig.RegisterMappings();
}
}
}
AutofacModuleRegister:通过该类中的方法成员,通过第3方依赖注入中间件把当前程序中所有接口及其具体实现类的实例依赖注入到“Autofac”容器中。
//Nuget--Autofac.Extensions.DependencyInjection
using Autofac;
//Nuget--Autofac.Extras.DynamicProxy
using Autofac.Extras.DynamicProxy;
//using AOP;
using Common.Helper;
using Repository.Base;
using IServices.Base;
using Services.Base;
using Model;
using log4net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Repository.UnitOfWorks;
namespace Extensions.ServiceExtensions
{
/// <summary>
/// 【注册“Autofac”依赖注入--类】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过该类中的方法成员,通过第3方依赖注入中间件把当前程序中所有接口及其具体实现类的实例依赖注入到“Autofac”容器中。
/// </remarks>
public class AutofacModuleRegister : Autofac.Module
{
private static readonly ILog log = LogManager.GetLogger(typeof(AutofacModuleRegister));
///<param name="builder">第3方依赖注入中间件“Autofac”依赖注入容器实例。</param>
/// <summary>
/// 【依赖注入】
/// <remarks>
/// 摘要:
/// 该方法通过第3方依赖注入中间件“Autofac”来以反射方式把Service.dll 程序集中所有接口及其具体实现类的实例依赖注入到“Autofac”容器中。
/// </remarks>
/// </summary>
protected override void Load(ContainerBuilder builder)
{
var basePath = AppContext.BaseDirectory;
#region 带有接口层的服务注入
var servicesDllFile = Path.Combine(basePath, "Services.dll");
var repositoryDllFile = Path.Combine(basePath, "Repository.dll");
if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile)))
{
var msg = "Repository.dll和service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。";
log.Error(msg);
throw new Exception(msg);
}
//用于存储通过第3方依赖注入中间件“Autofac”注入的所有的接口及其具体实现类的类型实例。
var cacheType = new List<Type>();
// AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。
//if (AppSettings.app(new string[] {"AppSettings", "RedisCachingAOP", "Enabled"}).ObjToBool())
//{
// builder.RegisterType<BlogRedisCacheAOP>();
// cacheType.Add(typeof(BlogRedisCacheAOP));
//}
//if (AppSettings.app(new string[] {"AppSettings", "MemoryCachingAOP", "Enabled"}).ObjToBool())
//{
// builder.RegisterType<BlogCacheAOP>();
// cacheType.Add(typeof(BlogCacheAOP));
//}
//if (AppSettings.app(new string[] {"AppSettings", "TranAOP", "Enabled"}).ObjToBool())
//{
// builder.RegisterType<BlogTranAOP>();
// cacheType.Add(typeof(BlogTranAOP));
//}
//if (AppSettings.app(new string[] {"AppSettings", "LogAOP", "Enabled"}).ObjToBool())
//{
// builder.RegisterType<BlogLogAOP>();
// cacheType.Add(typeof(BlogLogAOP));
//}
//“Autofac”中间件把泛型仓储接口及其具体实现类的实例依赖注入第3方“Autofac”容器中。
builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>)).InstancePerDependency();
//“Autofac”中间件把泛型服务接口及其具体实现类的实例依赖注入第3方“Autofac”容器中。
builder.RegisterGeneric(typeof(BaseServices<>)).As(typeof(IBaseServices<>)).InstancePerDependency();//注册服务
//“Autofac”中间件以反射方式把Service.dll 程序集中所有接口及其具体实现类的实例依赖注入第3方“Autofac”容器中。
var assemblysServices = Assembly.LoadFrom(servicesDllFile);
builder.RegisterAssemblyTypes(assemblysServices)
.AsImplementedInterfaces()
.InstancePerDependency()
.PropertiesAutowired()
.EnableInterfaceInterceptors() //引用Autofac.Extras.DynamicProxy;
.InterceptedBy(cacheType.ToArray()); //允许将拦截器服务的列表分配给注册。
//“Autofac”中间件以反射方式把Repository.dll 程序集中所有接口及其具体实现类的实例依赖注入第3方“Autofac”容器中。
var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
builder.RegisterAssemblyTypes(assemblysRepository)
.AsImplementedInterfaces()
.PropertiesAutowired()
.InstancePerDependency();
//“Autofac”中间件把工作单元管量器接口及其具体实现类的实例依赖注入第3方“Autofac”容器中。
builder.RegisterType<UnitOfWorkManage>().As<IUnitOfWorkManage>()
.AsImplementedInterfaces()
.InstancePerLifetimeScope()
.PropertiesAutowired();
#endregion
#region 没有接口层的服务层注入
//因为没有接口层,所以不能实现解耦,只能用 Load 方法。
//注意如果使用没有接口的服务,并想对其使用 AOP 拦截,就必须设置为虚方法
//var assemblysServicesNoInterfaces = Assembly.Load("Blog.Core.Services");
//builder.RegisterAssemblyTypes(assemblysServicesNoInterfaces);
#endregion
#region 没有接口的单独类,启用class代理拦截
//只能注入该类中的虚方法,且必须是public
//这里仅仅是一个单独类无接口测试,不用过多追问
//builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love)))
// .EnableClassInterceptors()
// .InterceptedBy(cacheType.ToArray());
#endregion
#region 单独注册一个含有接口的类,启用interface代理拦截
//不用虚方法
//builder.RegisterType<AopService>().As<IAopService>()
// .AsImplementedInterfaces()
// .EnableInterfaceInterceptors()
// .InterceptedBy(typeof(BlogCacheAOP));
#endregion
}
}
}
1 重构Program类
var builder = WebApplication.CreateBuilder(args);
// 把“第3方“Autofac”例依赖注入”中间件实例及其所实例化的接口及具体实现类,依赖注入到.Net(Core)7框架内置容器中。
builder.Host
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule(new AutofacModuleRegister());
//builder.RegisterModule<AutofacPropertityModuleReg>();
});
//通过设定的持久化过滤规则及其配置文件(Log4net.config)中的数据实例,实例化第3方“log4net”日志中间件,最后把第3方“log4net”日志中间件依赖注入到内置容器中。
//注意:原示例代码是把把第3方“log4net”日志中间件依赖注入到内置依赖注入第3方“Autofac”容器中。
builder.Services.AddLogging(loging => {
//第3方“log4net”日志中间件持久化过滤规则:在持久化时,过滤掉带有"System"命名空间的日志信息实例。
loging.AddFilter("System", LogLevel.Error);
//第3方“log4net”日志中间件持久化过滤规则:在持久化时,过滤掉带"Microsoft"命名空间的日志信息实例。
loging.AddFilter("Microsoft", LogLevel.Error);
loging.SetMinimumLevel(LogLevel.Error);
loging.AddLog4Net("Log4net.config");
});
//把“AppSettings”实例,依赖注入到.Net(Core)7框架内置容器中。
builder.Services.AddSingleton(new AppSettings(builder.Configuration));
//把“内存缓存”中间件实例,依赖注入到.Net(Core)7框架内置容器中。
builder.Services.AddMemoryCacheSetup();
//把“SqlSugar”中间件实例,依赖注入到.Net(Core)7框架内置容器中。
builder.Services.AddSqlsugarSetup();
//把数据库交互上下文实例,依赖注入到.Net(Core)7框架内置容器中。
builder.Services.AddScoped<MyContext>();
//把带有自定义赋值操作映射规则的“AutoMapper”中间件实例,依赖注入到.Net(Core)7框架内置容器中。
builder.Services.AddAutoMapperSetup();
builder.Services.AddControllers();
按F5执行程序如下图所示:
对以上功能更为具体实现和注释见:22125_06Blog(集成第3方依赖注入中间件“Autofac”)。