如果这个问题已经在其他地方得到回答,我深表歉意;但我在这件事上发现了很多好坏参半的结果。
我正在使用:
- .net 框架 4.6.1
- Microsoft.AspNet.Mvc 5.2.6
- 自动映射器 6.2.2
- 实体框架 6.2.0
我对 ASP.net 和C#都很陌生,最近我成为了AutoMapper包的粉丝。我主要使用它来转换我从ApplicationDbContext
获得的实体到我的DTO
(数据传输对象)或ViewModel
。
我现在在我的应用程序中使用此设置来初始化和使用Controller
中的Mapper
:
全球.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
Mapper.Initialize(AutoMapperConfiguration.Configure);
// Other configuration for MVC application...
}
}
自动映射程序配置.cs
public static class AutoMapperConfiguration
{
public static void Configure(IMapperConfigurationExpression config)
{
config.CreateMap<Post, Post.DetailsViewModel>().ForMember(post => post.CanEdit, cfg => cfg.ResolveUsing((src, dst, arg3, context) => context.Options.Items["UserId"]?.ToString() == src.UserId));
}
}
PostsController.cs and Post.cs
public class PostsController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Post post = db.Posts.Find(id);
if (post == null)
{
return HttpNotFound();
}
return View(Mapper.Map<Post.DetailsViewModel>(post, options => options.Items["UserId"] = User.Identity?.GetUserId()));
}
}
// Post.cs
public class Post
{
public int Id { get; set; }
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedAt { get; set; } = DateTime.Now;
public class DetailsViewModel
{
public int Id { get; set; }
public string UserId { get; set; }
public ApplicationUser User { get; set; }
public string Title { get; set; }
public string Content { get; set; }
/// <summary>
/// A value indicating whether the current logged in user can edit the model
/// </summary>
public bool CanEdit { get; set; }
public DateTime PostedAt { get; set; }
}
}
代码摘要
我正在一个静态类(AutoMapperConfiguration
)中配置我的Mapper
,该类包含一个Config
方法,该方法从Global.asax.cs
文件中调用并映射所需的类。
然后,在我的Controller
中,我使用静态方法Mapper.Map
将我的Post
映射到它的DetailsViewModel
。
问题
自动映射器的这种使用(通过静态方法Mapper.Map
)如何影响性能,有没有更好的方法来做到这一点?
一些澄清:
例如:如果我每秒收到 100 个关于不同控制器操作的请求,该怎么办?据我所知,每个请求都有一个单独的线程,但会访问Mapper.Map
方法的相同内存(如果我是对的)。据我所知,这意味着性能将受到严重影响。
我已经看过一个问题,但从以下方面得到了不同的结果:
非静态自动映射器和 ASP.NET MVC -> 在哪里放置 AutoMapper.CreateMaps?
如果我错了,请纠正我。
AutoMapper(通过静态方法Mapper.Map)的这种使用如何影响性能,有没有更好的方法可以做到这一点?
您对自动映射器的使用是正确的。在应用程序启动时初始化是要遵循的最佳做法。通过调用Mapper.Initialize
,您正在初始化一个映射器,该映射器是IMapper
接口的静态实例(可通过静态属性访问Mapper.Instance
)。使用类中的静态成员时Mapper
您正在处理Mapper.Instance
并且所有对象都处于同一AppDomain
中。只要你使用相同的AppDomain
并且不在你的映射配置中做一些耗时的事情(有人可能会把一些业务逻辑或耗时的逻辑放入AfterMap
、BeforeMap
、ResolveUsing
、MapFrom
等中,你就不会影响性能。
如果我每秒收到 100 个关于不同控制器操作的请求怎么办? 据我所知,每个请求都有一个单独的线程,但会访问 Mapper.Map 方法的相同内存(如果我是正确的)。 据我所知,这意味着性能将受到严重影响。
您的应用程序使用AppDomain
的一个实例,每个请求将获得一个新线程,但该线程将在启动应用程序的同一AppDomain
中运行,即执行Application_Start
方法的线程,最后您将获得相同的Mapper.Instance
实例,因此您的配置计划只会在第一个请求到达时进行编译和缓存。只有第一个请求会受到影响,而不会受到以下请求的影响。其他请求将使用缓存配置的计划。因此,只要您处于同一AppDomain
并且不使用一些耗时的逻辑进入 AutoMapper 允许您执行的自定义映射替代方案,就不会对性能产生影响。
此外,AutoMapper还附带了一些可以激活或停用并获得性能的图形。
显式编译
正如我已经说过的,使用映射的第一个请求将编译您的配置计划。正如AutoMapper的文档所说:
由于表达式编译可能会占用大量资源,因此 AutoMapper 会在第一个映射上延迟编译类型映射计划。但是,此行为并不总是可取的,因此您可以告诉自动映射程序直接编译其映射。 对于几百个映射,这可能需要几秒钟。
您可以通过在映射配置后执行此操作来显式编译计划:
Mapper.Configuration.CompileMappings();
我使用它并将其与应用程序"预热"相结合,其中我的应用程序会自动启动,而不是等待第一个请求进行映射并编译我的配置计划。您可以查看此答案,了解如何启用该功能。
内联映射
在 6.2.0 中,AutoMapper 附带了一项称为内联映射的新功能,它允许您动态创建类型映射,而不是通过调用Mapper.Initialize
方法配置它们。因为内联映射是动态创建的,因此它们在当时进行编译,因此您正在寻找的收益明确编译您的计划并没有多大帮助。所以我在我的项目中所做的,我停用了这个功能,这样任何人都不能不宣传地使用它。要停用它,请执行以下操作:
cfg.CreateMissingTypeMaps = false;
编辑 26/02/2019
AutoMapper创建者今天(26/02/2019)刚刚添加了一篇关于AutoMapper使用指南的博客文章。必读。
我不知道这是否可以帮助您,但是我已经将自动映射器更新到8.1.0版本,并且性能更好。我已经看到性能在 7.0.0 版上得到了纠正,但在最新版本中可能会更好。根据您的实现,更新的影响最小。