Before asking here I read all post relative to the subject but I can't find any solution.
I removed all my domain complexity and I'm down to the following simple issue:
I have 2 class (in a domain file Domain.cs) :
public abstract class BaseClass
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
public class ASubClass : BaseClass
{
public ASubClass() {}
public virtual string prop { get; set; }
}
My Mapping is (In another file Mapping.cs):
public class BaseClassMap : ClassMap<BaseClass>
{
public BaseClassMap ()
{
Id(x => x.ID).GeneratedBy.HiLo("1000");
Map(x => x.Name);
DiscriminateSubClassesOnColumn("Type");
}
}
public class ASubClassMap : SubclassMap<ASubClass>
{
public ASubClassMap ()
{
Map(x => x.prop);
DiscriminatorValue(1);
}
}
First I let NHibernate create the DB for me :
var cfg = new NHibernate.Cfg.Configuration();
Fluently.Configure(cfg)
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionstring)
.ShowSql())
.Mappings(m => m
.FluentMappings.AddFromAssemblyOf<BaseClass>()).BuildConfiguration();
new SchemaExport(cfg).Execute(false,true,false);
This works great, then my unit test is the following :
I build a session Factory within a Repository and use the Session/Transaction to try to save an empty SubClass:
var contract = new ASubClass();
IRepository<ASubClass> repository = new Repository<ASubClass>();
repository.Add(contract); >>> BUG HERE
This in fact bug on the line(inside the repo) : session.Save(contract);
I tried to copy/paste my Mapping.cs into Domain.cs or change the attribute of
.FluentMappings.AddFromAssemblyOf<BaseClass> to .FluentMappings.AddFromAssemblyOf<BaseClassMap>
I also tried to change the hierarchy of subclass by removing the Discriminator and the
DiscriminateSubClassesOnColumn keyword.
Didnt work so far.
Thanks.
You code actually works fine for me.
There must be something wrong with your configuration or your repository or how you use the configuration+repository within your unit tests. I mean, you create a new Repository, does this use the configuration you posted? 100% sure?
Are you using your cfg to build the ISessionFactory to be used by your repository? e.g.
var factory = cfg.BuildSessionFactory();
Maybe check that all mappings are in the same assembly, otherwise add both assemblies.
Related
I have some code that selects a property using reflection. It works fine but I can't unit test it. Apparently, the mock that it creates does not have any properties.
Can anyone tell me how to get the property from a Mocked object using reflection? (Or explain why it would not be possible?)
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
I'm using this for a copy class which allows to copy data for any entity type and expects a UnitOfWork that contains a IRepository where T is the type that you want to copy.
public class TableCopier<T> : ICopier where T : EntityBase
{
public IUnitOfWork From { get; set; }
public IUnitOfWork To { get; set; }
public virtual int Copy(IProgressReporter progres = null)
{
lock (this)
{
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
//...
}
}
}
The Unit of Work looks like this:
public interface IUnitOfWork
{
IRepository<Parameter> ParameterRepository { get; }
IRepository<Company> CompanyRepository { get; }
IRepository<CompanyAccountancynumber> CompanyAccountancynumberRepository { get; }
//...
And finally the unit test:
[Test]
public void DefaultCopy()
{
// (removed some code)
var to = MockRepository.GenerateStrictMock<IUnitOfWork>();
var target = MockRepository.GenerateStrictMock<IRepository<CompanyDataset>>();
to.Stub(x => x.CompanyDatasetRepository).Return(target);
var inserted = new List<CompanyDataset>();
target.Stub(x => x.BulkInsert(null))
.IgnoreArguments()
.WhenCalled(x => inserted.AddRange(x.Arguments[0] as IEnumerable<CompanyDataset>));
//ACT
var copier = new TableCopier<CompanyDataset>() as ICopier;
copier.Copy(from, to, map, log, null, CancellationToken.None );
I found the problem:
The properties are available as explicit properties.
And to access these kind of properties you need the interface map.
This means I should change this:
var target = To.GetType().GetProperties()
.Single(x => typeof(IRepository<T>).IsAssignableFrom(x.PropertyType))
.GetValue(To) as IRepository<T>;
To this:
var target = To.GetType().GetInterfaceMap(typeof(IUnitOfWork))
.InterfaceMethods
.Single(x => (typeof(IRepository<T>).IsAssignableFrom(x.ReturnType)))
.Invoke(To, null) as IRepository<T>;
My own implementations of IUnitOfWork used 'implicit' properties, so that explains why that worked and the unit test failed. Also using the map is generally better (/more correct) (-> no chance on problems if I would ever make another implementation of the interface with explicit props). So I suppose it was a good unit test :)
Regards
I want to create a custom mapping standard so I don't have to create the map.cs file for all new classes in the project.
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
}
Usually I'd have this:
public class PersonMap : ClassMapping<Person>
{
public PersonMap()
{
Table("Person");
Id(p => p.PersonID, map =>
{
map.Column("PersonID");
map.Generator(Generators.Identity);
});
Property(p => p.Name, map => map.Column("Name"));
}
}
I'd like to dynamically create these mappings based on some standards using reflection.
public class GenericDAL<T> where T : class, new()
{
public GenericDAL()
{
Configuration hConfig = new Configuration();
hConfig.DatabaseIntegration(c =>
{
c.ConnectionStringName = "myConnectionStringName";
c.Dialect<MsSql2012Dialect>();
});
ModelMapper mapper = new ModelMapper();
//Dynamic Mapping here
ISessionFactory _sessionFactory = hConfig.BuildSessionFactory();
}
}
I don't know how I can create a new ClassMapping from my T, how can I do this?
#SteveLillis already answered the question in the comments that there are already solutions for this.
Both MappingByCode in NHibernate (see below) and FluentNHibernate support automapping with conventions and overrides.
Links for Mapping By Code copied from original Answer which is not available anymore
First impressions
Naming convention resembling Fluent
Property
Component
ManyToOne
inheritance
dynamic component
Set and Bag
OneToMany and other collection-based relation types
concurrency
OneToOne
Join
Any
List, Array, IdBag
Map
Id, NaturalId
composite identifiers
entity-level mappings
the summary
I was looking for the same thing and I found a great documentation on NHibernate official website.
Here we have all the links to "fabiomaulo.blogspot" website, there you will find what are you looking for, WITHOUT FluentNHibernate.
Good Luck
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.
});
I have a services that is calling another services. Both of the services are using "the same classes". The classes are named same and have the same properties but has different namespace so I need to use AutoMapper to map from one of the type to the other type.
No it's pretty simple since all I have to do is the CreateMap<>, but the problem is that we have around hundreds of classes that I manually needs to write the CreateMap<> from, and it's works wired to me. Isn't there any Auto CreateMap function. So if I say CreateMap() then AutoMapper workes thru Organisation and finds all classes and automatically does the CreateMap for these Classes and it's subclasses etc etc…
Hope for a simple solution, or I guess some reflection can fix it...
Just set CreateMissingTypeMaps to true in the options:
var dto = Mapper.Map<FooDTO>
(foo, opts => opts.CreateMissingTypeMaps = true);
If you need to use it often, store the lambda in a delegate field:
static readonly Action<IMappingOperationOptions> _mapperOptions =
opts => opts.CreateMissingTypeMaps = true;
...
var dto = Mapper.Map<FooDTO>(foo, _mapperOptions);
UPDATE:
The approach described above no longer works in recent versions of AutoMapper.
Instead, you should create a mapper configuration with CreateMissingTypeMaps set to true and create a mapper instance from this configuration:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
// other configurations
});
var mapper = config.CreateMapper();
If you want to keep using the old static API (no longer recommended), you can also do this:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
// other configurations
});
UPDATE 2 - Automapper 9 and later:
Starting from Automapper version 9.0, the CreateMissingTypeMaps API was removed. Automapper documentation now suggests to explicitly configure maps, manually or using reflection.
https://docs.automapper.org/en/stable/9.0-Upgrade-Guide.html#automapper-no-longer-creates-maps-automatically-createmissingtypemaps-and-conventions
CreateMissingTypeMaps can be set within your profile. It's however recommended to explicitly use CreateMap for each mapping and call AssertConfigurationIsValid in your unit tests for each profile to prevent silent errors.
public class MyProfile : Profile {
CreateMissingTypeMaps = true;
// Mappings...
}
AutoMapper has a DynamicMap method which you might be able to use: here's an example unit test illustrating it.
[TestClass]
public class AutoMapper_Example
{
[TestMethod]
public void AutoMapper_DynamicMap()
{
Source source = new Source {Id = 1, Name = "Mr FooBar"};
Target target = Mapper.DynamicMap<Target>(source);
Assert.AreEqual(1, target.Id);
Assert.AreEqual("Mr FooBar", target.Name);
}
private class Target
{
public int Id { get; set; }
public string Name { get; set; }
}
private class Source
{
public int Id { get; set; }
public string Name { get; set; }
}
}
Set CreateMissingTypeMaps option to true. This is package AutoMapper.Extensions.Microsoft.DependencyInjection's example for ASP.NET Core:
public class Startup {
//...
public void ConfigureServices(IServiceCollection services) {
//...
services.AddAutoMapper(cfg => { cfg.CreateMissingTypeMaps = true; });
//...
}
//...
}
In case someone is still interested in this topic, I've created a NuGet package that gives the automatic mapping functionality since AutoMapper removed it in a certain version.
It's available under wakiter.AutoMapper.Extensions name.
To use it, invoke the CreateAutoMap extension method and it'll do the work for you.
Today I needed this in some generic code as well. I tried something like this:
private static IMapper CreateMapper<T1, T2>()
{
return new MapperConfiguration(cfg => FillMapperConfig(cfg, typeof(T1), typeof(T2)))
.CreateMapper();
}
private static void FillMapperConfig(IMapperConfigurationExpression cfg, Type T1, Type T2)
{
if (T1 == T2)
{
return;
}
cfg.CreateMap(T1, T2);
foreach (PropertyInfo propertyInfo in T1.GetProperties())
{
PropertyInfo correspondingProperty =
T2.GetProperties()
.FirstOrDefault(p =>
p.Name == propertyInfo.Name);
if (correspondingProperty != null)
{
if (propertyInfo.PropertyType.IsGenericType &&
correspondingProperty.PropertyType.IsGenericType)
{
FillMapperConfig(
cfg,
propertyInfo.PropertyType.GetGenericArguments()[0],
correspondingProperty.PropertyType.GetGenericArguments()[0]);
}
else if (propertyInfo.PropertyType.IsClass &&
correspondingProperty.PropertyType.IsClass)
{
FillMapperConfig(
cfg,
propertyInfo.PropertyType,
correspondingProperty.PropertyType);
}
}
}
}
Then I can do something like this:
IMapper mapper = CreateMapper<ClassA, ClassB>();
Which creates a map from ClassA to ClassB with all sub properties of ClassA and ClassB if they have the same name and recursively for sub sub properties.
Example:
public class ClassA {
public int IntProperty { get; set; }
public ClassASubProperty SubProperty { get; set; }
public List<ClassAListItem> ListItems { get; set; }
}
public class ClassB {
public int IntProperty { get; set; }
public ClassBSubProperty SubProperty { get; set; }
public List<ClassBListItem> ListItems { get; set; }
}
This should result in the IMapper equivalent:
new MapperConfiguration(cfg => {
cfg.CreateMap<ClassA, ClassB>();
cfg.CreateMap<ClassASubProperty, ClassBSubProperty>();
cfg.CreateMap<ClassAListItem, ClassBListItem>()
}).CreateMapper();
My web app uses half a dozen tables, each of which get populated when a user passes through the system. In order to do stats analysis I've written a database view to flatten these tables into a single view.
The view is working, however, I want to automate some tests around the view creation.
My idea to do this was to create a model/map and repository for the view - with list action only. My current implementation doesn't work.
This is my Repository:
namespace FunctionalTests.SpssView
{
public class SpssRepository
{
private readonly ISessionManager _sessionManager;
public SpssRepository(ISessionManager sessionManager)
{
_sessionManager = sessionManager;
}
public IList<Spss> ListFromSpssView()
{
ICriteria criteria = _sessionManager.GetSession().CreateCriteria(typeof(Spss));
return criteria.List<Spss>();
}
}
}
This is the model class:
namespace FunctionalTests.SpssView
{
public class Spss
{
public virtual String StudentId { get; set; }
public virtual String UPNSCN { get; set; }
...
}
}
And the mapping:
namespace FunctionalTests.SpssView
{
public sealed class SpssMap : ClassMap<Spss>
{
public SpssMap()
{
Id(x => x.StudentId).GeneratedBy.Assigned();
Map(x => x.UPNSCN);
...
}
}
}
I'm not entirely confident in the ID mapping - as it is just read from the view?
This is my test:
[Test]
public void ShouldPopulateAndRetrieveFromSpssView()
{
var mockSessionManager = new Mock<ISessionManager>();
mockSessionManager.Setup(x => x.GetSession()).Returns(_session);
var caseRepository = new CaseRepository(mockSessionManager.Object);
var caseList = caseRepository.ListCases();
Assert.That(caseList.Count, Is.EqualTo(2));
var repository = new SpssRepository(mockSessionManager.Object);
var spssList = repository.ListFromSpssView();
Assert.That(spssList.Count, Is.EqualTo(2));
}
Note the case list code - I put that in there to make sure the db connection was being made. This part of the test passes.
Running select * from spss; returns two results. (I'm using sql server 2005 fwiw)
And because this isn't production code, I created a new folder in my FunctionalTests visual studio project (I mention this, because it seems to me to be one of the main differences between this and my working repositories.) Should this make a difference??
Is it possible to test views like this?
Is there anyway I can see the sql that is being generated?
What am I doing wrong??!?
Thanks :)
Try adding:
public SpssMap()
{
Table("myViewBame"); // ADD THIS
Id(x => x.StudentId).GeneratedBy.Assigned();
Map(x => x.UPNSCN);
...
}
In order to see the generated SQL add this:
.ShowSql()
For example:
Fluently.Configure().Database(
MsSqlConfiguration.MsSql2005
.ConnectionString(
ConfigurationManager.ConnectionStrings["my"].ConnectionString).ShowSql())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MyClass>())
.BuildSessionFactory();