-
Notifications
You must be signed in to change notification settings - Fork 42
DTO data copying
GenericServices uses AutoMapper's projection method to do the database-to-DTO or DTO-to-database copying of data. AutoMapper uses a convention-based mapping that tries to link up by name and type.
AutoMapper is really sophisticated with many options which you can read about in the AutoMapper Wiki. However to save you time I will list the ones that are most useful:
-
Match by name/Type. If you have an
int
property in your databases class calledCustomerID
and anotherint
property calledCustomerId
in your DTO then it will copy the data (see note below for exception). -
Flattening. If a database class called
Product
has a relationship such that it can access a property by writingProductCategory.Name
then adding the same name without the full stop, i.e.ProductCategoryName
, with the right type to the DTO will access that property. -
Aggregation. AutoMapper allows you some simple, but powerful aggregations, with the most useful being Count. If I have a property called
Orders
, which is a collection of all orders placed by a customer then having a property calledOrdersCount
in the DTO will return the number of orders. (see note 2 below)
NOTES:
- When coping data from a DTO to a database class if a DTO property has the GenericServices attribute called
[DoNotCopyBackToDatabase]
that property will NOT be copied back to the database class. This is a very important attribute as allows the developer to stop certain properties from being updated by the user, e.g. MySalary. This provides a similar capability to the[Bind()]
attribute on MVC action parameters. - When using AutoMapper's Aggregation I have found that xxxCount(), xxxAny(), xxxMax() etc. work, but for some reason xxxSum() does not.
Calculated properties, like public string FullName { get { return FirstName + " " + LastName ; } }
are normally done by using the [Computed]
attribute and the DelegateDecompiler option (see Calculated Properties). However there are reasons why you may not want to, or be able to use the DelegateDecompiler option. They are:
- There are a few cases which the DelegateDecompiler cannot handle, such as parameters that need a external variable, e.g. a comparison with some user-provided variable.
- You have decided that you do not want to use the DelegateDecompiler as it may interfere with your business logic, i.e. you may need to add the
.DecompileAsync()
method to EF accesses that use that a[Computed]
parameter. Not very likely, but you might have some reason you don't want to use the DelegateDecompiler.
In these cases AutoMapper's .ForMember
mapping can to be used. The EfGenericDto/EfGenericDtoAsync classes provides a property called AddedDatabaseToDtoMapping
which can be overridden to add .ForMember
LINQ statements that will fill in given property. Below is an example which sets the property IsOnSale
to true using a local variable called _today
which has been set to DateTime.Now
when the DTO was created.
protected override Action<IMappingExpression<Product, ListProductDto>> AddedDatabaseToDtoMapping
{
get
{
return m => m.ForMember(d => d.IsOnSale,
opt => opt.MapFrom(c =>
c.SellStartDate < _today &&
_today <= (c.SellEndDate ?? _today)));
}
}
In this case there is only one .ForMember
method, but they can be chained, e.g.
protected override Action<IMappingExpression<Post, PostSpecialMappingDto>>
AddedDatabaseToDtoMapping
{
get { return
m => m.ForMember(p => p.Bigger,
opt => opt.MapFrom(x => x.SomeProp > localVariable))
.ForMember(p => p.Smaller,
opt => opt.MapFrom(x => x.SomeProp < anotherLocalVariable)); }
}
Live example web sites!
Introduction/Overview
Commands and options
- Introduction to Commands
- Key interfaces
- Calculated properties
- DoNotCopyBackToDatabase attribute
- Configuration Options
Data Transfer Objects (DTOs)