Automapper Projection and LINQ queries with EF6

automapper entity-framework-6 linq

Question

We have a legacy system with support for multiple languages. The language specific fields is duplicate on each database row - per language with language code as column name prefix. In our EF6 model we us a LocalizedString complex type to hold all the translated values for each string property. Based on this article: http://patrickdesjardins.com/blog/how-to-have-localized-string-with-mvc-and-entity-framework

I am able to define the mapping based on the web app threads CurrentCulture and us it in linq projections with EF6. But I am not totally happy with how the mapping code is inside the view model.

Sample domain class, ViewModel and mapping:


public partial class CountryDomain
{
    public CountryDomain()
    {
        CountryName = new LocalizedString();
    }

    public long CountryId { get; set; }
    public LocalizedString CountryName { get; set; }
    public string CountryCode { get; set; }
}

public class CountryViewModel
{
    public long CountryId { get; set; }
    public string CountryName { get; set; }
    public string CountryCode { get; set; }
}

public void CreateMap() 
{ 
Mapper.CreateMap<CountryDomain, CountryViewModel>()
  .ForMember(dest => dest.CountryName, opt => opt.MapFrom(src =>
     Context.TwoLetterISOLanguageName == "en" ? src.CountryName.ValueEn :
     Context.TwoLetterISOLanguageName == "da" ? src.CountryName.ValueDa :
     src.CountryName.ValueNo));

}


I would like to move the content of MapFrom to a general location and still retain the support for LINQ Projection Queries. Suppose we suddenly have to add support for a new language and then must remenber to update a lot of view models.

The inline ternery operator (?) is translated into a SQL: CASE WHEN .... END statement to only load the correct filed from database which is quite nice.

Do any of you have a solution to this problem that still support projection in LINQ queries?

1
1
2/4/2015 11:47:45 AM

Accepted Answer

You can use ProjectUsing:

string lang = null;
Mapper.CreateMap<LocalizedString, string>().ProjectUsing(src =>
 lang == "en" ? src.ValueEn :
 lang == "da" ? src.ValueDa :
 src.ValueNo);

During mapping:

context.Countries.Project().To<CountryViewModel>(new { lang = Context.TwoLetterISOLanguageName });

I switched to the parameterized projection because I wasn't sure if that "Context" value changed per request. If it's read once for the lifetime of the application you can go with your way of hardcoding it, if it changes then my way ensures that it gets parameterized with the query.

1
2/4/2015 2:27:01 PM

Popular Answer

That is much better solution! Thank you!

In order to support mapping from in-memory objects I assume that requires an additional:


map.ConvertUsing(src =>    
    Context.TwoLetterISOLanguageName == "en" ? src.ValueEn :
    Context.TwoLetterISOLanguageName == "da" ? src.ValueDa :
    src.ValueNo);    

var vm = Mapper.Map<List<CountryViewModel>>(list);



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow