Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Not sure if the title makes sense, but here's what I'm doing. I'm using AutoMapper to map my Entity Framework Entities 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.

like image 871
jason Avatar asked Jan 27 '10 14:01

jason


1 Answers

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

like image 157
mkedobbs Avatar answered Sep 28 '22 01:09

mkedobbs