简体   繁体   English

在C#asp.net性能实践中使用静态自动映射器

[英]Using static automapper in C# asp.net performance practices

In advance, I apologize if this question has been answered already somewhere else; 预先,对于这个问题是否已经在其他地方得到解答,我深表歉意。 But I've been finding a lot of mixed results on the matter. 但是我在这件事上发现了很多不同的结果。

I'm using: 我正在使用:

  • .net Framework 4.6.1 .net Framework 4.6.1
  • Microsoft.AspNet.Mvc 5.2.6 Microsoft.AspNet.Mvc 5.2.6
  • AutoMapper 6.2.2 AutoMapper 6.2.2
  • EntityFramework 6.2.0 实体框架6.2.0

I'm quite new to both ASP.net and C# and lately I've become a fan of the AutoMapper package. 我对ASP.net和C#都是陌生的,最近我成为了AutoMapper软件包的粉丝。 I've mostly been using it to convert my Entities I get from my ApplicationDbContext to my DTO 's (Data Transfer Objects) or ViewModel 's. 我主要使用它来将我从ApplicationDbContext获得的实体转换为DTO (数据传输对象)或ViewModel

I now use this setup in my applications to initialize and use the Mapper in my Controller 's: 现在,我在应用程序中使用此设置来初始化并在Controller使用Mapper

Global.asax.cs Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        Mapper.Initialize(AutoMapperConfiguration.Configure);

        // Other configuration for MVC application...
    }
}

AutoMapperConfiguration.cs AutoMapperConfiguration.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 PostsController.cs和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; }
    }
}

Code summary 代码摘要

I am configuring my Mapper in a static class ( AutoMapperConfiguration ) that contains a Config method which is invoked from the Global.asax.cs file and maps the required classes. 我在一个静态类( AutoMapperConfiguration )中配置Mapper ,该类包含一个Config方法,该方法从Global.asax.cs文件中调用并映射所需的类。

Then, in my Controller I use the static method Mapper.Map to map my Post to it's DetailsViewModel . 然后,在我的Controller我使用静态方法Mapper.MapPost映射到它的DetailsViewModel

The question 问题

How does this usage of AutoMapper (via static method Mapper.Map ) impact performance, and is there a better way to do this? AutoMapper的这种用法(通过静态方法Mapper.Map )如何影响性能,还有更好的方法吗?

Some clarifications: 一些说明:

For example: What if I get 100 requests per second on different controller actions; 例如:如果我每秒收到100个不同的控制器操作请求,该怎么办? As far as I know every request will have a separate thread but will access the same memory for the Mapper.Map method (if I am correct). 据我所知,每个请求都将有一个单独的线程,但是将为Mapper.Map方法访问相同的内存(如果我正确的话)。 Which - to my knowledge - means performance will be impacted severely. 据我所知,这意味着性能将受到严重影响。

A question I have already looked at but got mixed results from: 我已经看过一个问题,但是得到的结果却参差不齐:

Non-static AutoMapper and ASP.NET MVC -> Where to place AutoMapper.CreateMaps? 非静态AutoMapper和ASP.NET MVC- > 在哪里放置AutoMapper.CreateMaps?

Please correct me if I'm wrong on any of this. 如果我在这方面有误,请纠正我。

How does this usage of AutoMapper (via static method Mapper.Map) impact performance, and is there a better way to do this? AutoMapper的这种用法(通过静态方法Mapper.Map)如何影响性能,还有更好的方法吗?

Your usage of AutoMapper is correct. 您对AutoMapper的使用是正确的。 Initialization at the application startup is a best practice to respect. 在应用程序启动时进行初始化是值得尊重的最佳实践。 By calling Mapper.Initialize you are initializing a mapper which is an static instance of IMapper interface (accessible through Mapper.Instance static property). 通过调用Mapper.Initialize您正在初始化一个映射器,它是IMapper接口的静态实例(可通过Mapper.Instance静态属性访问)。 When using the static members from Mapper class you are dealing with Mapper.Instance and it is the same instance for all objects into the same AppDomain . 当使用Mapper类中的静态成员时,您正在处理Mapper.Instance并且所有对象进入同一AppDomain都是相同的实例。 You will not impact performance as long as long you're into the same AppDomain and not doing some time consuming in your mapping configuration (someone may put some business logic or time consuming logic into AfterMap , BeforeMap , ResolveUsing , MapFrom etc.). 只要您使用相同的AppDomain并且不花费一些时间在映射配置中(您可能会在AfterMapBeforeMapResolveUsingMapFrom等中放入一些业务逻辑或耗时的逻辑),就不会影响性能。

What if I get 100 requests per second on different controller actions; 如果我每秒在不同的控制器操作上收到100个请求怎么办? As far as I know every request will have a separate thread but will access the same memory for the Mapper.Map method (if I am correct). 据我所知,每个请求都将有一个单独的线程,但是将为Mapper.Map方法访问相同的内存(如果我正确的话)。 Which - to my knowledge - means performance will be impacted severely. 据我所知,这意味着性能将受到严重影响。

Your application use one instance of AppDomain , each request will got a new thread but that thread will run in the same AppDomain that started your application, the one that executed the Application_Start method and finally you'll get the same instance of Mapper.Instance so your configuration's plan will only be compiled and cached when the first request arrive. 您的应用程序使用一个AppDomain实例,每个请求将获得一个新线程,但是该线程将在启动您的应用程序的同一AppDomain中运行,该线程执行了Application_Start方法,最后您将获得相同的Mapper.Instance实例。只有在第一个请求到达时,才会编译和缓存您的配置计划。 Only the first request will be impacted not the following. 仅第一个请求不会受到影响。 Other requests will use the cached configuration's plan. 其他请求将使用缓存的配置计划。 So no performance impact as long as you're in the same AppDomain and not using some time consuming logic into custom mapping alternative that AutoMapper allows you to do. 因此,只要您位于同一个AppDomain并且没有在AutoMapper允许您执行的自定义映射替代中使用一些耗时的逻辑,就不会对性能造成影响。

Also AutoMapper comes with some cnfigurations that can be activated or deactivated and gain performance. AutoMapper还带有一些可以激活或取消激活并获得性能的配置。

Explicit compilation 显式编译

As I already said, the first request that uses mapping will compile your configuration's plan. 正如我已经说过的,使用映射的第一个请求将编译您的配置计划。 As AutoMapper's documentation says: 如AutoMapper的文档所述

Because expression compilation can be a bit resource intensive, AutoMapper lazily compiles the type map plans on first map. 由于表达式编译可能会占用一些资源,因此AutoMapper会在第一个地图上延迟编译类型地图计划。 However, this behavior is not always desirable, so you can tell AutoMapper to compile its mappings directly. 但是,这种行为并不总是令人满意的,因此您可以告诉AutoMapper直接编译其映射。 For a few hundred mappings, this may take a couple of seconds. 对于数百个映射,这可能需要几秒钟。

You can explicitly compile the plan by doing this just after your mapping configuration: 您可以在映射配置之后通过执行以下操作来显式编译计划:

Mapper.Configuration.CompileMappings();

I use this and combine it with application "warming" where my application is started automatically instead of waiting for the first request to do mapping and compiles my configuration's plan. 我将其与应用程序“ warming”结合使用,在该应用程序中,我的应用程序将自动启动,而不是等待第一个请求进行映射和编译我的配置计划。 You can look at this answer for how to enable that feature. 您可以查看此答案以了解如何启用该功能。

Inline mapping 内联映射

With 6.2.0, AutoMapper came with a new feature which is called inline mapping which allow you to create a type map on the fly instead of configuring them through the call of Mapper.Initialize method. 在6.2.0中,AutoMapper附带了一项称为内联映射的新功能,该功能使您可以即时创建类型映射,而不必通过调用Mapper.Initialize方法对其进行配置。 Because an inline mapping is created on the fly so they are compiled at that time so the gain you're looking for explicitly compiling your plan does not help a lot. 因为内联映射是动态创建的,所以它们会在那时进行编译,因此您要寻求的明确编译计划的收益没有太大帮助。 So what I do in my projects, I deactivate this feature so anyone can't inadvertisely used it. 因此,在我的项目中,我禁用了此功能,因此任何人都无法以广告形式使用它。 To deactivate it, you do this: 要停用它,请执行以下操作:

cfg.CreateMissingTypeMaps = false;

Edit 26/02/2019 编辑26/02/2019

AutoMapper creator just added a blog post today (26/02/2019) about AutoMapper Usage Guidelines . AutoMapper的创建者刚刚在今天(26/02/2019)添加了一篇有关AutoMapper使用指南的博客文章。 Must read. 必读。

I don't know if this can help you, but i've updated the AutoMapper to version 8.1.0 and the performance is better. 我不知道这是否可以为您提供帮助,但是我已经将AutoMapper更新到版本8.1.0,并且性能更好。 I've seen that performance was corrected on version 7.0.0 but in the latest version can be better. 我已经看到性能在7.0.0版本上已得到纠正,但在最新版本中可能会更好。 Depends on your implementation the impact of update is minimal. 根据您的实施,更新的影响很小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM