1

I am trying to use AutoMapper's ProjectTo extension method to convert an Entity Framework table based object to a custom DTO. The Person entity on the EF side looks like this:

public partial class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public string Age { get; set; }
}

The PersonViewModel DTO that I am projecting to looks like this:

public class PersonViewModel
{
    public PersonViewModel(Person p)
    {
        PersonId = p.PersonId;
        Name = p.Name;
        Age = Convert.ToInt32(p.Age);
    }

    public PersonViewModel()
    {

    }

    public PersonViewModel(int id, string name, int age)
    {
        PersonId = id;
        Name = name;
        Age = age;
    }

    public int PersonId { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

The key thing to note is that the person table contains a field called Age that is a nvarchar type and the PersonViewModel has an int type for Age. Unfortunately, I cannot change the table and have to find a way to convert the data.

The following code is the AutoMapper configurations I have tried, their "issues", and the consuming code (database call, etc).

MapperConfiguration config = new MapperConfiguration(cfg => 
        {
            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>();


            // -----  Throws "Linq to Entities does not support method" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ForMember(dest => dest.Age, opt => opt.MapFrom(src => Convert.ToInt32(src.Age)));


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructProjectionUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = Convert.ToInt32(src.Age)
            //    });


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructProjectionUsing(src => new PersonViewModel(src.PersonId, src.Name, Convert.ToInt32(src.Age)));


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructProjectionUsing(src => new PersonViewModel(src.PersonId, src.Name, 25));


            // -----  Throws "Linq to Entities does not support method" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ProjectUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = Convert.ToInt32(src.Age)
            //    });


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = Convert.ToInt32(src.Age)
            //    });


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructUsing(src => new PersonViewModel(src));


            // -----  Doesn't throw exception, but all values are null or 0.  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConvertUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = Convert.ToInt32(src.Age)
            //    });


            // -----  Doesn't throw exception, but all values are null or 0.  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConvertUsing(src => new PersonViewModel(src.PersonId, src.Name, Convert.ToInt32(src.Age)));


            // -----  Works, but bypasses the conversion I need  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ProjectUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = 25
            //    });


            // -----  Works, but bypasses the conversion I need  ------
            cfg.CreateMap<Person, PersonViewModel>()
                .ForMember(dest => dest.Age, opt => opt.MapFrom(src => 35));
        });

        using (FamilyEntities db = new FamilyEntities())
        {
            PersonViewModel p = db.Set<Person>()
                .Where(x => x.PersonId == 1)
                .ProjectTo<PersonViewModel>(config)
                .SingleOrDefault();

            Console.WriteLine("Name: " + p.Name + Environment.NewLine + "Age: " + p.Age);
        }

        Console.ReadLine();

Is this conversion of string to int possible using AutoMapper's ProjectTo? Secondly, if it is possible, what does that configuration look like?

Thanks in advance for any help.

user1011627
  • 1,741
  • 1
  • 17
  • 25
  • http://stackoverflow.com/a/24027242/1736944 might provide you a viable workaround unless automapper has something clever up its sleeve. – 9Rune5 May 12 '16 at 20:07
  • @9Rune5 - Thanks for pointing out this extensibility point of EF. I wasn't aware that you could define your own methods like this. I have tested this and it is a viable workaround. If you supply this as an answer, I will accept it. – user1011627 May 12 '16 at 21:55

2 Answers2

4

Your first try is why this won't work. Entity Framework doesn't know how to convert a string to an int, so you're hosed. None of the other ways matter, what matters is the exception that EF gave you.

If EF can't support the conversion, there's nothing AutoMapper can do to help you.

Instead, I'd do something like a conversion inside of your viewmodel:

public class PersonViewModel
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public string Age { get; set; }
    public int AgeValue => Convert.ToInt32(Age);
} 
Jimmy Bogard
  • 26,045
  • 5
  • 74
  • 69
  • Thanks for responding Jimmy. The solution you provided would definitely work, but it would force me into adding more data (negligible I know), logic (albeit trivial), and redundancy into my models (ie..Person2ViewModel). I'm surprised that EF doesn't support this natively as it doesn't seem like a crazy request considering TSQL supports CAST and CONVERT already. I think 9Rune5's answer of providing a custom function to EF is a better approach for my situation as it eliminates the negatives listed above and is more in line with the rest of the mapping/conversion that is present. – user1011627 May 12 '16 at 22:17
1

https://stackoverflow.com/a/24027242/1736944 explains how to add functionality to EF. Basically the generated query will have to perform the typecast.

Community
  • 1
  • 1
9Rune5
  • 373
  • 1
  • 4
  • 16