Reputation: 97
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
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