Not sure if the title makes sense, but here's what I'm doing. I'm using AutoMapper to map my Entity Framework Ent开发者_开发百科ities to my DTO objects and vice versa. The issue comes when I try to map the DTO data to the EF entity. There's not a property to property mapping for the EntityKey. To fix this, I do some like the following:
Mapper.CreateMap<VideoDTO, Video>()
.ForMember(dest => dest.EntityKey, opt =>
opt.ResolveUsing<VideoEntityKeyResolver>());
The VideoEntityKeyResolver class looks like:
public class VideoEntityKeyResolver : ValueResolver<VideoDTO, EntityKey>
{
protected override EntityKey ResolveCore(VideoDTO source)
{
EntityKey key = new EntityKey("EntityFrameworkTestingEntities.Videos",
"VideoId", source.VideoId);
return key;
}
}
I was wondering if there was a more generic way of doing this where I could have 1 class with a constructor that takes the Entity Set Name, Key Property Name, and Key Value in a constructor.
I've thought about just adding an EntityKey property to my DTO objects that sounds a lot like crossing the streams as the whole point of creating the DTO objects was to severe the tie to my data layer in the rest of my application.
On a completely unrelated note (I can create a new question if needed), where exactly do I need to define my mappings when using AutoMapper? Currently I'm doing it in the constructor of my context object (which is my EF repository object), but I believe that's rather costly and just not correct, though, it works.
I haven't gone so far as to test this, but the following should work:
public class EntityKeyResolver<T, TProperty> : ValueResolver<T, EntityKey> where T : class
{
private Expression<Func<T, TProperty>> _propertyExpression;
private string _qualifiedEntitySetName;
private string _keyName;
public EntityKeyResolver(string qualifiedEntitySetName, string keyName, Expression<Func<T, TProperty>> propertyExpression)
{
_qualifiedEntitySetName = qualifiedEntitySetName;
_keyName = keyName;
_propertyExpression = propertyExpression;
}
protected override EntityKey ResolveCore(T source)
{
return new EntityKey(_qualifiedEntitySetName, _keyName, ExpressionHelper.GetValue(_propertyExpression));
}
}
The ExpressionHelper
is a static class that I use to help evaluate expressions in a variety of cases. The GetValue method looks like this:
internal static TProperty GetValue<T, TProperty>(T obj, Expression<Func<T, TProperty>> expression) where T : class
{
if (obj == null)
{
return default(TProperty);
}
Func<T, TProperty> func = expression.Compile();
return func(obj);
}
You would then alter your code as follows (assuming VideoId is a Guid):
Mapper.CreateMap<VideoDTO, Video>()
.ForMember(dest => dest.EntityKey, opt => opt.ResolveUsing(new EntityKeyResolver<VideoDTO, Guid>("EntityFrameworkTestingEntities.Videos", "VideoId", v => v.VideoId)));
Probably a little more verbose than you wanted. An alternative to the generic resolver would be to use MapFrom to map the entity key (they are about equally verbose):
Mapper.CreateMap<VideoDTO, Video>()
.ForMember(dest => dest.EntityKey, opt => opt.MapFrom(src => new EntityKey("EntityFrameworkTestingEntities.Videos", "VideoId", src.VideoId)));
As to your other question, I've gotten in the habit of creating a static class that initializes my maps and sets a boolean as to whether or not the mappings have been created since you only need to call it once per AppDomain. Then, in the constructor of my repository, I just call MapInitializer.EnsureMaps();
精彩评论