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
Related
In my project I'm trying to use automapper to unflatten my command objects to my domain objects by convention as much as possible.
It works when I explicitly map the two members in the mapping profile, but according to the automapper documentation I think this should also work by convention.
I created a dotnetfiddle to demonstrate the minimal case.
Related questions end up with people explicitly adding the mapping, but that kind of goes against what Automapper is built for and contradicts the documentation, no?
It doesn't work with flattening either, so the reversemap is a red herring I think.
The mapping
public class Mapping: Profile
{
public Mapping()
{
this.CreateMap<CreateSelectionCommand, Selection>();
// .ForMember(selection => selection.Name, opt => opt.MapFrom(x => x.SelectionName))
.reverseMap()
}
}
What I expect to work
[Fact]
public void ShouldMapName()
{
var cmd = new CreateSelectionCommand {SelectionName = "selectionName"};
var selection = _mapper.Map<Selection>(cmd);
Assert.Equal(cmd.SelectionName, selection.Name); <== selection.Name == ""
}
Classes for context
public class Selection
{
public string Name { get; set; }
}
public class CreateSelectionCommand
{
public string SelectionName { get; set; }
}
Did I misread the docs or am I missing something?
Flattening is about mapping nested "complex" objects to properties on "higher" level i.e. in your case if CreateSelectionCommand had property Selection of type which had Name property it would be mapped to SelectionName in destination type (see this fiddle).
You can try to use prefixes by adding:
cfg.RecognizePrefixes("Selection");
to your configuration (see this fiddle) but I doubt that it is suitable option for convention based handling.
Also it seems that you can add custom name convention using ISourceToDestinationNameMapper and AddMemberConfiguration:
class TypeNamePrefixedSourceToDestinationNameMapper : ISourceToDestinationNameMapper
{
public MemberInfo GetMatchingMemberInfo(IGetTypeInfoMembers getTypeInfoMembers, TypeDetails typeInfo,
Type destType,
Type destMemberType, string nameToSearch)
{
return getTypeInfoMembers.GetMemberInfos(typeInfo)
.FirstOrDefault(mi => mi.Name == destType.Name + nameToSearch);
}
}
var config = new MapperConfiguration(cfg =>
{
cfg.AddMemberConfiguration().AddName<TypeNamePrefixedSourceToDestinationNameMapper>();
// ...
}
At least it works in this simple case, see this fiddle.
Assume I have the following entity classes:
public class Customer {
public int Id { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Order {
public int Id { get; set; }
public virtual Customer Customer { get; set; }
}
How should those be mapped in Entity Framework 6 fluent code-first mapping? I want to be explicit about the mapping and not rely on automatic mapping conventions.
Option 1
Just map the local properties of both classes. That's how I would do it in Fluent NHibernate.
public class CustomerMap : EntityTypeConfiguration<Customer> {
public CustomerMap() {
HasMany(x => x.Orders);
}
}
public class OrderMap : EntityTypeConfiguration<Order> {
public OrderMap() {
HasRequired(x => x.Customer);
}
}
Option 2
Map both sides of the relationship in both classes.
public class CustomerMap : EntityTypeConfiguration<Customer> {
public CustomerMap() {
HasMany(x => x.Orders).WithRequired(x => x.Customer);
}
}
public class OrderMap : EntityTypeConfiguration<Order> {
public OrderMap() {
HasRequired(x => x.Customer).WithMany(x => x.Orders);
}
}
Option 3
Map both sides of the relation, but only in one of the classes. The code would be similar to option 2, just one of the two constructors would be empty.
Is there any difference between those options? If yes, please also explain why I should or shouldn't use a specific option.
I would go for option 3.
In option 1 you can forget to map the inverse end of an association. In this simple example it's clear that Order.Customer and Customer.Orders are two ends of the same association. When things get more complex, this isn't always obvious. Also, it is redundant code.
In option 2 you could have conflicting mappings. For instance when you have...
HasOptional(x => x.Customer).WithMany(x => x.Orders);
...in OrderMap, you will get a runtime exception telling you that both mappings don't match. And again, it is redundant code.
So option 3 is DRY and safe. The only issue is that it's a bit arbitrary where to configure the mappings. I tend to adhere to mapping children in their parent's mapping.
One more comment. You may want to add a primitive property CustomerId in Order. The mapping would look like:
public class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
HasMany(x => x.Orders).WithRequired(x => x.Customer)
.HasForeignKey(o => o.CustomerId);
}
}
Now you have full control over both ends of the association and the foreign key name to be used. Besides that, there are some advantages of these foreign key associations as opposed to independent associations (without a primitive foreign key property). For instance, the ability to establish an association without having to fetch the parent object from the database. You can just by set an Id value.
Using Entity Framework (Code First), I'm trying to map a conditional/filtered relationship between the following 2 entities:
Building
BuildingId
BuildingName
Area
AreaId
ParentId
AreaName
IsSubArea
A Building can have many Areas
An Area can have many (Sub)Areas
I would like to create the relationship between Building and Area where the areas marked with 'IsSubArea' are filtered out of the relationship. In this context, ParentId would relate to a Building, otherwise, ParentId would be another Area. This would allow me to create a building with many areas, and each area could have many sub-areas, creating a tree style structure.
The closest to a solution I have found relates to 'soft delete' functionality (source):
modelBuilder.Entity<Foo>().Map(m => m.Requires("IsDeleted").HasValue(false));
Converted to fit my example:
modelBuilder.Entity<Area>().Map(m => m.Requires("IsSubArea").HasValue(false));
But as far as I can tell, this has no bearing on the relationship to the Building.
Another solution would be to create a property on the Building which specifies the query definition to use to return related areas (source):
public class Building
{
public int BuildingId {get; set;}
public string BuildingName {get; set;}
public IQueryable<Area> BuildingAreas
{
get
{
return from area in areas
where area.IsSubArea == false
and area.ParentId == BuildingId
select area;
//Assume I have a reference to relevant DbSets
}
}
}
This solution would work but doesn't feel as elegant as a conditional mapping.
Another solution would be to inherit from Area and create the 2 sub-classes:
BuildingArea
AreaId
BuildingId
AreaName
SubArea
AreaId
ParentAreaId
AreaName
Each inherits from Area and sets the 'IsSubArea' field as appropriate.
This solution feels tidier but I do not know how to implement this in Entity Framework.
Is there a way to specify conditional mapping on relationships?
Is there a better way to implement this structure?
Update 1:Found this & this guide on inheritance which seems to match my requirements. However, neither of these tutorials define relationships between derived types. I'll update the question tonight with what I have tried with regards to the Table per Hierarchy (TPH) method.
Update 2:
I'm going to try an describe the Table per Hierarchy (TPH) method I tried to implement based on the tutorial links above. Forgive me if this gets a little complicated (maybe I'm over thinking it).
Models
The building class remains the same as the OP.
I have created an abstract base class defining the Area properties common to each derived type (BuildingArea and SubArea):
public abstract class Area
{
protected Area(bool isSubArea)
{
IsSubArea = isSubArea;
SubAreas = new List<SubArea>();
}
public int AreaId { get; set; }
public int ParentId { get; set; }
public string AreaName { get; set; }
public bool IsSubArea { get; private set; } //note the private set
public virtual ICollection<SubArea> SubAreas { get; set; }
}
I then have 2 derived types which inherit from Area:
public class BuildingArea : Area
{
public BuildingArea () : base(false)
{}
public virtual Building ParentBuilding { get; set; }
}
public class SubArea : Area
{
public SubArea(): base(true)
{}
// This is of type `Area` because parent could be either `BuildingArea` or `SubArea`
public virtual Area Parent { get; set; }
}
I then have the following 2 EntityTypeConfigurations:
public class BuildingAreaMap : EntityTypeConfiguration<BuildingArea>
{
public BuildingAreaMap ()
{
// Primary Key
HasKey(t => t.AreaId);
// Properties
Property(t => t.AreaName)
.IsRequired()
.HasMaxLength(256);
// Table & Column Mappings
ToTable("Areas");
Property(t => t.AreaId).HasColumnName("AreaId");
Property(t => t.ParentId).HasColumnName("ParentId");
Property(t => t.AreaName).HasColumnName("AreaName");
Property(t => t.IsSubArea).HasColumnName("IsSubArea");
// This is the discriminator column
Map(m => m.Requires("IsSubArea").HasValue(false));
HasRequired(a => a.Site).WithMany(s => s.SiteAreas).HasForeignKey(k => k.ParentId);
}
public class SubAreaMap : EntityTypeConfiguration<SubArea>
{
public SubAreaMap()
{
// Primary Key
HasKey(t => t.AreaId);
// Properties
Property(t => t.AreaName)
.IsRequired()
.HasMaxLength(256);
// Table & Column Mappings
ToTable("AssetHealthAreas");
Property(t => t.AreaId).HasColumnName("AreaId");
Property(t => t.ParentId).HasColumnName("ParentId");
Property(t => t.AreaName).HasColumnName("AreaName");
Property(t => t.IsSubArea).HasColumnName("IsSubArea");
// This is the discriminator column
Map(m => m.Requires("IsSubArea").HasValue(true));
HasRequired(a => a.Parent).WithMany(s => s.SubAreas).HasForeignKey(k => k.ParentId);
}
}
This code builds successfully, but I do get the following runtime error:
Map was called more than once for type 'SiteArea' and at least one of the calls didn't specify the target table name.
But I am specifying the target table name (once in each EntityTypeConfiguration class).
So I removed the EntityTypeConfiguration for SubArea but I get the same error.
One of the tutorials pulls the mapping out of the EntityTypeConfiguration class and puts it in the OnModelCreating handler as follows:
modelBuilder.Entity<Area>()
.Map<BuildingArea>(m => m.Requires("IsSubArea").HasValue(false))
.Map<SubArea>(m => m.Requires("IsSubArea").HasValue(true));
This also gives me the same error.
If I remove relationships from the equation, I get a different error regarding the ParentId property:
The foreign key component 'ParentId' is not a declared property on type 'SiteArea'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.
Update 3
An image of the model I'm trying to create...
Update 4
I'm going to try and simplify my model to match the following.
If the solution below works, I will need to have a little more business logic to navigate the tree but it should be manageable.
For the errors when creating the TPH classes:
I think this is because you are not supposed to have the discriminator column as a property in your classes.
Remove the property IsSubArea from your base class.
When you create new instance, EF should automatically detect the type and fill the IsSubArea accordingly.
I'm using Automapper to map my NHibernate proxy objects (DTO) to my CSLA business objects
I'm using Fluent NHibernate to create the mappings - this is working fine
The problem I have is that the Order has a collection of OrderLines and each of these has a reference to Order.
public class OrderMapping : ClassMap<OrderDTO>
{
public OrderMapping()
{
// Standard properties
Id(x => x.OrderId);
Map(x => x.OrderDate);
Map(x => x.Address);
HasMany<OrderLineDTO>(x => x.OrderLines).KeyColumn("OrderId").Inverse();
Table("`Order`");
}
}
public class OrderDTO
{
// Standard properties
public virtual int OrderId { get; set; }
public virtual DateTime OrderDate { get; set; }
public virtual string Address { get; set; }
// Child collection properties
public virtual IList<OrderLineDTO> OrderLines { get; set; } <-- this refs the lines
}
and:
public class OrderLineMapping : ClassMap<OrderLineDTO>
{
public OrderLineMapping()
{
// Standard properties
Id(x => x.OrderLineId);
References<OrderDTO>(x => x.Order).Column("OrderId");
Map(x => x.Description);
Map(x => x.Amount);
Table("`OrderLine`");
}
}
public class OrderLineDTO
{
// Standard properties
public virtual int OrderLineId { get; set; }
public virtual string Description { get; set; }
public virtual decimal Amount { get; set; }
public virtual OrderDTO Order { get; set; } // <-- this refs the order
}
These DTO objects map to Order and OrderLines CSLA objects respectively
When auto-mapping to OrderLines a list of OrderLinesDTO is mapped. Auto mapper is then mapping the "Order" property on of the lines, which maps back to Order which then circularly maps back to OrderLine, then to Order and so on
Does anyone know if Automapper can avoid this circular reference?
In your Automapper configuration:
Mapper.Map<OrderLine, OrderLineDTO>()
.ForMember(m => m.Order, opt => opt.Ignore());
Mapper.Map<Order, OrderDTO>()
.AfterMap((src, dest) => {
foreach(var i in dest.OrderLines)
i.Order = dest;
});
I was having the same issue using EF 6 and AutoMapper 6. Apparently what Kenny Lucero posted led me to the solution. Here's an extract from AM site:
// Circular references between users and groups
cfg.CreateMap<User, UserDto>().PreserveReferences();
Adding PreserveReferences() to both models made it work.
I was having the same issue and solved it by downgrading to version 4.2.1.
apparently the checks for circular references was expensive so they made it default to not check.
Migrating to AutoMapper 5 - Circular references
Supposedly these are supposed to be the settings methods for v 5+ but it didn't work for my data model because we opt'd for complex dto relationships instead of single use dtos for each action.
// Self-referential mapping
cfg.CreateMap<Category, CategoryDto>().MaxDepth(3);
// Circular references between users and groups
cfg.CreateMap<User, UserDto>().PreserveReferences();
http://docs.automapper.org/en/stable/5.0-Upgrade-Guide.html#circular-references
Automapper is supposed to be able to statically determine if the circular reference settings in v6.1+, So if it doesn't work for you automatically in version v6.1+ contact the automapper team.
Since this is the #1 google search result, I think there might be some people, like me, coming here who don't get a stackoverflow exception, but find trouble when sending the object (via ASP.NET) to the client, and thus it being JSON serialized.
So I had the same structure in place, Invoices has multiple InvoiceLines, when I load an Invoice and use the Linq-to-SQL .Include(x => x.InvoiceLines) I get errors when I try to load the object from the Api because each InvoiceLine contains the same Invoice again.
To solve this, do the following in ASP.NET Core Startup class:
services.AddMvc().AddJsonOptions(o =>
{
o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
o.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
// ^^ IMPORTANT PART ^^
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
So include o.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; in your JsonConfiguration when adding MVC to your application.
JSON.Net is taking the extra step to setup each reference with an additional meta-property called “$id”. When JSON.Net encounters the same instance in another place in the object graph, it simply drops a reference to the original instance, instead of duplicating the data, and thus not causing circular reference issues!
Source: https://johnnycode.com/2012/04/10/serializing-circular-references-with-json-net-and-entity-framework/
So now I don't have to further edit my AutoMapper configuration.
If anyone using Mapster (a mapping library for C# same as AutoMapper)
TypeAdapterConfig<TSource, TDestination>
.NewConfig()
.PreserveReference(true);
need to be used for preventing stack overflow error.
Not sure if I should post it here:
I had the same error after doing an automapper.map in a method.
The answer of CularBytes got me thinking that the issue was not automapper related but json related.
I did:
Return ok(_service.getDataById(id));
instead of
Return ok(await _service.getDataById(id));
(I forgot to await an asyc call... rookie mistake I know)
I have the following class which I am mapping using Fluent NHibernate's AutoMapper. I do not want the list items publically modifiable so have a public IEnumerable backed by an IList which can be populated internally or by NHibernate.
I want NHibernate to map teamMembers to a column named TeamMembers. I also want FNH to ignore the TeamMembers property on this class.
public class Team : Entity
{
protected internal virtual IList<Person> teamMembers { get; set; }
public IEnumerable<Person> TeamMembers { get { return teamMembers;} }
}
Here's how you tell NHibernate's Autopersistence model to ignore your property:
var cfg = Fluently.Configure()
.Database(configurer)
.Mappings(m =>
{
m.AutoMappings.Add(AutoMap.Assemblies(Assembly.GetExecutingAssembly())
.Override<Team>(map => map.IgnoreProperty(team => team.TeamMembers)));
});
You would then have just what you want.