嗨我有一些自动映射器的主要问题,它很慢。我不知道如何加快速度。
我正在使用nhibernate,流利的nhibernate和asp.net mvc 3.0
[Serializable()]
public class Test
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual DateTimeDate { get; set; }
public virtual IList<Reminder> Reminders { get; set; }
public virtual IList<Reminder2> Reminders2 { get; set; }
public virtual Test2 Test2 { get; set; }
public Test()
{
Reminders = new List<Reminders>();
Reminders2 = new List<Reminders2>();
}
}
所以你可以看到我得到了一些属性,在我的数据库中有一些其他类我在它们之间有引用。
然后我这样做
var a = // get all items (returns a collection of Test2)
var List<MyViewModel> collection = new List<MyViewModel>();
foreach (Test2 t in a)
{
MyViewModel vm = Mapper.Map<Test2, MyViewModel>(t);
vm.SetDateFormat(t.DateTimeDate, DateFilters.All.ToString());
collection.Add(vm);
}
//查看模型
public class MyViewModel
{
public int Id { get; private set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime DateTimeDate { get; set; }
public string FormatedDueDate { get; set; }
public string Test2Prefix { get; set; }
public string Test2BackgroundColor { get; set; }
public string SelectedDateFilter { get; set; }
public bool DescState { get; set; }
public bool AlertState { get; set; }
/// <summary>
/// Constructor
/// </summary>
public MyViewModel()
{
// Default values
SelectedDateFilter = "All";
DescState = false;
AlertState = false;
}
/// <summary>
/// Sets the date formatter string used
/// </summary>
/// <param name="dateFormat"></param>
public void SetDateFormat(DateTime dueDate, string dateFilter)
{
// simple if statement to format date.
}
}
//映射
Mapper.CreateMap<Test2,MyViewModel>().ForMember(dest => dest.DescState, opt =>
opt.ResolveUsing<DescStateResolver>())
.ForMember(dest => dest.AlertState, opt =>
opt.ResolveUsing<AlertStateResolver>());
解析器
public class AlertStateResolver : ValueResolver<Task, bool>
{
protected override bool ResolveCore(Task source)
{
if (source.Reminders.Count > 0 || source.Reminders2.Count > 0)
{
return true;
}
else
{
return false;
}
}
}
public class DescStateResolver : ValueResolver<Task,bool>
{
protected override bool ResolveCore(Task source)
{
if (String.IsNullOrEmpty(source.Description))
{
return false;
}
else
{
return true;
}
}
}
忽略奇怪的名字和任何错别字我的真实对象工作得很好并且有意义。
所以我用秒表做了这个
Stopwatch a = new Stopwatch()
foreach (Test2 t in a)
{
a.Start()
MyViewModel vm = Mapper.Map<Test2, MyViewModel>(t);
a.Stop()
vm.SetDateFormat(t.DateTimeDate, DateFilters.All.ToString());
collection.Add(vm);
}
var b = a.Elapsed; // comes back with 32 seconds.
我需要非常好地优化它。
代替:
var a = // get all items (returns a collection of Test2)
List<MyViewModel> collection = new List<MyViewModel>();
foreach (Test2 t in a)
{
MyViewModel vm = Mapper.Map<Test2, MyViewModel>(t);
vm.SetDateFormat(t.DateTimeDate, DateFilters.All.ToString());
collection.Add(vm);
}
尝试:
var a = // get all items (returns a collection of Test2)
List<MyViewModel> collection = Mapper.Map<IEnumerable<Test2>, IEnumerable<MyViewModel>>(a);
这相当于第一个除了SetDateFormat
调用,你可以在你的映射定义。它也可能更快。
如果你在Test2 => MyViewModel
之间定义了一个映射,AutoMapper会自动为IEnumerable<Test2> => IEnumerable<MyViewModel>
提供一个映射,这样你就不需要循环了。
你也在你的问题中提到过NHibernate。确保源对象及其集合在将数据传递到映射层之前从数据库中急切加载,或者您不能责怪AutoMapper因为速度缓慢,因为当它尝试映射源对象的其中一个集合时它会访问数据库,因为NHibernate没有获取此集合。
要寻找的另一件事是映射抛出异常的代码。 AutoMapper将以静默方式捕获这些内容,但以这种方式捕获异常会影响性能。
因此,如果SomethingThatMightBeNull通常为null,则由于NullreferenceExceptions,此映射将执行得很差:
.ForMember(dest => dest.Blah, c.MapFrom(src=>src.SomethingThatMightBeNull.SomeProperty))
我发现这样做的更改将超过映射所用的一半时间:
.ForMember(dest => dest.Blah, c.MapFrom(src=> (src.SomethingThatMightBeNull == null
? null : src.SomethingThatMightBeNull.SomeProperty)))
更新:C#6语法
.ForMember(dest => dest.Blah, c.MapFrom(src => (src.SomethingThatMightBeNull?.SomeProperty)))
添加此功能后,能够改善启动时间
.ForAllMembers(options => options.Condition(prop => prop.SourceValue != null));
在每个结尾
.CreateMap<..,..>()
如果您的子集合很大,则可以使用“Any()”而不是“Count> 1”。 Any函数只需迭代一次,而Count可能需要迭代entmes集合(取决于实现)。
不确定这是否会导致您的情况出现任何问题,但要注意序列化自动实现的属性。
每次编译代码时,编译器都会随机选取每个(匿名)支持字段的名称。因此,如果使用一次编译的progran序列化数据并使用其他程序对其进行反序列化,则可能会看到一些令人惊讶的异常。
我修复了与你相同的问题。仅映射一个对象也需要花费32秒。所以,我使用opts.Ignore()来处理一些自定义对象,如下所示:
CreateMap<SiteConfiguration, Site>()
.ForMember(x => x.SubSystems, opts => opts.Ignore())
.ForMember(x => x.PointInformations, opts => opts.Ignore())
.ForMember(x => x.Schedules, opts => opts.Ignore())
.ForMember(x => x.EquipmentDefinitions, opts => opts.Ignore());
在那之后,它只花费几毫秒。
一个很好的提示是优化AutoMapper的配置,使用Ignore作为ViewModel的属性,并进行方法调用以验证映射“Mapper.AssertConfigurationIsValid()”。
Mapper.Initialize(cfg =>
{
cfg.ValidateInlineMaps = true;
cfg.AllowNullCollections = false;
cfg.AllowNullDestinationValues = true;
cfg.DisableConstructorMapping(); // <= In the case of my project, I do not use builders, I had a performance gain.
cfg.AddProfile<DomainToViewModelMappingProfile>();
cfg.AddProfile<ViewModelToDomainMappingProfile>();
});
Mapper.AssertConfigurationIsValid();