I have a domain model that contains member variables for two languages, something like this:
public class Resource
{
public string SwedishName;
public string EnglishName;
}
For presentation I have a simplified model, that is delivered to a json serializer:
[JsonObject]
public class JsonResource
{
[JsonProperty]
public string Name;
}
These are mapped with automapper like so:
Mapper.CreateMap<Resource, JsonResource>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.SwedishName));
My question is, if this is possible to do in a more conditional way, depending on which language is asked for? My initial thought, was something along these lines:
string lang = "en";
json = Mapper.Map<Resource, JsonResource>(resource, lang)
Though, it does not seem possible to have several mappings for the same types?
Currently Im leaning towards, just defining another identical presentation model for the other language:
if (lang == "en")
json = Mapper.Map<Resource, EnglishJsonResource>(resource)
else
json = Mapper.Map<Resource, JsonResource>(resource)
Is this a feasible solution, or is there a better way?
I would not create separate classes. Use AfterMap:
Mapper.CreateMap<Resource, JsonResource>()
.AfterMap((r,b) => r.Name = isEnglish ? b.EnglishName : b.SwedishName);
isEnglish is the condition in your app, however you need to use it.
You could create two different classes EngMapper and SimpleMapper which will implement the IMapper interface with a method initMapping for example after that you can create a factory in order to get the right mapper according to your language. So in the end your mapping will be separated for the different languages( that is better in my opinion ).
Related
Can I use Mapster.Tool to generate Mappers without also generating the class that I'm mapping to? I have a typical Domain objects to and from DTOs scenario but the sample code here
https://github.com/MapsterMapper/Mapster/tree/master/src/Sample.CodeGen
and the documentation here
https://github.com/MapsterMapper/Mapster/wiki/Mapster.Tool
both focus on generating the DTOs from the Domain objects, either by annotating them with attributes or using configuration. There is configuration to create DTOs that are CRU specific but I'd still rather create my own DTOs but not have to create my own mappings.
Yes you can - take a look at the interface based code gen documented here.
This allows you to define an interface that will generate a mapper based on existing classes.
From there you can choose how to consume the mapper that's generated. Register in services and then use DI being one way.
Here is a quick example:
[Mapper]
public interface IContactMapper
{
ContactDetailVm MapTo(Contact contact);
}
Would result in
public partial class ContactMapper : IContactMapper
{
public ContactDetailVm MapTo(Contact p2)
{
return p2 == null ? null : new ContactDetailVm()
{
Id = p2.Id,
Created = p2.Created,
LastUpdate = p2.LastUpdate,
Title = p2.Title,
FirstName = p2.FirstName,
LastName = p2.LastName,
PreferredName = p2.PreferredName,
BirthYear = p2.BirthYear
};
}
}
I don't believe you can use the tool to generate the mapping extension methods for existing entities however. At least im not aware it can be done in v6.
Automap the dto with subDto
class productsDTO
{
public int id;
public AddressDTO DeliveryAddress;
}
Class productsViewModel
{
public int id;
public AddressViewModel DeliveryAddress;
}
Here, I have one dto class. I just want to auto map the dTo class into view model. In DTO class has AddressDTo that has to be automatically mapped AddressViewModel in productsViewModel.
If anyone have solution just post
var products = [some objects];
products.ForEach(a =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ProductsDTO, ProductsViewModel>()
.ForMember(dest => dest.DeliveryAddress, opts =>opts.Ignore())
.AfterMap((src, dest) => {
dest.DestinationAddress =
Mapper.Map(src.DeliveryAddress,dest.DeliveryAddress);
});
});
IMapper iMapper = config.CreateMapper();
var productList = iMapper.Map<ProductsDTO, ProductsViewModel>(a);
products.add(productList)
});
This code produce an error like this : "Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance."
You need to add a second mapping, between AddressDTO and AddressViewModel. When when you map between productsDTO and productsViewModel it will automatically map those properties as well, and AutoMapper will know how the mapping should work.
In the same way you mapped productsDTO and productsViewModel, you can map these classes too:
cfg.CreateMap<AddressDTO, AddressViewModel>();
Also, it should not be necessary to define your mappings multiple times inside a loop, as you are doing now. The mapping is simply a definition, to be used later. It only needs to be specified once. When you want to actually carry out a mapping of two objects, then use Mapper.Map - use this as many times as you have objects. It will use the mapping definition you created beforehand. But cfg.CreateMap only needs to be called once for each combination of classes. I'm also not sure you need the whole "Ignore" business in your code - it seems redundant to ignore a property and then map it again on the next line. You can also convert all the objects in the products list in a single Map operation.
Something like this should work better, I would think:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ProductsDTO, ProductsViewModel>();
cfg.CreateMap<AddressDTO, AddressViewModel>();
});
IMapper iMapper = config.CreateMapper();
var products = [some objects];
List<ProductViewModel> productVMList = iMapper.Map<List<ProductViewModel>>(products);
Firstly, You map AddressDTO and AddressViewModel properties.If you give same property name AutoMapper mapped automatically. If you wanna to map properties with different name, You define which property equals with other...
Docs ;
http://docs.automapper.org/en/stable/Projection.html
After You will mapped productsDTO and productsViewModel and Everythings work fine.. :)
I'm using AutoMapper to map from IDataReader to a simple DTO.
I'm able to map the properties when I use ForMember, but not when I use ConstructUsing/ConvertUsing. In this case, all my NUnit tests fail, because the AutoMapper returns a DTO with null properties.
What's interesting is that this behavior does not occur in MSTest: When running the tests under MSTest, the mapping works.
Here's the code:
public class Dto
{
public string Name { get; set; }
public string Value { get; set; }
}
This passes in NUnit and in MSTest:
Mapper.CreateMap<IDataReader, Dto>()
.ForMember(x => x.Name, map => map.MapFrom(reader => reader["Name"]))
.ForMember(x => x.Value, map => map.MapFrom(reader => reader["Value"]));
This passes only in MSTest and returns Dto with null properties in NUnit:
Mapper.CreateMap<IDataReader, Dto>()
.ConvertUsing(Map); // ConstructUsing doesn't work either
private Dto Map(IDataReader reader)
{
return new Dto
{
Name = (string)reader["Name"],
Value = (string)reader["Value"]
};
}
MyTestMethod is not even called in NUnit.
Is this a bug in AutoMapper? In NUnit? Both?
Should I not use AutoMapper for IDataReader mapping?
Thanks in advance.
After I accidentally stumbled upon this question,
https://groups.google.com/forum/#!topic/automapper-users/3DcPbP-GgNg
I figured out that this has nothing to do with NUnit/MsTest and was simply caused due to AutoMapper.Net4.DLL being in my project.
This DLL contains DataReaderMapper, which overrode my custom IDataReader mapping.
Since my real project columns did not match the object's properties names, DataReaderMapper simply returned null.
(As for NUnit/MsTest, I had different projects, and didn't suspect the extra DLL in one of them. After narrowing the problem I was able to reproduce it on both frameworks.)
Removing AutoMapper.Net4.DLL from my project solved the problem.
Sorry for the misleading question :)
I'm building CMS like application.
For example my BlogPost page contains several widget areas. Each widget hosts a serie of "related" blog posts.
All my views are pure presentational, i build urls, convert datetime and ints into strings in my service layer. I find this approach easier to maintain, since views have zer0 logic. All logic is consolidated into AutoMapper's resolvers, converters and custom transformation logic.
So lets come closer to the problem at hand.
To create Url i need 2 parameters: BlogId and BlogSlug, my urls look like b/{id}/{slug}.html
Im quite happy with that.
In my CSM i use so called "source models", a model that is not view model, but an intermediate representation of it. Why do i have to resort to such wicked solutions ?
Well, lets take a look how a typical data retrieval code can look like in my project:
.Select(x =>
{
Id = x.Id,
BlogId = x.Blog.Id,
BlogSlug = x.Blog.Slug,
// Here is the trap, LINQ provider will throw an exception, since he doesn't know how to translate function into expression
BlogUrl = Url.Action("RenderPost", "BlogController", new { Id = x.Blog.Id, slug = x.Blog.Slug })
}
So thats not an option.
Luckily, we can do this
.Select(x => new
{
Id = x.Id,
BlogId = x.Blog.Id,
BlogSlug = x.Blog.Slug
}
.ToList()
.Select(x => new
{
// This works
BlogUrl = Url.Action("RenderPost", "BlogController", new { Id = x.BlogId, slug = x.BlogSlug })
}
Copy paste this stuff into each and every action method that renders different "intresting blog" parts (they have different visual representation as well cant use same view model) ? Not a good way, so i came up with a solution.
I created "source model", so the code will be
.Select(x => new BlogPostSourceViewModel
{
Id = x.Id,
BlogId = x.Blog.Id,
BlogSlug = x.Blog.Slug
}
.ToList()
.Select(x => x.ToBlogPostViewModel()) // Extension method { return Mapper.Map<>() }
.ToList();
This surely looks better, but i have many different models like BlogPostSourceViewModel, BlogAuthorSourceViewModel, BlogCommentSourceViewModel. They all need this link building logic.
Ok, i extract the needed source data (BlogId, BlogSlug) into an interface
BlogPostSourceViewModel : IBlogPostUrl
BlogAuthorSourceViewModel: IBlogPostUrl
BlogCommentSourceViewModel : IBlogPostUrl
Then i define the mappings
Mapper.CreateMap<BlogPostSourceViewModel, BlogPostViewModel>
.ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
Mapper.CreateMap<BlogAuthorSourceViewModel, BlogAuthorViewModel>
.ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
Mapper.CreateMap<BlogCommentSourceViewModel, BlogCommentViewModel>
.ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
Resolver:
BlogPostUrlResolver : ValueResolver<IBlogPostUrl, String>
// Here goes the url building logic
As you see the more models i have that need blog url the more identical mappings i have to add. This is ok for now, but as project grows it will be painful.
Ideally i would want to have it like this:
Mapper.CreateMap<IBlogPostUrl, SomeOtherInterfaceWithBlogUrlAsString>
.ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
but Automapper doesn't understand it. And i dont know how if there is other way to do it.
Any ideas ?
If I understand the question correctly...
In order to use AutoMapper mapping capabilities across all of your classes you can provide base class that will use Generics:
public abstract class Base<Entity, ViewModel>
where Entity : EntityObject
where ViewModel : BaseViewModel
{
// you will call this method from your operations class using base
public SomeViewModel GetData()
{
public Entity entityObject = db.Entity.SingleOrDefault();
public ViewModel yourViewModelName = base.Map(entityObject);
return yourViewModelName;
}
....
// this will be defined only once for each mapping direction
// ie. there will be multiple of these.
public static Entity Map(ViewModel typeViewModel)
{
try
{
Mapper.CreateMap<ViewModel, Entity>();
Entity t = Mapper.Map<ViewModel, Entity>(typeViewModel);
return t;
}
catch (Exception exc)
{
throw exc;
}
}
}
// this will be your class for database operations on specific Entity
// that inherits generic base, with its AutoMapping setup.
public class DataBaseOperationsClass : Base<SomeEntity, SomeViewModel>
{
public SomeViewModel Get()
{
return base.GetData();
}
}
Hope this helps !
If I have the following class:
class SPUser
{
public int ID { get; set; }
public string Name { get; set; }
public string LoginName { get; set; }
public string Email { get; set; }
public bool IsSiteAdmin { get; set; }
public bool IsSiteAuditor { get; set; }
public bool IsDomainGroup { get; set; }
public List<SPGroup> Groups { get; set; }
}
And I am using the sharepoint web services, which return an XML with an attribute for each property on my class, such as:
<Users>
<User Name="name" Description="desc" ..... />
</Users>
Is there any way to use AutoMapper to map the XML fragment to an SPUser class instance?
Blog has been deleted - here's the Bing archive of the post by #DannyDouglass
Simplify Using Xml Data With AutoMapper and Linq-to-Xml
I recently ran into a scenario at work that required manually consuming several SOAP web services, which I’m sure you can imagine was rather monotonous. A co-worker (Seth Carney) and I tried a few different approaches, but we finally settled on a solution that simplified consumption of the xml and ultimately made the code more testable. That solution centered around leveraging AutoMapper, an open source object-object mapping tool, to create a link between the XElements(http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.aspx) returned in the SOAP messages and custom contracts we created – in a reusable manner.
I put together a quick demo that shows how you could use the same approach to consume and display the Twitter Public Timeline (http://api.twitter.com/1/statuses/public_timeline.xml) (using the API’s Xml response type).
Note: The source code for the following example can be found on my GitHub page: https://github.com/DannyDouglass/AutoMapperXmlMappingDemo
Getting the Project Setup
After creating a basic MVC3 (download beta) project and the associated test project, the first step was to get the AutoMapper package installed. I have been using NuGet, Microsoft’s recently announced package management system, to install any open source dependencies. The following command was all that was needed to setup AutoMapper in my MVC3 project (read more about NuGet here(http://weblogs.asp.net/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx) and here(http://weblogs.asp.net/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx)):
PM> add-package AutoMapper
Creating the Mapping
With AutoMapper installed I’m ready to get started creating the components necessary for the xml-to-object mapping. The first step is creating a quick contract used in my application to represent the Tweet object:
public interface ITweetContract
{
ulong Id { get; set; }
string Name { get; set; }
string UserName { get; set; }
string Body { get; set; }
string ProfileImageUrl { get; set; }
string Created { get; set; }
}
Nothing crazy here – just a simple entity. These are all fields that are provided in the response from the Twitter API using a different name for some fields. In simple cases where the source and destination objects have the same name you can setup a map very quickly using this syntax:
Mapper.CreateMap<SourceObj, DestinationObj>();
However, AutoMapper does not support Xml by default I have to specify the fields that I will be mapping. Using the Fluent API in AutoMapper I’m able to chain my field mappings. Take a look at one example field mapped in my example – the tweet’s Body:
Mapper.CreateMap<XElement, ITweetContract>()
.ForMember(
dest => dest.Body,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("text")))
It may look complicated at first, but all that is really happening here is that we are providing details to AutoMapper on what value to use in my source object and how to map it to the destination object’s property. There is one particular line I would like to focus on in the above Body field mapping:
options => options.ResolveUsing<XElementResolver<ulong>>()
.FromMember(source => source.Element("id")))
The XElementResolver is a custom value resolver (http://automapper.codeplex.com/wikipage?title=Custom%20Value%20Resolvers) that Seth came up with to handle parsing the XmlElement source object to retrieve a strongly-typed value for use in the mapping. I’ll detail that more in a moment, but before we move on take a look at my full mapping:
Mapper.CreateMap<XElement, ITweetContract>()
.ForMember(
dest => dest.Id,
options => options.ResolveUsing<XElementResolver<ulong>>()
.FromMember(source => source.Element("id")))
.ForMember(
dest => dest.Name,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("name").Single()))
.ForMember(
dest => dest.UserName,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("screen_name").Single()))
.ForMember(
dest => dest.Body,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("text")))
.ForMember(
dest => dest.ProfileImageUrl,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("profile_image_url").Single()))
.ForMember(
dest => dest.Created,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("created_at")));
The Generic XElementResolver
This custom value resolver is the real key that allowed these XElement-to-Contract maps to work in the original solution. I’ve reused this resolver in this example as we saw above. This was all that was necessary to create the custom resolver class:
public class XElementResolver<T> : ValueResolver<XElement, T>
{
protected override T ResolveCore(XElement source)
{
if (source == null || string.IsNullOrEmpty(source.Value))
return default(T);
return (T)Convert.ChangeType(source.Value, typeof(T));
}
}
This generic XElementResolver allows use to easily pass the type of the value retrieved in our mapping above. For example, the following syntax is used to strongly type the value retrieved from the XmlElement in the Id field’s .ForMember() declaration above:
ResolveUsing<XElementResolver<ulong>>()
With my mapping completely configured and instantiated, I’m ready to invoke the Twitter API and leverage AutoMapper to display that latest Public Timeline.
Putting the Pieces Together
I created a simple class responsible for retrieving the Twitter API response:
public class TwitterTimelineRetriever
{
private readonly XDocument _twitterTimelineXml;
public TwitterTimelineRetriever()
{
_twitterTimelineXml = XDocument
.Load("http://api.twitter.com/1/statuses/public_timeline.xml");
}
public IEnumerable<ITweetContract> GetPublicTimeline(int numberOfTweets)
{
var tweets = _twitterTimelineXml.Descendants("status")
.Take(numberOfTweets);
return tweets.Select(Mapper.Map<XElement, ITweetContract>).ToList();
}
}
The GetPublicTimeline method is a simple method returning, you guessed it, the Twitter Public Timeline by leveraging the map we created earlier:
return tweets.Select(Mapper.Map<XElement, ITweetContract>).ToList();
In my MVC3 site’s HomeController I can make a quick call to the retrieval method, requesting the last 10 results:
public class HomeController : Controller
{
private TwitterTimelineRetriever _twitterTimelineRetriever;
public ActionResult Index()
{
_twitterTimelineRetriever = new TwitterTimelineRetriever();
ViewModel.Message = "Twitter Public Timeline";
return View(_twitterTimelineRetriever.GetPublicTimeline(10));
}
}
And finally, after a little formatting in my View using the new Razor view engine from Microsoft, I have my public timeline displaying!
You need to check out XML serialization in .NET for this - that's the way to serialize an object to XML or deserialize it from XML.
Automapper can be used to set properties between two objects - it doesn't deal with XML at all.
More resources:
C# Tutorial - XML Serialization
Bit late in the day, but someone has used AutoMapper to map XML to a POCO rather than going down the XMLSerialization route. I found the following blog entry:-
Simplify Using Xml Data with AutoMapper and Linq-to-Xml
This is enough to get you started on implementing a generic custom resolver of your own if the example one is not enough.
EDIT: fixed link
EDIT: really fixed link
You can user XML deserialization for this purposes. In .NET we have XmlSerializer and DataContractSerializer