how do I map this using Automapper

2019-09-18 07:07发布

问题:

I am in need to map the below scenario.

public class Customer
{
    public string CustomerJson { get; set; }
}

public class CustomerTO
{
     public object CustomerJson { get; set; }
}

From DAL I get CustomerJson value as below.

Customer.CustomerJson = {
    "name": "Ram",
    "city": "India"
}

I am in need to Deserialize this string. so I tried the below stuff while mapping.

var config = new MapperConfiguration(cfg =>
                    {
                        cfg.CreateMap<Customer, CustomerTO>()
                        .ForMember(dest => dest.CustName, opt => opt.MapFrom(src => JsonConvert.DeserializeObject(src.CustName)));
                    });

But this gives me run time error.

Unhandled Exception: AutoMapper.AutoMapperMappingException: Error mapping types.

So I kept it simple.

var config = new MapperConfiguration(cfg =>
                    {
                        cfg.CreateMap<Customer, CustomerTO>()
                        .ForMember(dest => dest.CustName, opt => opt.MapFrom(src => (src.CustName));
                    });

And I tried to deserialize it while consuming. But this give compile time error.

var custJson = JsonConvert.DeserializeObject(customerTO.CustomerJson );

Error 2 The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject(string)' has some invalid arguments

I know customerTO.CustomerJson is not string but how do should I do the required mapping?

Thanks.

回答1:

Based on your previous question and given the information above you seem to be confusing what you're trying to do here.

So I'm going to amalgamate the data from both in an attempt to solve the issues.

Entity Classes:

public class Customer
{
    public int CustomerId {get; set; }
    public string CustomerName { get; set; }
    public string CustomerJson { get; set; }
}

public class CustomerTO
{
    public int CustId { get; set; }
    public object CustData { get; set; }
    public object CustomerJson { get; set; }
}

AppMapper Class:

public static class AppMapper
{
    public static MapperConfiguration Mapping()
    {
        return new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<Customer, CustomerTO>()
                .ForMember(dest => dest.CustId, opt => opt.MapFrom(src => src.CustomerId))
                .ForMember(dest => dest.CustData, opt => opt.MapFrom(src => src.CustName))
                .ForMember(dest => dest.CustomerJson, opt => opt.MapFrom(src => JsonConvert.DeserializeObject(src.CustomerJson));
            });
    }
}

Main:

public class Program
{
    static void Main(string[] args)
    {
        var config = AppMapper.Mapping();
        var mapper = config.CreateMapper();

        // From Previous question get list of Customer objects
        var customers = AddCustomers();
        var mappedCustomers = mapper.Map<IEnumerable<CustomerTO>>(customers);
    }
}

A couple of things to point out I'm not sure what the purpose of CustData is in CustomerTO. It seems to be duplicating CustomerJson and if so remove it and the associated mapping.

Also, you never mention mapping from DTO back to entity, but for the JsonObject you just need to configure it to map the serialized string to the appropriate Property.



回答2:

This is how I addressed my requirement.

Db Entity

public class Customer 
{
   public string CustomerData { get; set; }
   // & other properties 
}

My DTO

public class CustomerTO
{
   public object CustomerData { get; set;}
   // & other properties
}

I created a Utility like class with name AppMapper. This is how my AppMapper.cs looks like.

public class AppMapper
{
    private IMapper _mapper;
    public AppMapper()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Customer, CustomerTO>();
            //& other such mapping
        });

        _mapper = config.CreateMapper();
    }

public CustomerTO Map(Customer customerEntity)
    {
        var customerTo= _mapper.Map<Customer,CustomerTO>(customerEntity);
        return customerTo;
    }

Now when I needed the mapping.

class DAL
{
    public CustomerTO GetCustomers()
    {
        var customers= //LINQ To get customers 

        var customerTO = Mapping(customer);

        return customerTO;

    }
    //However, this mapping glue in some internal class to retain SOLID principles
    private CustomerTO Mapping(Customer custEntity)
    {
        var custTO = _appMapper.Map(custEntity);
        var str = JsonConvert.Serialize(custTO.CustomerData);
        custTO.CustomerData = JsonConvert.Deserialize(str);
        return custTO;
    }
}

That's it.

@Barry O'Kane - Sincere thanks for your inputs.

Points to be noted:-

  1. I don't need to map manually any of the properites since the property name is same. Plus I am casting string to object. So no issues.
  2. If you use .Map() for one property, then I found that I need to map each property else it gives default value of the data type.(Ex. for int it gives 0).

Yes. agreed there could be other method in Automapper which allows me specify that for a particulay property do this manual mapping & for rest use Automapping mechanism. But I am not sure on that.

Please feel free to improve this ans in any way.

Hope this helps :)