初始化映射器
public static class MapperConfig
{
public const int MAX_MAPPING_DEPTH = 2;
private static MapperConfiguration _config;
private static IMapper _mapper;
public static IMapper Mapper => _mapper ?? (_mapper = GetConfig().CreateMapper());
public static MapperConfiguration GetConfig()
{
var assembly = Assembly.GetExecutingAssembly();
if (_config != null)
return _config;
_config = new MapperConfiguration(cfg =>
{
cfg.AddProfiles(assembly);
cfg.ForAllMaps(ConfigTypeMapping);
});
_config.AssertConfigurationIsValid();
return _config;
}
public static void ConfigTypeMapping(TypeMap map, IMappingExpression expression)
{
map.ShouldCheckForValid();
expression.PreserveReferences();
expression.MaxDepth(MAX_MAPPING_DEPTH);
//expression.ForAllMembers(m => m.UseDestinationValue());
}
}
主要实体
[ResourceKey("MEDIA_ITEM")]
public class MediaItem : SocialEntity
{
/// <summary>
/// User-defined media item <see cref="Title"/> or its original file name.
/// </summary>
public string Title { get; set; }
/// <summary>
/// The original <see cref="FileName"/>.
/// </summary>
public string FileName { get; set; }
/// <summary>
/// The <see cref="FileGuid"/> that refers to the CDN file entry.
/// </summary>
public Guid FileGuid { get; set; }
/// <summary>
/// The purpose / group (aka <see cref="Type"/>) for this <see cref="MediaItem"/>.
/// </summary>
public FileType Type { get; set; }
/// <summary>
/// Allows limiting retrieval of this <see cref="MediaItem"/> depending on a specific <see cref="PrivacyLevel"/>.
/// </summary>
public PrivacyLevel PrivacyLevel { get; set; }
/// <summary>
/// The <see cref="Source"/> for the Content / File of this <see cref="MediaItem"/>.
/// </summary>
public virtual Url Source { get; set; }
public Guid? SourceGuid { get; set; }
/// <summary>
/// The <see cref="Profile"/> entity that this media file is bound to.
/// </summary>
public virtual Profile Profile { get; set; }
public Guid? ProfileGuid { get; set; }
/// <summary>
/// The <see cref="Article"/> entity that this media file is bound to.
/// </summary>
public virtual Article Article { get; set; }
public Guid? ArticleGuid { get; set; }
/// <summary>
/// The <see cref="Communication.Comment"/> entity that this media file is bound to.
/// </summary>
public virtual Comment Comment { get; set; }
public Guid? CommentGuid { get; set; }
/// <summary>
/// The <see cref="Theme"/> entity that this media file is bound to.
/// </summary>
public virtual Theme Theme { get; set; }
public Guid? ThemeGuid { get; set; }
}
如您所见,该实体继承自包含一堆默认集合的SocialEntity
。 SocialEntity
然后继承基础Entity
类,其中包含Id,用户,创建日期等
基类映射
CreateMap<SocialEntity, SocialDomainModel>()
.IncludeBase<Entity, DomainModel>()
.ReverseMap()
.IncludeBase<DomainModel, Entity>();
CreateMap<Entity, DomainModel>()
.IncludeBase<global::Data.Pattern.Entity.Entity, DomainModel>()
.ReverseMap()
.IncludeBase<DomainModel, global::Data.Pattern.Entity.Entity>();
CreateMap<global::Data.Pattern.Entity.Entity, DomainModel>()
.ForMember(dm => dm.Creator, mo => mo.Ignore())
.ForMember(dm => dm.CreatorGuid, mo => mo.Ignore())
.ReverseMap();
CreateMap<MediaItem, Domain.Models.Storage.MediaItem>()
.IncludeBase<SocialEntity, SocialDomainModel>()
.ReverseMap()
.IncludeBase<SocialDomainModel, SocialEntity>();
测试方法
[TestMethod]
public void MapMediaTest()
{
var m = MapperConfig.Mapper;
var entity = new MediaItem();
var entityToDomain = m.Map<Domain.Models.Storage.MediaItem>(entity);
Assert.IsTrue(entityToDomain != null);
}
如您所见,内存在~20秒后泛滥
看起来像某个地方的automapper被配置错误导致无限循环并导致StackOverflowException
。
我的尝试
HashSet
个集合转换为普通ICollections
。我在实体和域模型之间进行映射,不使用投影/查询。
在映射之前检索每个实体,没有延迟加载。
注意,我有virtual
个关键字和其他实体和类,默认情况下会初始化每个集合。
我没有选择,除了放弃(不是那样)AutoMapper以外,我无法想到要做什么。我非常喜欢一些额外的见解,谢谢!
修改
Article
班。
/// <summary>
/// Used to store <see cref="Article"/> data.
/// </summary>
[ResourceKey("ARTICLE")]
public class Article : SocialEntity
{
/// <summary>
/// The main <see cref="Title"/> of this <see cref="Article"/>.
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// The content <see cref="Body"/> of this <see cref="Article"/>.
/// </summary>
public string Body { get; set; } = string.Empty;
/// <summary>
/// The <see cref="ShortDescription"/> of this <see cref="Article"/>.
/// </summary>
public string ShortDescription { get; set; } = string.Empty;
/// <summary>
/// The long <see cref="Description"/> of this <see cref="Article"/>.
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// The <see cref="DateTime"/> that the <see cref="Entity"/> will be available to use.
/// </summary>
public DateTime ExpiresOn { get; set; }
public virtual HashSet<Url> Sources { get; set; } = new HashSet<Url>();
public virtual HashSet<MediaItem> Media { get; set; } = new HashSet<MediaItem>();
public virtual HashSet<Tag> Tags { get; set; } = new HashSet<Tag>();
public virtual HashSet<Hobby> Hobbies { get; set; } = new HashSet<Hobby>();
}
答案 0 :(得分:0)
根据我使用Automapper的经验,在映射循环引用时会出现StackOverflowException。
我可以看到MediaItem有一个&#34;文章&#34; property,Article有一个MediaItems集合。
如果您的域模型也具有这些导航属性的等价物,那么您是否需要它们两种方式?
答案 1 :(得分:0)
@ Richard的回答让我走上正轨!导航属性太多......
AutoMapper无法弄清楚如何将它们全部映射,可能缺少配置。
实体框架没有任何问题,只有AutoMapper。
我删除了大部分导航属性,似乎我并不需要大部分导航属性。这解决了我的问题快速&amp;简单的方法!
答案 2 :(得分:-1)
就我而言,我的系统中有一个这样的结构(DTO和实体模型都相同)。有计划列表,只有其中一些计划会引发StackOverflowException。实体框架在具有多个嵌套级别的情况下表现不错,但是AutoMapper在大约3个级别上会失败。幸运的是,不需要PreviousPlan属性,因此我将其删除,并且工作正常。如果您无力删除它,我建议您使用Automapper的MaxDepth。
我知道这并不是真正的答案,但也许可以帮助人们找到原因。
public class Plan
{
public int Id { get; set; }
public int? PreviousPlanId { get; set; }
public Plan PreviousPlan { get; set; }
...
}