Question

I am trying to use AutoMapper 3 to project a class with an Integer property to another class with a String property.

When the query is executed then I get the following exception:

System.NotSupportedException: LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.

Here are the relevant parts of the code:

public partial class Lookup
{
    public int LookupId { get; set; }
    public int LookupTypeId { get; set; }
    public string Value { get; set; }
    public int SequencialOrder { get; set; }

    public virtual LookupType LookupType { get; set; }
}

public class LookupProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<Lookup, SelectListItem>()
            .ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.LookupId.ToString()))
            .ForMember(dest => dest.Text, opt => opt.MapFrom(src => src.Value));

    }
}

And the query looks like:

Provinces = _db.Lookups.Project().To<SelectListItem>().ToList()

Question:

Is there a way I could configure the LookupProfile to do the proper mapping and still work inside Linq To Entities? Or is there another way I could make the projection work with Linq to Entities?

Était-ce utile?

La solution

The solution was to use the SqlFunctions.StringConvert function.

Here is the modified profile code that made everything work:

public class LookupProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<Lookup, SelectListItem>()
            .ForMember(dest => dest.Value, opt => opt.MapFrom(src => SqlFunctions.StringConvert((double)src.LookupId)))
            .ForMember(dest => dest.Text, opt => opt.MapFrom(src => src.Value));

    }
}

Autres conseils

I'll leave this answer here in case anyone else stumbles upon the same issue I had.

One problem with the current accepted answer is that if you're on an ASP.NET MVC project using client-side validation through helpers, you'll get a validation error for the ID field (if it's a number): The field [field] must be a number. That happens because the result from SqlFunctions.StringConvert returns a string with several leading spaces, so the unobtrusive validator doesn't see it as a number.

The way I solved this issue on my own was to create a generic SelectListItem<T> class that inherits from SelectListItem, hides the original Value property and implements its own Value setter:

public class SelectListItem<T> : SelectListItem
{
    public new T Value {
        set {
            base.Value = value.ToString();
        }
        // Kind of a hack that I had to add 
        // otherwise the code won't compile
        get {
            return default(T);
        }
    }
}

Then on the Automapper profile I would map the items like so:

public class LookupProfile : Profile
{
    protected override void Configure()
    {
        //Use whatever datatype is appropriate: decimal, int, short, etc
        CreateMap<Lookup, SelectListItem<int>>()
            .ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.LookupId))
            .ForMember(dest => dest.Text, opt => opt.MapFrom(src => src.Value));

    }
}

And finally on the Service layer, I would map the entities to the generic class and return an IEnumerable<SelectListItem>.

public IEnumerable<SelectListItem> GetList() {
    return _db.Lookups.Project().To<SelectListItem<int>>().ToList();
}

This way you'll get the right value for the Value property without trailing spaces.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top