I'm using an automapper to flatten the object coming from WS. Simplified model would be as follows:
public abstract class AOrder {
public Product Product {get;set;}
public decimal Amount {get;set;}
//number of other properties
}
public abstract class Product {
//product properties
}
public class RatedProduct : Product {
public int Rate { get;set;}
}
public class MarketOrder : AOrder {
//some specific market order properties
}
Using automapper I'm trying to flatten this into:
public class OrderEntity {
public decimal Amount {get;set;}
public int ProductRate {get;set;}
}
with next mapping:
CreateMap<RatedProduct, OrderEntity>();
CreateMap<MarketOrder, OrderEntity>();
The above mapping will not map the ProductRate.
Atm I've just used the AfterMap:
CreateMap<MarketOrder, OrderEntity>()
.AfterMap((s,d) => {
var prod = s.Product as RatedProduct;
if (prod != null)
{
//map fields
}
});
which works pretty well, but thought if I could reuse the automapper flattening possibilities (i.e. matching by name) I wouldn't need to apply the after map in quite many places.
Note: I can't change the WS and this is just a tiny part from object hierarchy.
Advice appreciated.
Mapping Rate to ProductRate is fairly straight forward with "ForMember"
The one where you have to do a cast to the specific type to see if it is that type is a little trickier but I think the same approach you took is what you might have to do however I don't think you need to do "aftermap". I thought all your destination mappings had to be found OR you need to mark them as ignore of the mapping will fail.
Another thing you could do is just change the OrderEntity.ProductRate to be OrderEntity.Rate. Then it would find it and map it for you except where it was hidden because Product doesn't have a rate (but RatedProducts do).
public class OrderEntity {
public decimal Amount {get;set;}
public int Rate {get;set;} //changed name from ProductRate to just Rate.
}
Mapper.CreateMap<Product, OrderEntity>()
.Include<RatedProduct, OrderEntry>();
Mapper.CreateMap<RatedProduct, OrderEntry>();
SEE: Polymorphic element types in collections
Related
I'm trying to model online shop catalog using Domain Driven Design.
There are three main concepts I have right now: Product, Category, Attribute.
Attribute is a characteristic of a product. For instance things such as color, weight, number of CPU cores etc. There are attributes which possible values are fixed, for instance "condition" - can be new or used. Some of them are within some range of values, for instance "number of CPU cores". Some are freely created like "color".
Category have required attributes which every product within that category needs to have, and optional ones. Categories can have parent categories.
Product belongs to a single category which needs to be a leaf category(no children categories).
Now the problem I have is to model these three concepts as aggregates.
One option is to have three different aggregates: Product, Attribute, Category.
Product will have it's attribute values(each with parent id to Attribute AR). Attribute will be in different types(fixed, freely choosen, range). Category will have a list of IDs of Attributes which are required, and list of IDs
The issue here is that whenever I need to create a new product I would need to check if it has all of the required attributes, check the values, and then store the product. This validation would span three aggregates. Where should it go ? It should be domain service ?
Other option is to have 2 AR. Category, with it's products and Attributes. The issue here is again validation of correct values for a single attribute added to a product. The other huge issue I see here, is that I should fetch the whole aggregate from the repository. Given that category can have hundreds of products, I don't think that's a good idea. However it makes sense as a conceptual whole, as If I would like to delete a category, all of it's products should be deleted as well.
What I am missing here ?
In "Implementing Domain Driven Design", Vaugh Vernon uses the "specification pattern" to handle entity/aggregate validation. Without quoting the entire chapter, you have different possibilities : (Java is used in my example, I hope you get the overall idea)
Validating Attributes / Properties
If it is a simple validation process field by field, then validate each attribute separately inside the setter method.
class Product {
String name;
public Product(String name) {
setName(name);
}
public void setName(String name) {
if(name == null) {
throw new IllegalArgumentException("name cannot be null");
}
if(name.length() == 0) {
throw new IllegalArgumentException("name cannot be empty");
}
this.name = name;
}
}
Validating Whole Object
If you have to validate the whole object, you can use a kind of specification to help you. To avoid having the entity having too much responsibilities (dealing with the state, and validate it), you can use a Validator.
a. Create a generic Validator class, and implement it for your Product Validator. Use a NotificationHandler to deal with your validation error (exception, event, accumulating errors and then sending them ? up to you) :
public abstract class Validator {
private ValidationNotificationHandler notificationHandler;
public Validator(ValidationNotificationHandler aHandler) {
super();
this.setNotificationHandler(aHandler);
}
public abstract void validate();
protected ValidationNotificationHandler notificationHandler() {
return this.notificationHandler;
}
private void setNotificationHandler(ValidationNotificationHandler aHandler) {
this.notificationHandler = aHandler;
}
}
NotificationHandler is an interface, that you could implement given your requirements in term of validation error handling. Here is the interface proposed by Vaugh Vernon :
public interface ValidationNotificationHandler {
public void handleError(String aNotificationMessage);
public void handleError(String aNotification, Object anObject);
public void handleInfo(String aNotificationMessage);
public void handleInfo(String aNotification, Object anObject);
public void handleWarning(String aNotificationMessage);
public void handleWarning(String aNotification, Object anObject);
}
b. Implements this class with a specific validator ProductValidator:
public class ProductValidator extends Validator {
private Product product;
public ProductValidator(Product product, ValidationNotificationHandler aHandler) {
super(aHandler);
this.setProduct(product);
}
private void setProduct(Product product) {
this.product = product;
}
#Override
public void validate() {
this.checkForCompletness();
}
private void checkForCompletness() {
if(product.getName().equals("bad name") && anotherCondition()) {
notificationHandler().handleError("This specific validation failed");
}
...
}
}
And then, you can update your entity, with a validate method, that will call this validator to validate the whole object:
public class Product {
private String name;
public Product(String name) {
setName(name);
}
private void setName(String name) {
if (name == null) {
throw new IllegalArgumentException("Name cannot be null");
}
if (name.length() == 0) {
throw new IllegalArgumentException("Name cannot be empty");
}
this.name = name;
}
// Here is the new method to validate your object
public void validate(ValidationNotificationHandler aHandler) {
(new ProductValidator(this, aHandler)).validate();
}
}
Validating multiple aggregates
And finally, which is your direct concern, if you want to validate multiple aggregates to have something coherent, the recommendation is to create a Domain Service and a specific validator. The domain services can either have injected the repositories to look up for the different aggregates, or I everything is created by the application layers, then inject the different aggregates as method parameter:
public class ProductCategoryValidator extends Validator {
private Product product;
private Category category;
public ProductCategoryValidator(Product product, Category category, ValidationNotificationHandler aHandler) {
super(aHandler);
this.setProduct(product);
this.setCategory(category);
}
private void setCategory(Category category) {
this.category = category;
}
private void setProduct(Product product) {
this.product = product;
}
#Override
public void validate() {
this.checkForCompletness();
}
private void checkForCompletness() {
// Count number of attributes, check for correctness...
}
}
And the domain service that will call the Validator
public class ProductService {
// Use this is you can pass the parameters from the client
public void validateProductWithCategory(Product product, Category category, ValidationNotificationHandler handler) {
(new ProductCategoryValidator(product, category, handler)).validate();
}
// Use This is you need to retrieve data from persistent layer
private ProductRepository productRepository;
private CategoryReposiory categoryReposiory;
public ProductService(ProductRepository productRepository, CategoryReposiory categoryReposiory) {
this.productRepository = productRepository;
this.categoryReposiory = categoryReposiory;
}
public void validate(String productId, ValidationNotificationHandler handler) {
Product product = productRepository.findById(productId);
Category category = categoryReposiory.categoryOfProductId(productId);
(new ProductCategoryValidator(product, category, handler)).validate();
}
}
Like I said, I think you might be interested into the solution 3. As you have guessed it, you can use a Domain Service. But, add a specific validator to ensure the "responsibilities" are not mixed.
The issue here is that whenever I need to create a new product I would need to check if it has all of the required attributes, check the values, and then store the product. This validation would span three aggregates. Where should it go ? It should be domain service ?
The usual answer is that the retrieval of information (aka I/O) is done in an application service. Copies of that information are then passed, like other inputs, into the domain model.
A single "transaction" might include multiple calls to aggregate methods, as we fetch inputs from different places.
These copies of information are generally treated as data on the outside - we have an unlocked copy of the data here; while we are using that copy, the authoritative copy might be changing.
If you find yourself thinking that "the authoritative copy of the data over there isn't allowed to change while I use it over here" - that's a big red flag that either (a) you don't actually understand your real data constraints or (b) that you've drawn your aggregate boundaries incorrectly.
Most data from the real world is data on the outside (Bob's billing address may change without asking your permission - what you have in your database is a cached copy of the Bob's billing address as of some point in the past).
I have my model classes set up with the integer properties just as they are stored in the database. So a sample model might look like:
public class TaskModel
{
public int TaskId { get; set; }
public int TaskStatus { get; set; }
}
But on my actual business classes I want to use enums, so the matching business class would look like:
public class Task
{
public int TaskId { get; set; }
public Status TaskStatus { get; set; }
}
I then want to use Automapper's LINQ projection features to query these business classes, like:
return db.Tasks.Where( t => t.TaskStatus == 1 ).Project().To<Task>();
But when I do this I get this error:
Unable to create a map expression from System.Int32 to MyNamespace.TaskStatus
I've been able to resolve it by setting up the mapping as such:
Mapper.CreateMap<TaskModel, Task>()
.ForMember(t => t.TaskStatus, opt => opt.MapFrom(m => (TaskStatus)m.TaskStatus))
.ReverseMap();
This seems to work (so far), but my question is there a better or DRYer way to do this. The problem is I will need to do this for a ton of properties across a ton of models and classes. Seems like there should be a simpler way to do what is essentially a simple cast with having to write 100's of lines of mapping code.
You can do this with a type converter:
Mapper.CreateMap<int, TaskStatus>()
.ProjectUsing(src => (TaskStatus)src);
This will be used everywhere. The reason you have to do this is because some LINQ providers have different ways of dealing with enum conversions and persistence, so you have to use the right expression it expects (and AutoMapper doesn't assume it knows what EF or NHibernate or whatever need).
I have a lots of DTOs that must be mapped to my domain's objects. In general, the mapping to monetary values need to apply a rounding rule. That's apply for more than 95% of the cases, but I have some data that need a different rounding rule. I was planned to do the next:
1) Create a ITypeConverter for the general rounding rule, that apply for default in for this type conversions.
2) Create a ValueResolver for the special rounding cases, and specify their use in the member mapping.
I created a test case to try my approach, but after the application of the ValueResolver for the special cases, AutoMapper uses the TypeConverter to get the final value, using the general rule.
That's a wrong approach?? May be I'm missing something??
Then the test case:
[TestFixture]
public class RoundingTests
{
public class MoneyConverter : ITypeConverter<decimal, decimal>
{
public decimal Convert(ResolutionContext context)
{
return Math.Round((decimal)context.SourceValue, 2, MidpointRounding.AwayFromZero);
}
}
public class Money12Resolver : ValueResolver<decimal, decimal>
{
protected override decimal ResolveCore(decimal source)
{
return Math.Round(source, 12, MidpointRounding.AwayFromZero);
}
}
public class PercentResolver : ValueResolver<decimal, decimal>
{
protected override decimal ResolveCore(decimal source)
{
return Math.Round(source, 4, MidpointRounding.AwayFromZero);
}
}
internal class Source
{
public decimal Price { get; set; }
public decimal Percent { get; set; }
public decimal Prorate { get; set; }
}
internal class Destination
{
public decimal Price { get; set; }
public decimal Percent { get; set; }
public decimal Prorate { get; set; }
}
[Test]
public void MappingTest()
{
Mapper.CreateMap<decimal, decimal>().ConvertUsing<MoneyConverter>();
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.Percent, o => o.ResolveUsing<PercentResolver>().FromMember(s => s.Percent))
.ForMember(d => d.Prorate, o => o.ResolveUsing<Money12Resolver>().FromMember(s => s.Prorate));
Mapper.AssertConfigurationIsValid();
var source = new Source
{
Price = 12345.6789m,
Percent = 0.123456m,
Prorate = 123.123451234512345m
};
var convertion = Mapper.Map<Destination>(source);
Assert.That(convertion, Is.Not.Null);
Assert.That(convertion.Price, Is.EqualTo(12345.68m));
Assert.That(convertion.Percent, Is.EqualTo(0.1235m));
Assert.That(convertion.Prorate, Is.EqualTo(123.123451234512m));
}
}
Test Result:
Expected: 0.1235m
But was: 0.12m
You're telling AutoMapper to convert all decimal->decimal mappings using your MoneyConverter. AutoMapper does, in fact, use your resolver (set a break point to see), but the result of resolving is a decimal value that is then used by the MoneyConverter you applied.
Note: This appears to be the design of AutoMapper; I don't see a way to override a type converter.
Not all of your decimal properties represent money, so you may want to take a different approach. Also ask yourself, are you rounding purely for values used for presentation, or are you potentially losing precision you'd want to retain in those domain objects? If you really need the rounding, you could be explicit and set a resolver per member, skipping the converter completely. Alternatively, you could ignore the members which are exceptions and handle that with a .ConstructUsing(...).
But since your original question relates to using a Resolver and Converter for the same type, here's one way you can make it work:
Basically we want the converter to skip the default conversion for certain properties. We can't do it via configuration, so we'll have to do it at run-time. Assuming you have access to the Destination class, just decorate properties with non-default behavior using a custom attribute.
class PercentAttribute : Attribute
{ }
class Destination
{
[Percent]
public decimal Percent { get; set; }
...
}
In your converter, you can then look for properties which are [Percent] and return the source value, which came from your PercentResolver.
public class MoneyConverter : ITypeConverter<decimal, decimal>
{
public decimal Convert(ResolutionContext context)
{
var propInfo = context.PropertyMap.DestinationProperty.MemberInfo;
bool isPercent = propInfo.GetCustomAttributes(typeof(PercentAttribute), true).Any();
if (isPercent) return (decimal)context.SourceValue;
return Math.Round((decimal)context.SourceValue, 2, MidpointRounding.AwayFromZero);
}
}
If you're going to go that route, just have the converter do Money, Percent, and Money12 conversions based on attributes, and skip the resolvers completely.
I get the following error: The type 'EfTest.Person' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.
when running this code:
using System.Data.Entity;
namespace EfTest
{
internal class Program
{
private static void Main(string[] args)
{
using (var db = new PersonContext())
{
db.Persons.Add(new Person());
db.SaveChanges();
}
}
}
public class PersonContext : DbContext
{
public DbSet<Person> Persons { get; set; } //people ;)
}
public class Person : EfTest2.Person
{
public int PersonId { get; set; }
public string Name { get; set; }
}
}namespace EfTest2
{
public class Person
{
public int Age { get; set; }
}
}
can you help and explain why new type can not have same name as its base?
I would focus on the keyword nested in the error. I'm sure taking it out of EfTest2 namespace and keeping the namespace the same will correct this issue. Of course then you must change the base class name from Person to some thing else.
EF has quite complex multi-layered mapping and some of these layers are not directly visible outside. This is a problem of mapping objects into their entity representation in EDM. The point is that EF is not using full class names (including namespaces) but only bare class names so your Person classes are in collision and only one can be resolved.
The origin of this behavior is probably in EF 4.0 when it was used to simplify class creation for POCO models and make namespaces independent on the EDMX.
I think you cannot write the class next to the main session.
You have to separate it to another cs file.
I solved the problem by doing that. Worth a try
Is it possible to use AutoMapper with Immutable types?
For example my Domain type is immutable and I want to map my view type to this.
I believe it is not but just want this confirmed.
Also as it is best practice to have your domain types immutable, what is the best practice when mapping your view types to domain types?
I typically do the mapping from view types to domain types by hand, as I'll typically be working through a more complex interface, using methods and so on. If you use AutoMapper to go from view to domain, you're now locked in to an anemic domain model, whether you've intentionally decided to or not.
Suppose that you really did want an immutable property on your Domain type, say Id. Your domain type might look something like this:
public class DomainType
{
public DomainType(int id)
{
Id = id;
}
public int Id { get; }
// other mutable properties
// ...
}
Then you can use ConstructUsing using a public constructor of your choice, such as:
CreateMap<ViewType, DomainType>()
.ConstructUsing(vt => new DomainType(vt.Id));
Then map all the mutable properties in the normal way
AutoMapper relies on property setters to do its work, so if you have read-only properties, AutoMapper won't be of much use.
You could override the mapping behaviour and, for example, configure it to invoke a specific constructor, but that basically defeats the purpose of AutoMapper because then you are doing the mapping manually, and you've only succeeded in adding a clumsy extra step in the process.
It doesn't make a lot of sense to me that your domain model is immutable. How do you update it? Is the entire application read-only? And if so, why would you ever need to map to your domain model as opposed to from? An immutable domain model sounds... pretty useless.
P.S. I'm assuming that you mean this AutoMapper and not the auto-mapping feature in Fluent NHibernate or even some other totally different thing. If that's wrong then you should be more specific and add tags for your platform/language.
We have immutable objects using the builder pattern. Mapping them takes a little more boilerplate code, but it is possible
// ViewModel
public class CarModel : IVehicleModel
{
private CarModel (Builder builder)
{
LicensePlate = builder.LicensePlate;
}
public string LicensePlate { get; }
//
public Builder
{
public string LicensePlate { get; set; }
}
}
// Model
public class CarViewModel : IVehicleViewModel
{
private CarViewModel (Builder builder)
{
LicensePlate = builder.LicensePlate ;
}
public ILicensePlate LicensePlate { get; }
//
public Builder
{
public ILicensePlate LicensePlate { get; set; }
}
}
Our AutoMapper Profiles have three mappings registered:
CreateMap<IVehicleModel, CarViewModel.Builder>();
CreateMap<CarViewModel.Builder, IVehicleViewModel>().ConvertUsing(x => x.Build());
CreateMap<IVehicleModel, IVehicleViewModel>().ConvertUsing<VehicleModelTypeConverter>();
The VehicleModelTypeConverter then defines a two stage conversion:
public IVehicleViewModel Convert(IVehicleModel source, IVehicleViewModel destination,
ResolutionContext context)
{
var builder = context.Mapper.Map<CarViewModel.Builder>(source);
var model = context.Mapper.Map<IVehicleViewModel>(builder);
return model;
}
(An implementation of ITypeListConverter<string, ILicensePlate> carries out that mapping).
Usage in our system is as normal:
var result = _mapper<IVehicleViewModel>(_carModel);
This is using AutoMapper v7.0.1
You can use Automapper with classes or records that have properties init only setters. This is new in C# 9.0.
Automapper can set the properties at object creation because the properties have init only setters, but after Automapper has mapped them, they are locked in (immutable).
https://www.tsunamisolutions.com/blog/c-90-records-and-dtos-a-match-made-in-redmond