For our examples we will use the CarDTO and CategoryDTO classes:
public class CarDTO
{
public int CarID { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string TagNumber { get; set; }
public CategoryDTO Category { get; set; }
}
public class CategoryDTO
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
You can easily employ AutoMapper in order to map a graph of your persistent entities to DTOs. All you need to do is create the mapping configuration between the respective types and call the Map method for the entity or entities you wish to map to DTOs:
using (EntitiesModel context = new EntitiesModel())
{
Mapper.CreateMap<Car, CarDTO>();
Mapper.CreateMap<Category, CategoryDTO>();
var carDtos = Mapper.Map<IList<CarDTO>>(context.Cars.ToList());
}
Short and simple, however there is a detail that can be easily missed - navigation properties are lazy loaded. As AutoMapper can map graphs of objects, this means that the Map method will make additional calls to the database in order to retrieve the Categories related to the Cars. The result is behavior akin to the N+1 problem and a possible negative effect on the performance of your application.
To prevent such undesired behavior you can define and use a FetchStrategy which specifies which navigation properties must be loaded together with the entity:
using (EntitiesModel context = new EntitiesModel())
{
Mapper.CreateMap<Car, CarDTO>();
Mapper.CreateMap<Category, CategoryDTO>();
FetchStrategy loadCarWithCategory = new FetchStrategy();
loadCarWithCategory.LoadWith<Car>(car => car.Category);
context.FetchStrategy = loadCarWithCategory;
var carDtos = Mapper.Map<IList<CarDTO>>(context.Cars.ToList());
}
If you are using AutoMapper you are probably familiar with its IQueryable extension methods. They allow you to directly retrieve only the required information and project it into a DTO directly in your LINQ query:
using (EntitiesModel context = new EntitiesModel())
{
Mapper.CreateMap<Car, CarDTO>();
Mapper.CreateMap<Category, CategoryDTO>();
IList<CategoryDTO> categories = this.context.Categories.Project().To<CategoryDTO>().ToList();
}
Eventually in your application you would need to convert the used DTOs back to entities. Those entities may be new and need to be persisted in the database or contain updates for already persisted objects. The entities received from DTOs however are not being managed by an OpenAccessContext instance. While you can use the Add method to persist the new entities, you would not be able to handle with it entities which carry an update for an already persisted object.
Telerik Data Access provides you a way to handle both of those scenarios using the same approach – the AttachCopy method:
using (EntitiesModel context = new EntitiesModel())
{
Mapper.CreateMap<Category, CategoryDTO>().ReverseMap();
CategoryDTO sourceCategoryDto = new CategoryDTO()
{
CategoryID = 1,
CategoryName = "Space Car"
};
Category targetCategoryEntity = Mapper.Map<Category>(sourceCategoryDto);
Category attachedCar = null;
if (targetCategoryEntity != null)
{
attachedCar = context.AttachCopy(targetCategoryEntity);
context.SaveChanges();
}
}
The context object will automatically determine whether the attached entity is new or if it is an already existing one. In case the attached entity is new it would be persisted in the database when SaveChanges is called, if it is an already existing object it will use it to push possible updates to the database. In case you are interested in learning more about the AttachCopy method, you can do so here.
With these simple tips in mind AutoMapper would be easy to integrate with Telerik Data Access and could greatly ease the development of your application. Do not forget to regularly check out our blog - next week we will present you our powerful bulk select capabilities for loading multiple objects based on a list of values with minimum amount of database calls.