afaaqa dabookick
afaaqa dabookick

Reputation: 97

While returning a DTO which is mapped using automapper, how do I assign it a list of it's child class type?

I have an entity

Topics having these properties

1. public string Name {get; set;}
2. public string Location {get; set;}
3. public string Identity {get; set;}
4. public TopicsMapped TopicsMapped {get; set;}

Also, I have one DTO that is TopicsDTO and it has properties

1. public string Name {get; set;}
2. public string Location {get; set;}
3. public string Identity {get; set;}
4. public TopicsMapped TopicsMapped {get; set;}

so in Automapper.config I have created a mapping of these 2.

roughly,

    var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<Topics, TopicsDTO>();
    
});

now in webapi method, I am using linq to sql and I have written something like this

 var query= (from t in topics select t).tolist();

 Topics t= new topics();

 var mapper = new Mapper(config);
 var topicsDTO = mapper.Map<List<TopicsDTO>>(query);
 
 return OK(topicsDTO)  //since the webapi method returning IHttpActionResult.

But my problem is that I have to assign a list of TopicsMapped type and return in OK as a part of TOPICs DTO. I tried taking join of both types and assign to query but it threw error that mapping configuration is not properly configured.

How do I do this?

Upvotes: 0

Views: 1892

Answers (1)

Steve Py
Steve Py

Reputation: 34908

Defining a DTO for your web interface is good, but the purpose of implementing a DTO is to provide a model specific to what the consumer needs and to avoid passing more data that is needed, or data/schema information that the consumer has no right to see.

Assuming TopicsMapped is a class containing some fields, the relationship between Topic & TopicsMapped is a one-to-one. Typically in a one-to-one relationship you might want to provide some relevant fields from the related entity. A general rule to follow when mapping DTOs or ViewModels is don't mix DTOs (view models) with entities. (data models) This can lead to problems because if TopicsMapped has references back to Topic or other entities/tables, putting that entity into your DTO could see lazy load calls or exceptions when serializing your DTO. Either create a DTO graph for related entities where needed, or flatten the DTO model to represent the data you care about.

For example, if the TopicsMapped has a field called "Name" that we want to include for our web API, we don't need the entire TopicsMapped model, just the field. So we can flatten that down in the DTO:

[Serializable]
public class TopicDTO
{
    public int TopicId { get; set; }
    public string Name { get; set; }
    public string Location { get; set; }
    public string Identity { get; set; }
    public string MappedName { get; set; } // <- Will map to TopicsMapped.Name
}

Then with the automapper configuration:

var config = new MapperConfiguration(cfg => 
{
    cfg.CreateMap<Topics, TopicsDTO>()
        .ForMember(x => x.MappedName, opt => opt.MapFrom(src => src.TopicsMapped.Name));
};

By default Automapper can use convention to work out mappings for destination fields from the source object, and if you follow those conventions in your DTO it is possible to avoid an explicit mapping, but I honestly prefer explicit mapping to avoid the guesswork and bugs if property names get changed. It also helps ensure usage counts for properties are properly accounted for. Linq2SQL should provide access to IQueryable, so to properly leverage projection /w Automapper you will want to use ProjectTo rather than Map:

var topicDTOs = Context.Topics
    .ProjectTo<TopicDTO>(config)
    .ToList();

This will involve adding a using clause for "Automapper.QueryableExtensions". The difference between ProjectTo and Map is that with Map, the SQL generation happens with your ToList call. Map would have to resort to lazy loading the data from the TopicMapping class or you would have to set it up to eager-load that data. In both cases this would essentially include all fields from TopicMapping even though we only want 1 field in this example. ProjectTo on the other hand lets Automapper in on the SQL generation. The resulting query would only return the fields that the DTO needs from the Topic and TopicMapping tables, nothing more. No need to eager load related entities, and it produces much more streamlined queries.

Disclaimer: From what I have read, IQueryable and Automapper's ProjectTo should be supported with Linq2SQL. If you encounter issues I would strongly recommend considering using Entity Framework rather than Linq2SQL as it has full support for projection, eager, and lazy loading.

Upvotes: 2

Related Questions