开发者

Is there a generic way of using AutoMapper's ValueResolver to map EntityKey values for EF Entities?

开发者 https://www.devze.com 2022-12-18 18:39 出处:网络
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

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();

0

精彩评论

暂无评论...
验证码 换一张
取 消