介紹
github:https://github.com/QQ2287991080/AutofacSolution
webapi+autofac+autoMapper單元測試:https://www.cnblogs.com/aqgy12138/p/13283977.html
看到標題大伙可能會想,現在不都是學.net core了嗎。
我想說的是,學的人是不少,但是有多少是能夠在公司拍板技術方向的人呢,很多時候也是身不由己吧。
.net core webapi+autofac+autoMapper這種模式可以說百度上多如牛毛。
下面開始。。
首先創建一個.net webapi項目 framework的版本是4.72
這里我附帶創建了一個單元測試。后續用來做測試。
🆗,項目建好之后把沒用的東西先刪除去。
然后我們需要安裝nuget包,盡量都用最新的吧。
主要是三個包Autofac、Autofac.WebApi2、AutoMapper
包裝好后創建一下做測試的類。
不要忘記讓Concrete繼承Interfacs中對應的接口哦。
然后在ITest系列類中加一個抽象方法,再到Test中去實現。
好的,我們的測試類就這樣搞定了。
在App_Start文件夾下創建一個類,用於實現autofac。並且把我們的測試類都放到容器中。
public class AutofacRegister { public static void Register() { //初始化容器 var builder = new ContainerBuilder(); /* * 把我們的測試類放到容器中 */ builder.RegisterType<TestA>().As<ITestA>().AsImplementedInterfaces(); builder.RegisterType<TestB>().As<ITestB>().AsImplementedInterfaces(); builder.RegisterType<TestC>().As<ITestC>().AsImplementedInterfaces(); //獲取global配置 var configuration = GlobalConfiguration.Configuration; //注冊控制器 builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); //創建容器 var container = builder.Build(); //將DI解析程序設置為autofac configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); } }
按照管理,類似這種更改了web初始配置的都需要到Global.asax配置一下。
public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() {
AutofacRegister.Register(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); } }
在Global里加上AutofacRegister.Register(); 這里可能有人問我為什么只有三行代碼,因為最開始將項目的時候把沒用的都刪除,相應一些配置在這里也需要去掉。
建一個控制器然后寫上我們需要測試的東西。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.Http; using WEBAPI.Autofac.AutoMapper.Demo.Interfacs; namespace WEBAPI.Autofac.AutoMapper.Demo.Controllers { public class TestController:ApiController { readonly ITestA _testA; readonly ITestB _testB; readonly ITestC _testC; public TestController( ITestA testA, ITestB testB, ITestC testC ) { _testA = testA; _testB = testB; _testC = testC; } [HttpGet] public IHttpActionResult TestA() { return this.Json(_testA.Get()); } [HttpGet] public IHttpActionResult TestB() { return this.Json(_testB.Get()); } [HttpGet] public IHttpActionResult TestC() { return this.Json(_testC.Get()); } } }
寫好之后就用Postman來測試一下吧。
咳咳,出了個錯誤,然我來瞅瞅啥錯誤。看樣子好像是路由有問題。
不慌。
給控制上加個[RoutePrefix("api/test")]特性,然后方法上加個Route試試先。
成了!!!
控制的完整代碼貼一下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.Http; using WEBAPI.Autofac.AutoMapper.Demo.Interfacs; namespace WEBAPI.Autofac.AutoMapper.Demo.Controllers { [RoutePrefix("api/test")] public class TestController:ApiController { readonly ITestA _testA; readonly ITestB _testB; readonly ITestC _testC; public TestController( ITestA testA, ITestB testB, ITestC testC ) { _testA = testA; _testB = testB; _testC = testC; } [HttpGet] [Route("TestA")] public IHttpActionResult TestA() { return this.Json(_testA.Get()); } [HttpGet] [Route("TestB")] public IHttpActionResult TestB() { return this.Json(_testB.Get()); } [HttpGet] [Route("TestC")] public IHttpActionResult TestC() { return this.Json(_testC.Get()); } } }
到目前位置所做的其實是autofac做依賴注入的基操,開文所將的automapper咋還沒有用上。
這次主要想講的是automapper的兩種實現。
先來做做准備工作
創建文件夾Profiles
創建抽象接口IProfile,這個后面會講到
/// <summary> /// 需要注冊automapper都實現該接口 /// </summary> public interface IProfile { }
然后我再弄兩個測試用的類,這個放在Models文件夾下的。
/// <summary> /// 這里便於測試我就隨便弄弄 /// </summary> public class TestModel { public int Age { get; set; } public string Name { get; set; } } public class TestModelDto { public int Age { get; set; } public string Name { get; set; } }
創建關系映射類
public class TestProfile:Profile,IProfile { public TestProfile() { CreateMap<TestModel, TestModelDto>(); } }
下面說的第一種做法,這個是官網的例子我就直接復制過來了。
using Autofac; using AutoMapper; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using Module = Autofac.Module; namespace WEBAPI.Autofac.AutoMapper.Demo.Profiles { public class AutoMapperRegister:Module { private readonly IEnumerable<Assembly> assembliesToScan; public AutoMapperRegister(IEnumerable<Assembly> assembliesToScan) => this.assembliesToScan = assembliesToScan; public AutoMapperRegister(params Assembly[] assembliesToScan) : this((IEnumerable<Assembly>)assembliesToScan) { } protected override void Load(ContainerBuilder builder) { base.Load(builder); var assembliesToScan = this.assembliesToScan as Assembly[] ?? this.assembliesToScan.ToArray(); var allTypes = assembliesToScan .Where(a => !a.IsDynamic && a.GetName().Name != nameof(AutoMapper)) .Distinct() // avoid AutoMapper.DuplicateTypeMapConfigurationException .SelectMany(a => a.DefinedTypes) .Where(w => w.IsAssignableFrom(typeof(IProfile)))//默認繼承IProfile,排除不需要configuration的實例 .ToArray(); var openTypes = new[] { typeof(IValueResolver<,,>), typeof(IMemberValueResolver<,,,>), typeof(ITypeConverter<,>), typeof(IValueConverter<,>), typeof(IMappingAction<,>) }; foreach (var type in openTypes.SelectMany(openType => allTypes.Where(t => t.IsClass && !t.IsAbstract && ImplementsGenericInterface(t.AsType(), openType)))) { builder.RegisterType(type.AsType()).InstancePerDependency(); } //configuration配置 builder.Register<IConfigurationProvider>(ctx => new MapperConfiguration(cfg => { cfg.AddMaps(assembliesToScan); cfg.AllowNullCollections = true;//允許空集合 }) ); builder.Register<IMapper>(ctx => new Mapper(ctx.Resolve<IConfigurationProvider>(), ctx.Resolve)).InstancePerDependency(); } private static bool ImplementsGenericInterface(Type type, Type interfaceType) => IsGenericType(type, interfaceType) || type.GetTypeInfo().ImplementedInterfaces.Any(@interface => IsGenericType(@interface, interfaceType)); private static bool IsGenericType(Type type, Type genericType) => type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == genericType; } }
這種做法呢,其實就是遍歷程序集,把程序集中注冊過映射關系的類型都Configuration掉,另外我等下要說的第二種做法來說就是直接用在MapperConfiguration注冊映射類。
上面寫好之后 需要在AutofacRegister中加入一句話,放在builder.RegisterType<TestC>().As<ITestC>().AsImplementedInterfaces();后面就好了。
builder.RegisterModule(new AutoMapperRegister(Assembly.Load("WEBAPI.Autofac.AutoMapper.Demo")));
接下測試
using AutoMapper; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.Http; using WEBAPI.Autofac.AutoMapper.Demo.Models; namespace WEBAPI.Autofac.AutoMapper.Demo.Controllers { [RoutePrefix("api/Mapper")] public class MapperController:ApiController { readonly IMapper _mapper; public MapperController(IMapper mapper) { _mapper = mapper; } [HttpGet] [Route("Get")] public IHttpActionResult Get() { //初始化一些數據 var test = new TestModel() { Age = 1, Name = "zero" }; var dto= _mapper.Map<TestModelDto>(test); return this.Json(dto); } } }
康康效果
映射成功。
接下來是第二種方法,第二種其實是我在研究這個的過程中的發現的一種方式,其實也沒啥把,理解了就都知道了。
為了省時間我就直接貼代碼了。
using Autofac; using Autofac.Integration.WebApi; using AutoMapper; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Web.Compilation; using System.Web.Http; using WEBAPI.Autofac.AutoMapper.Demo.Concreate; using WEBAPI.Autofac.AutoMapper.Demo.Interfacs; using WEBAPI.Autofac.AutoMapper.Demo.Models; using WEBAPI.Autofac.AutoMapper.Demo.Profiles; namespace WEBAPI.Autofac.AutoMapper.Demo.App_Start { public class AutofacRegister { public static void Register() { //初始化容器 var builder = new ContainerBuilder(); /* * 把我們的測試類放到容器中 */ builder.RegisterType<TestA>().As<ITestA>().AsImplementedInterfaces(); builder.RegisterType<TestB>().As<ITestB>().AsImplementedInterfaces(); builder.RegisterType<TestC>().As<ITestC>().AsImplementedInterfaces(); //automapper配置 //builder.RegisterModule(new AutoMapperRegister(Assembly.Load("WEBAPI.Autofac.AutoMapper.Demo"))); builder.Register<IMapper>(r => { var mapperConfiguration = new MapperConfiguration(c => { c.AddProfile(new TestProfile());//剛剛我們注冊的Profile類。 }); mapperConfiguration.AssertConfigurationIsValid(); return new Mapper(mapperConfiguration); }); //獲取global配置 var configuration = GlobalConfiguration.Configuration; //注冊控制器 builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); //創建容器 var container = builder.Build(); //將DI解析程序設置為autofac configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); } } }
測試一下,避免翻車。
這兩種方式其實明眼就能看出差別,第一種更加適合項目,第二種更傾向於做測試,主要我每次寫好Profile類之后我還需要去注冊一下,其實是不夠方便的。