views:

509

answers:

1

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.

+2  A: 

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

mkedobbs
Thanks for the detailed response. Most of it is over my head, but that's more me than you. I'm still wrestling with the use of lambda's and such.Anyhow, do you know if there is an automapper method that allows you to explicitly assign a calculated value to a property? If that's the case, I could just create an EntityKey object with the necessary parameters and map that value to the EntityKey property on my entity. I had been looking at the MapFrom AutoMapper method, but haven't had time to delve into it.
Jason
I was going to suggest that as a simpler method, but decided to directly answer your question. I'll add that to the answer. (Also, don't forget to vote up and/or accept the answer if you find it to meet your needs)
mkedobbs
Thanks for going above and beyond on this question. This really helped me! I did go ahead and settle on just mapping to a EntityKey property and that seems to have worked.
Jason