How do I translate complex objects in ServiceStack? - c#

Suppose I have two objects:
class Order
{
string Name {get; set;}
Customer Customer {get; set;}
Item[] Items {get; set;}
}
and
class OrderDTO
{
string Name {get; set;}
CustomerDTO Customer {get; set;}
ItemDTO[] Items {get; set;}
}
If I receive an object orderDTO that is fully populated and do orderDTO.TranslateTo<Order>() the result will only have Name populated, not Customer or Items. Is there a way to do a recursive translation or the only option is to translate Customer and each of the Items manually?

I would wrap this in a re-usable extension method, e.g:
public static OrderDTO ToDto(this Order from)
{
return new OrderDTO {
Name = from.Name,
Customer = from.ConvertTo<CustomerDTO>(),
Items = from.Items.Map(x => x.ConvertTo<ItemDTO>()).ToArray(),
}
}
Which can then be called whenever you need to map to an Order DTO, e.g:
return order.ToDto();
Some more examples of ServiceStack's Auto Mapping is available on the wiki.
ServiceStack's party-line is if your mapping requires more than the default conventional behavior that's inferable from an automated mapper then you should wrap it behind a DRY extension method so the extensions and customizations required by your mapping are cleanly expressed and easily maintained in code. This is recommended for many things in ServiceStack, e.g. maintain un-conventional and complex IOC binding in code rather than relying on an obscure heavy-weight IOC feature.
If you prefer it, you can of course adopt a 3rd party tool like AutoMapper.

You are going to have to handle complex mapping explicitly yourself. Here are some unit tests from ServiceStack src that show how complex types are currently handled.
You can see that the the Car object is serialized into JSON.
var user = new User() {
FirstName = "Demis",
LastName = "Bellot",
Car = new Car() { Name = "BMW X6", Age = 3 }
};
var userDto = user.TranslateTo<UserDto>();
Assert.That(userDto.FirstName, Is.EqualTo(user.FirstName));
Assert.That(userDto.LastName, Is.EqualTo(user.LastName));
Assert.That(userDto.Car, Is.EqualTo("{Name:BMW X6,Age:3}"));
I agree with Trust Me - I'm a Doctor that Automapper is worth using. The built in Translating was designed to reduce dependencies.

Related

automapper with runtime mapping configuration

In my ASP.NET MVC application I need to implemenet mapping from one object to another with some kind of UI for mapping configuration in runtime, so the user can define mapping "on the go". Is there any libraries that supports such functionality?
Description
This is objects in my application. I need to somehow allow user to configure mapping of this objects via UI during application runs. For exmaple some kind of page in my application where user will be able to define mapping in simple way like so map Amout of OrderDTO to Order Qty and later without application recompile change this mapping for exmaple for ExactAmmount
//Object in DAL
public class Order
{
public int Id {get; set;}
public string Name {get; set;}
public decimal Qty {get; set;}
//Lots of other fields
}
//Object from XSD generation (for example)
public class OrderDTO
{
public int Id {get; set;}
public string Description {get; set;}
public decimal Ammout {get; set;}
public decimal VAT {get; set;}
public decimal ExactAmmount {get; set;}
//Lots of other fields
}
Note: for legacy reasons I based this answer on AutoMapper 4.2.1 instead of the current 5.x version. The overall approach should be similar with the new version.
It is possible to create different mapping configurations and different mappers within a program. Also, it is possible to create member mappings by member names (string) instead of lambda expressions. However, some static type information is still necessary (as far as my example goes).
See the following example of a profile, that prepares a custom mapping based on property names:
class MemberProfile : Profile
{
private string from;
private string to;
public MemberProfile(string from, string to)
{
this.from = from;
this.to = to;
}
protected override void Configure()
{
this.CreateMap<Order, OrderDTO>()
.ForMember(to, c => c.MapFrom<decimal>(from));
}
}
This could be extended to support different source property types and a collection of custom mappings instead of a single one.
Usage example:
var order = new Order() { Id = 1, Name = "Test", Qty = 0.5m };
var conf1 = new MapperConfiguration(c => c.AddProfile(new MemberProfile("Qty", "Ammout")));
var conf2 = new MapperConfiguration(c => c.AddProfile(new MemberProfile("Qty", "ExactAmmount")));
var res1 = conf1.CreateMapper().Map<OrderDTO>(order);
var res2 = conf2.CreateMapper().Map<OrderDTO>(order);
For res1, Qty is mapped to Ammout and for res2, Qty is mapped to ExactAmmount. Since the difference is described as string property names, it should be possible to let the user influence this configuration.

Add OData to Web API 2 without coupling clients and server

I want to add OData syntax for querying my application's data. I don't want to fully implement ODataController, but have ApiController and implement one GET action that will support OData queries, as described here: Supporting OData Query Options in ASP.NET Web API 2
Example of what I want to have:
public class LetterEntity
{
public int Id {get; set;}
public string Title {get; set;}
public string Content {get; set;}
public string Source {get; set;}
public DateTime SendingTime {get; set;}
public string AnotherWierdString {get; set;
...
}
public class LetterDTO
{
public int Id {get; set;}
public string Title {get; set;}
public string LetterContent {get; set;}
public string Source {get; set;}
public DateTime SendingTime {get; set;}
}
public class LetterInsideFolderDTO
{
public string Title {get; set;}
public string Source {get; set;}
}
public class LettersController : ApiController
{
// Is there a way to do something like the following?:
[HttpGet]
[Route("api/letters")]
[EnableQuery]
public IQueryable<LetterInsideFolderDTO> Get(ODataQueryOptions<LetterDTO> query)
{
IQueryable<Letter> letters = db.Letters;
var queryOnEntites = // Convert the query to work on the entities somehow? - This is where I need help!!
var afterQuery = query.ApplyTo(letters)
IQueryable<LetterInsideFolderDTO> dtos = afterQuery.ProjectTo<LetterInsideFolderDTO>(afterQuery)
return dtos;
}
}
Because of the fact that at the moment I take Entity model directly in the clients query, there is a strong coupling between clients and server.
For example if i want to query and get all the letters that has "abc" inside the Content field, I need to route to the following:
api/letters/?$filter=contains(Content,'abc')
If tomorrow I decide to change that property from "Content" to "LetterContent" all clients code will be broken.
How can I surpass it?
I have not tried it. This and this can help you to map the query url to actual controller by using router map and controller selector where you can map LetterDto to LetterEntity
This will work for Entity Framework (not sure for Nhibrenate but probably will) and will do a real SQL query not in memory filtering.
var queryOnEntites = db.Letters.Select(l=>new LetterDTO{Id =l.Id ... });
var afterQuery = query.ApplyTo(queryOnEntites);
But you shouldn't use DTOs with odata if you want to make some properties private to the API use builder.EntitySet<T1>("TEndpoint").EntityType.Ignore(o => o.SomeProp);
Now if you don't want the whole LetterEntity sent to client you have the $select=Id,... option for this purpose.
If you don't setup
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
you don't have an Odata endpoint and $metadata won't be available.
I haven't tested it but this way I don't think the client libraries(.net,java,js...) would work and you will have to make raw ajax requests to get the data.
ODataQueryOptions<> and translate it to
ODataQueryOptions<>
You can't do this. ProjectTo(AutoMapper) function dose the same thing as .Select(l=>new LetterDTO{Id =l.Id ... }); but you will have problems with this. This all is dependent on your back end since IQueryable is not the same as IEnumerable.
http://blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/
This all depends on what level of LINQ your back end provides (NHibrenate tends to be worse then EF, and if you are using Mongo, Elastic, Cassandra... who knows what might go wrong when you use AutoMapper)
If tomorrow I decide to change that property from "Content" to
"LetterContent" all clients code will be broken.
You can setup Name property on an Entity set with oData. Remember Odata is data access level not BL. You should treat it the same way as changing SQL column name in the database.
Instead of exposing your entity models directly, create ViewModels corresponding to each entity model. They are nothing but simple classes having same or only required properties which we want to be exposed to the outer world. Having implemented this way, your entity model properties may change but ViewModels can remain unchanged. You are also addressing the security aspect of not exposing every property of your entity to end clients.
Mapping between entity model and ViewModel has to be done by yourself or by an object-object mapper like AutoMapper.

Mapping a small properties object to large properties object

I got two objects with some properties as follows:
public class Person
{
public string DoctorId { get; set ;}
public string DoctorName { get; set;}
public string PersonId { get; set;}
public string PersonName { get; set;}
}
public class SaveRequest
{
public string DoctorName { get; set;}
public string PersonName { get; set;}
public string PersonId { get ; set;}
}
Now as you can see, I want to map the SaveRequest to Person Object I have. We do this by PersonId which is the key and also my requirement as you see is, not all properties are in SaveRequest, its just name of person and doctor and PersonId just for primary key...
Please help me as to how to map these in C# with a small code.
You could try using a mapper like AutoMapper
With automapper, you'll have to
Add a reference using nuGet (PM> Install-Package AutoMapper)
Then create a map using Mapper.Initialize(cfg => cfg.CreateMap<Person, SaveRequest>());
Then, simply use the mapper like so: SaveRequest sr = Mapper.Map<SaveRequest>(person); where person is an instance of Person
More info here (https://github.com/AutoMapper/AutoMapper/wiki/Flattening)
Or you could build your own custom Mapping method which takes in a Person and returns/maps the data to a SaveRequest
Or you could simply use Person and ignore the DoctorId property wherever you intended to use SaveRequest
Automapper is a library that aims to make this type of thing much easier, however it's more suited for use in multiple areas within a project rather than just one isolated case (otherwise it'll take longer to set it up than it's worth). http://automapper.org/
The old school way is to just do it by hand, property by property.
One could write a routine using reflection that would examine property names and match the values, but if you go that far, you might as well just use Automapper, is that's exactly what it does (and a lot more).

Best way to project ViewModel back into Model

Consider having a ViewModel:
public class ViewModel
{
public int id { get; set; }
public int a { get; set; }
public int b { get; set; }
}
and an original Model like this:
public class Model
{
public int id { get; set; }
public int a { get; set; }
public int b { get; set; }
public int c { get; set; }
public virtual Object d { get; set; }
}
Each time I get the view model I have to put all ViewModel properties one by one into Model. Something like:
var model = Db.Models.Find(viewModel.Id);
model.a = viewModel.a;
model.b = viewModel.b;
Db.SaveChanges();
Which always cause lots of problems. I even sometimes forget to mention some properties and then disaster happens!
I was looking for something like:
Mapper.Map(model, viewModel);
BTW: I use AutoMapper only to convert Model to ViewModel but vice-versa I always face errors.
Overall that might be not the answer, that you are looking for, but here's a quote from AutoMapper author:
I can’t for the life of me understand why I’d want to dump a DTO
straight back in to a model object.
I believe best way to map from ViewModel to Entity is not to use AutoMapper for this. AutoMapper is a great tool to use for mapping objects without using any other classes other than static. Otherwise, code gets messier and messier with each added service, and at some point you won't be able to track what caused your field update, collection update, etc.
Specific issues often faced:
Need for non-static classes to do mapping for your entities
You might need to use DbContext to load and reference entities, you might also need other classes - some tool that does image upload to your file storage, some non-static class that does hashing/salt for password, etc etc... You either have to pass it somehow to automapper, inject or create inside AutoMapper profile, and both practices are pretty troublemaking.
Possible need for multiple mappings over same ViewModel(Dto) -> Entity Pair
You might need different mappings for same viewmodel-entity pair, based on if this entity is an aggregate, or not + based on if you need to reference this entity or reference and update. Overall this is solvable, but causes a lot of not-needed noise in code and is even harder to maintain.
Really dirty code that's hard to maintain.
This one is about automatic mapping for primitives (strings, integers, etc) and manual mapping references, transformed values, etc. Code will look really weird for automapper, you would have to define maps for properties (or not, if you prefer implicit automapper mapping - which is also destructive when paired with ORM) AND use AfterMap, BeforeMap, Conventions, ConstructUsing, etc.. for mapping other properties, which complicates stuff even more.
Complex mappings
When you have to do complex mappings, like mapping from 2+ source classes to 1 destination class, you will have to overcomplicate things even more, probably calling code like:
var target = new Target();
Mapper.Map(source1, target);
Mapper.Map(source2, target);
//etc..
That code causes errors, because you cannot map source1 and source2 together, and mapping might depend on order of mapping source classes to target. And I'm not talking if you forget to do 1 mapping or if your maps have conflicting mappings over 1 property, overwriting each other.
These issues might seem small, but on several projects where I faced usage of automapping library for mapping ViewModel/Dto to Entity, it caused much more pain than if it was never used.
Here are some links for you:
Jimmy Bogard, author of AutoMapper about 2-way mapping for your entities
A small article with comments about problems faced when mapping ViewModel->Entity with code examples
Similar question in SO: Best Practices For Mapping DTO to Domain Object?
For this purpose we have written a simple mapper. It maps by name and ignores virtual properties (so it works with entity framework). If you want to ignore certain properties add a PropertyCopyIgnoreAttribute.
Usage:
PropertyCopy.Copy<ViewModel, Model>(vm, dbmodel);
PropertyCopy.Copy<Model, ViewModel>(dbmodel, vm);
Code:
public static class PropertyCopy
{
public static void Copy<TDest, TSource>(TDest destination, TSource source)
where TSource : class
where TDest : class
{
var destProperties = destination.GetType().GetProperties()
.Where(x => !x.CustomAttributes.Any(y => y.AttributeType.Name == PropertyCopyIgnoreAttribute.Name) && x.CanRead && x.CanWrite && !x.GetGetMethod().IsVirtual);
var sourceProperties = source.GetType().GetProperties()
.Where(x => !x.CustomAttributes.Any(y => y.AttributeType.Name == PropertyCopyIgnoreAttribute.Name) && x.CanRead && x.CanWrite && !x.GetGetMethod().IsVirtual);
var copyProperties = sourceProperties.Join(destProperties, x => x.Name, y => y.Name, (x, y) => x);
foreach (var sourceProperty in copyProperties)
{
var prop = destProperties.FirstOrDefault(x => x.Name == sourceProperty.Name);
prop.SetValue(destination, sourceProperty.GetValue(source));
}
}
}
I want to address a specific point in your question, regarding "forgetting some properties and disaster happens". The reason this happens is that you do not have a constructor on your model, you just have setters that can be set (or not) from anywhere. This is not a good approach for defensive coding.
I use constructors on all my Models like so:
public User(Person person, string email, string username, string password, bool isActive)
{
Person = person;
Email = email;
Username = username;
Password = password;
IsActive = isActive;
}
public Person Person { get; }
public string Email { get; }
public string Username { get; }
public string Password { get; }
public bool IsActive { get; }
As you can see I have no setters, so object construction must be done via constructor. If you try to create an object without all the required parameters the compiler will complain.
With this approach it becomes clear, that tools like AutoMapper don't make sense when going from ViewModel to Model, as Model construction using this pattern is no longer about simple mapping, its about constructing your object.
Also as your Models become more sophisticated you will find that they differ significantly from your ViewModels. ViewModels tend to be flat with simple properties like string, int, bool etc. Models on the other hand often include custom objects. You will notice in my example there is a Person object, but UserViewModel would use primitives instead like so:
public class UserViewModel
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool IsActive { get; set;}
}
So mapping from primitives to complex objects limits AutoMapper's usefulness.
My approach is always manual construction for the ViewModels to Model direction. In the other direction, Models to ViewModels, I often use a hybrid approach, I would manually map Person to FirstName, LastName, I'd but use a mapper for simple properties.
Edit: Based on the discussion below, AutoMapper is better at unflattering than I believed. Though I will refrain from recommending it one way or the other, if you do use it take advantage of features like Construction and Configuration Validation to help prevent silent failures.
Use Newtonsoft.Json to serialize viewmodel first and deserialize it to model.
First we need to Serialize the viewmodel:
var viewmodel = JsonConvert.SerializeObject(companyInfoViewModel);
Then Deserialize it to model:
var model = JsonConvert.DeserializeObject<CompanyInfo>(viewmodel);
Hence, all the data is passed from viewmodel to model easily.
One Line Code:
var company = JsonConvert.DeserializeObject<CompanyInfo>(JsonConvert.SerializeObject(companyInfoViewModel));

NHibernate load the nested complex object entity in automapper queryable extension

I am having class employee which contain complex property Department.
public Class Employee
{
public int Id { get; set;}
public Department {get; set;}
}
public Class Department
{
public int Id {get;set;}
public string Name { get;set;}
}
i create the map for both
(consider the above both class are available in two namespace 'source', 'destination')
Mapper.CreateMap<source.Employee,destination.Employee>()
Mapper.CreateMap<source.Department,destination.Department>()
when i project it.
empQueryable.Project().To<destination.Employee>();
If i saw the NHProfiler
I found that it loads the entity Department
and create the query
select ... from employee left outer join Department .....
i don't know why it loads entity Department, it should make just the projection.
I'm going to go out on a limb here and assume that "destination.Employee" contains references to the "destination.Department". When AutoMapper builds a projection, it does the same as it would as "Mapper.Map". It crawls the destination type, its properties, and its member's properties all the way down. In short, it will build a Select expression something like:
.Select(e => new destination.Employee {
Id = e.Id,
Department = new destination.Department {
Id = e.Department.Id,
Name = e.Department.Name
}
});
You have a few choices here:
Ignore members you don't want mapped in your employee configuration, namely the "Department" property
Create targeted destination types based on use case, and don't share destination types that require different hydrated data based on different needs.
Use explicit expansion (ForMember(d => d.Department, opt => opt.ExplicitExpansion()), then explicitly expand that member in your projection as needed, with the overload for "Project.To" that takes a list of members to expand.

Categories

Resources