I've read the nested mapping wiki page but it appears to not like multiple levels of nesting. I've got the following maps created and classes defined.
AutoMapper.Mapper.CreateMap<Address, AddressDTO>();
AutoMapper.Mapper.CreateMap<MatchCompanyRequest, MatchCompanyRequestDTO>();
public class MatchCompanyRequest
{
Address Address {get;set;}
}
public class MatchCompanyRequestDTO
{
public CompanyInformationDTO {get;set;}
}
public class CompanyInformationDTO {get;set;}
{
public string CompanyName {get;set;}
public AddressDTO Address {get;set;}
}
But the following code...
// works
matchCompanyRequestDTO.companyInformationDTO.Address =
AutoMapper.Mapper.Map<Address, AddressDTO>(matchCompanyRequest.Address);
// fails
matchCompanyRequestDTO =
AutoMapper.Mapper
.Map<MatchCompanyRequest, MatchCompanyRequestDTO>(matchCompanyRequest);
Does this deep nesting work and I have it configured improperly? Or is this kind of nesting not yet supported?
-- Edit
For anyone interested, I am not in control of the DTOs.
It lacks the mapping from Address to CompanyInformationDTO, as those objects are on the same nest-level.
The map is created for MatchCompanyRequest -> MatchCompanyRequestDTO, but it is unable to figure out whether it can map Address to CompanyInformationDTO.
So your MatchCompanyRequestDTO could in fact have same declaration as your CompanyInformationDTO:
public class MatchCompanyRequestDTO
{
public string CompanyName {get;set;}
public AddressDTO Address {get;set;}
}
This of course only affects you if you want to use automatic mapping. You still can configure your maps manually, but it seems like the DTOs should be fixed instead, let's try anyway:
public class CustomResolver : ValueResolver<Address, CompanyInformationDTO>
{
protected override CompanyInformationDTO ResolveCore(Address source)
{
return new CompanyInformationDTO() { Address = Mapper.Map<Address, AddressDTO>(source) };
}
}
// ...
AutoMapper.Mapper.CreateMap<MatchCompanyRequest, MatchCompanyRequestDTO>()
.ForMember(dest => dest.companyInformationDTO, opt => opt.ResolveUsing<CustomResolver>().FromMember(src => src.Address)); // here we are telling to use our custom resolver that converts Address into CompanyInformationDTO
The important thing is you define how deeper is your navigation, to previne the stackoverflow problems. Imagine this possibility:
You have 2 entities Users and Notifications in NxN model (And
you have DTOs object to represent that), when you user auto mapper
without set MaxDepth in you mapper expression, "Houston we have a
problem" :).
The code below show a workaround to resolve this for all Mappers. If you want can be defined to each mapper. Like this Question
Solution 1 (Global Definition)
public class AutoMapperConfig
{
public static void RegisterMappings()
{
Mapper.Initialize(mapperConfiguration =>
{
mapperConfiguration.AddProfile<DomainModelToYourDTOsMappingProfile>();
mapperConfiguration.AddProfile<YourDTOsToDomainModelMappingProfile>();
mapperConfiguration.AllowNullCollections = true;
mapperConfiguration.ForAllMaps(
(mapType, mapperExpression) => {
mapperExpression.MaxDepth(1);
});
}
}
Solution 2 (For each Mapper)
public class AutoMapperConfig
{
public static void RegisterMappings()
{
Mapper.CreateMap<User, DTOsModel>()
.MaxDepth(1);
}
}
Consider the following instead:
public class MatchCompanyRequest
{
Address Address {get;set;}
}
public class MatchCompanyRequestDTO
{
public string Name {get;set;}
public AddressDTO Address {get;set;}
}
public class AddressDTO
{
....
}
Your DTO objects need to have the same structure as your domain objects for the default mapping conventions to work in AutoMapper.
Look at this: https://github.com/AutoMapper/AutoMapper/wiki/Projection It will explain the Projection for you, you could customize it to work the way you have it.
Related
I have a model that looks similar to this:
public class Employee {
public virtual string Name { get; set; }
public virtual string Name { get; set; }
public virtual Gender Gender { get; set; }
}
I have a query like this:
var myEmployees = session.Query<Employee>()
.Where(a => a.Name.Equals(name)); // name is given as parameter
now I have the need to set the Gender to null with an NHibernate EventListener. I guess I have to use an IPreLoad or IPostLoad EventListener. But I don't know hot to access the entity.
Any ideas? Thanks in advance.
You have to understand something important before using Listeners on NHibernate. It will run over all entities mapped from the NHibernate perspective. If you write a bad code on it, it can degrade the performance of your application. Given that, you can define a listener on the NHibernate Configuration object. You define a class that implements an interface from the NHibernate listeners you want and implement the method. Add an instance of this class into the NHibernate configuration. (I didn't test the code bellow, it's just a draft) For sample:
using NHibernate;
using NHibernate.Event;
using NHibernate.Persister.Entity;
public class EmployeePostLoadListener : IPostLoadEventListener
{
public virtual void OnPostLoad(PostLoadEvent postloadEvent)
{
if (postloadEvent.Entity is Employee)
{
var employee = (Employee)postloadEvent.Entity;
// do what you want with the object
employee.Gender = null;
}
}
}
In the configuration of the NHibernate, you can add this listener, something like this:
NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
cfg.EventListeners.PostLoadEventListeners = new IPostLoadEventListener[] {new EmployeePostLoadListener()};
See more about EventListeners and Interceptors on the documentation.
I'm using AutoMapper v6.1.1 to map from my rich domain model entities to some flattened DTOs.
I'm initialising the configuration in a static class that returns an IMapper, which adds our mapping profiles and configures PreserveReferences() for all our maps.
I have declared a custom attribute against a subset of my source entity members (that only applies to members of type string).
I would like to add a global configuration to AutoMapper that allows me to call an extension method against any members with that attribute during the mapping.
Each of these members will end up in many different destination types, so I thought it would be a simple way of ensuring the extension method is always run for those members without explicitly configuring it for each new map.
A contrived example follows.
Source entity:
public class SomeEntity
{
public string PropertyWithoutCustomAttribute { get; set; }
[CustomAttribute]
public string PropertyWithCustomAttribute { get; set; }
}
Target entity:
public class SomeEntityDto
{
public string PropertyWithoutCustomAttribute { get; set; }
public string PropertyWithCustomAttribute { get; set; }
}
Extension method:
public static string AppendExclamationMark(this string source)
{
return source + "!";
}
If my source instance is defined with these values:
var source = new SomeEntity
{
PropertyWithoutCustomAttribute = "Hello",
PropertyWithCustomAttribute = "Goodbye"
};
I would expect the following statements to be true:
destination.PropertyWithoutCustomAttribute == "Hello"
destination.PropertyWithCustomAttribute == "Goodbye!"
I have become completely bogged down (and am struggling with the documentation somewhat) but I think the closest I have got is this:
cfg.ForAllPropertyMaps(
map => map.SourceType == typeof(string) &&
map.SourceMember
.GetCustomAttributes(
typeof(CustomAttribute),
true)
.Any(),
(map, configuration) => map.???);
Any help would be greatly appreciated, even if to tell me it's a terrible idea or it's not possible.
Given these two objects
public class UserModel
{
public string Name {get;set;}
public IList<RoleModel> Roles {get;set;}
}
public class UserViewModel
{
public string Name {get;set;}
public IList<RoleViewModel> Roles {get;set;} // notice the ViewModel
}
Is this the most optimal way to do the mapping, or is AutoMapper capable of mapping Roles to Roles on its own?
App Config
Mapper.CreateMap<UserModel, UserViewModel>()
.ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
Mapper.CreateMap<UserViewModel, UserModel>()
.ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
Implementation
_userRepository.Create(Mapper.Map<UserModel>(someUserViewModelWithRolesAttached);
Is this the most optimal way to do the mapping, or is AutoMapper capable of mapping Roles to Roles on its own?
If the property names are identical, you should not have to manually provide a mapping:
Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
Just make sure the inner types are mapped as well (RoleViewModel ↔ RoleModel)
What this means, however, is that if you change a source or destination property name, AutoMapper mappings can fail silently and cause hard to track down problems (e.g., if you changed UserModel.Roles to UserModel.RolesCollection without changing UserViewModels.Roles).
AutoMapper provides a Mapper.AssertConfigurationIsValid() method that will check all of your mappings for errors and catch misconfigured mappings. It's useful to have a unit test that runs with the build that validates your mappings for this kind of problem.
You don't need to map the properties. Just make sure that the property names match and there is a mapping defined between them.
Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
Mapper.CreateMap<RoleModel, RoleViewModel>();
Mapper.CreateMap<RoleViewModel, RoleModel>();
Or with the cooler way I just found out:
Mapper.CreateMap<UserModel, UserViewModel>().ReverseMap();
Mapper.CreateMap<RoleModel, RoleViewModel>().ReverseMap();
All the other answers, are much better (which I gave an upvote to each).
But what I wanted to post here is a quick playground that you could copy and past right into LinqPad in C# program mode and play your idea's without messing with your actual code.
Another awesome thing about moving all your conversions into a TyperConverter class is that your conversions are now Unit Testable. :)
Here you will notice that the model and viewmodel are almost identical except for one property. But through this process the right property is converted to the correct property in the destination object.
Copy this code into LinqPad and you can run it with the play button after switching to C# Program mode.
void Main()
{
AutoMapper.Mapper.CreateMap<UserModel, UserViewModel>().ConvertUsing(new UserModelToUserViewModelConverter());
AutoMapper.Mapper.AssertConfigurationIsValid();
var userModel = new UserModel
{
DifferentPropertyName = "Batman",
Name = "RockStar",
Roles = new[] {new RoleModel(), new RoleModel() }
};
var userViewModel = AutoMapper.Mapper.Map<UserViewModel>(userModel);
Console.WriteLine(userViewModel.ToString());
}
// Define other methods and classes here
public class UserModel
{
public string Name {get;set;}
public IEnumerable<RoleModel> Roles { get; set; }
public string DifferentPropertyName { get; set; }
}
public class UserViewModel
{
public string Name {get;set;}
public IEnumerable<RoleModel> Roles { get; set; } // notice the ViewModel
public string Thingy { get; set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine(string.Format("Name: {0}", Name));
sb.AppendLine(string.Format("Thingy: {0}", Thingy));
sb.AppendLine(string.Format("Contains #{0} of roles", Roles.Count()));
return sb.ToString();
}
}
public class UserModelToUserViewModelConverter : TypeConverter<UserModel, UserViewModel>
{
protected override UserViewModel ConvertCore(UserModel source)
{
if(source == null)
{
return null;
}
//You can add logic here to deal with nulls, empty strings, empty objects etc
var userViewModel = new UserViewModel
{
Name = source.Name,
Roles = source.Roles,
Thingy = source.DifferentPropertyName
};
return userViewModel;
}
}
public class RoleModel
{
//no content for ease, plus this has it's own mapper in real life
}
Result from the Console.WriteLine(userViewModel.ToString());:
Name: RockStar
Thingy: Batman
Contains #2 of roles
Inside the Startup.cs in the Configure() method:
Mapper.Initialize(config => {
config.CreateMap<UserModel, UserViewModel>().ReverseMap();
// other maps you want to do.
});
Just getting my feet wet with some Fluent NHibernate AutoMap conventions, and ran into something I couldn't figure out. I assume I'm just not looking in the right place...
Basically trying to enforce NOT-NULL on the "many" side of the one to many relationship.
It seems, using the automapping, it always makes the parent property Id nullable in the database.
I did some searching on StackOverFlow and found similar questions, but nothing relating to AutoMapping and Conventions though (unless I missed it).
Quick example...
public class Group // One Group
{
public Group() { this.Jobs = new List<Job>(); }
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Job> Jobs { get; protected set; }
}
public class Job // Has many Jobs
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
// Trying to make this field not-nullable in the database.
public virtual Group Group { get; set; }
}
I thought I'd be able to just create a convention like...
public class OneToManyConvention : IHasOneConvention
{
public void Apply(IOneToOneInstance instance)
{
// Nullable() isn't a valid method...
instance.Not.Nullable();
}
}
But it seems IOneToOnInstance doesn't have a Nullable() method. I can do this if I create a Map file for Job, but trying to avoid any Map files and stick with auto-mapping.
I came across this link on the Fluent group list describing something similar.
Which describes something like this...
public class NotNullPropertyConvention : IPropertyConvention
{
public bool Accept(IProperty target)
{
return true;
}
public void Apply(IProperty target)
{
target.Not.Nullable();
}
}
But that raises the questions of...
1) How would I determine IProperty to be a Job (or any child property that is a link back to the parent)
2) It made a mention on that page that using this would override my manual overrides, eg. if a very specific property link needed to be NULL. Which would be an issue (if it's still an issue, but can't test without figuring out #1 first)
Any ideas on this? Am I just missing something?
Update 1
Still no go. Even the following still doesn't enforce Not-Nullable in the database schema...
public class FluentConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
}
}
It does for all of the other fields though...
/shrug
Any ideas?
Update 2
While this isn't the answer I was looking for, I did find a work around...
I was using NHibernate Validator assembly, and within that assembly there is a [NotNull] attribute. If I decorated my class with the Validator attribute, and associated the ValidationEngine to NHibernate before the schema creation, it would tag the FK database column as Not-Nullable.
public class Job // Has many Jobs
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
[NHibernate.Validator.Constraints.NotNull]
public virtual Group Group { get; set; }
}
If anyone needs the full code for the NHibernate + ValidationEngine initialization, just let me know.
Still looking for a way to do it using the pure mapping convention route though if anyone has any info...
Thanks!
You can override the auto-mapped properties as part of your AutoMap in Fluenttly.Configure().
So you can do this:
.Override<Job>(map => map.References(x => x.Group).Not.Nullable())
It's not exactly convenient if you have a lot of classes that need this though.
Edit:
You can also specify the override in a class that implements IAutoMappingOverride like so:
public class JobMappingOverride : IAutoMappingOverride<Job>
{
public void Override(AutoMapping<Job> mapping)
{
mapping.References(x => x.Group).Not.Nullable();
}
}
and include it like so:
.UseOverridesFromAssemblyOf<JobMappingOverride>()
This would keep your fluent configuration a little cleaner.
It seems that IPropertyConvention is only called on simple properties of your classes. If your property references another class, you need to use IReferenceConvention too.
Try this:
public class FluentConvention : IPropertyConvention, IReferenceConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
}
public void Apply(IManyToOneInstance instance)
{
instance.Not.Nullable();
}
}
I'm trying to figure out what the convention would be for a value object list, in this case an IList. Here a code fragment for my domain model:
public class RegionSetting : Entity {
public virtual bool Required { get; set; }
public virtual string Name { get; set; }
public virtual IList<string> Options { get; set; }
}
My automapping is set to:
public class RegionSettingMap : IAutoMappingOverride<RegionSetting> {
public void Override(AutoMapping<RegionSetting> mapping) {
mapping
.HasMany(x => x.Options).Element("Options")
.Table("RegionSettingOptions")
.KeyColumn("RegionSettingId");
}
}
I'd like to make the .Table() and .KeyColumn() overrides into a convention so that I don't have to do that everywhere I'm using IList<string>. I thought that I could create an IHasManyConvention, but it doesn't seem to affect this mapping. I set a breakpoint in my custom HasManyConvention class, but it doesn't break for the Options property. Could anyone tell me what convention I should be using to automate this override?
Using an IHasManyConvention should've worked. Try an IBagConvention, see if that works. If not, there's a bug in there.