AutoMapper与ASP.NET MVC结合,一般会在Controller的Action中使用AutoMapper把前台传递过来的Presentation Model转换成相应的业务逻辑上的Entity Model.如果在Action中写AutoMapper.CreateMap<T1,T2>,这些代码未免与业务逻辑无多大关系,所以不应该写在Action中。我们可以利用ASP.NET MVC中的Filter这个特点,以过滤器的方式在执行Action中AutoMapper的转换前就完成配置。
public class OrderDomainMvcProfile : Profile
{
public override string ProfileName
{
get { return "OrderDomainMvcProfile"; }
}
protected override void Configure()
{
// Map……
Mapper.CreateMap<T1, T2>().ForMember(……);
}
}
然后再Filter中进行加载
[AttributeUsageAttribute(AttributeTargets.Class |AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class AutoMapperConfigurationActionFilterAttribute : FilterAttribute
{
public AutoMapperConfigurationActionFilterAttribute(Type profileType)
{
Mapper.Configuration.AddProfileThreadSafe(Activator.CreateInstance(profileType) as Profile);
}
}
最后把Filter应用到Controller的Action
[AutoMapperConfigurationActionFilter(typeof(OrderDomainMvcProfile))]
public virtual ActionResult OrderGrid_Select(GridCommand command)
{ ……
}
这里介绍另外一种比较特别的方法。我希望在程序刚刚开始启动,即Application_Start的时候,就完成所有的配置,一劳永逸。如果ASP.NET MVC有多个Domain对应多个Area,那么在global.asax.cs中Application_Start中就要写很多配置AutoMapper的代码。这里我们使用反射,去寻找DLL中实现了IStartupTask的接口的方法,然后运行Excecute方法即可。
创建一个IStartupTask接口
public interface IStartupTask
{
void Execute();
}
实现这个接口
public class AutoMapperStartupTask : IStartupTask
{
public void Execute()
{
Create<User, UserModel>();
Create<Widget, WidgetModel>();
Create<WidgetDefinition, WidgetDefinitionModel>();
//Mapper.CreateMap<ModuleDefinition, ModuleDefinitionModel>()
// .ForMember(m => m.AppId, o => o.MapFrom(e => e.App == null ? (int?)null : e.App.Id))
// .ForMember(m => m.AppTitle, o => o.MapFrom(e => e.App == null ? null : e.App.Title));
//Mapper.CreateMap<ModuleDefinitionModel, ModuleDefinition>();
}
protected virtual void Create<T1, T2>()
{
Mapper.CreateMap<T1, T2>();
Mapper.CreateMap<T2, T1>();
}
}
在Global.ascx中一次性引入所有的AutoMapper配置
protected void Application_Start()
{
……
ExecuteStartupTasks();
}
private void ExecuteStartupTasks()
{
List<IStartupTask> startupTasks = new List<IStartupTask>();
Assembly asm = this.ExecutingAssembly;
// get path of executing (bin) folder from the current executing assembly
string codeBase = this.ExecutingAssembly.CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
string bin = Path.GetDirectoryName(path);
string[] assemblies = Directory.GetFiles(bin, "*.dll");
foreach (String file in assemblies)
{
try
{
if (File.Exists(file))
{
// load the assembly
asm = Assembly.LoadFrom(file);
// get all types from the assembly that inherit IStartupTask
var query = from t in asm.GetTypes()
where t.IsClass && t.GetInterface(typeof(IStartupTask).FullName) != null
select t;
// add types to list of startup tasks
foreach (Type type in query)
{
startupTasks.Add((IStartupTask)Activator.CreateInstance(type));
}
}
}
catch (Exception ex)
{
Exceptions.LogException(ex);
}
}
// execute each startup task
foreach (IStartupTask task in startupTasks)
{
task.Execute();
}
}