NHibernate Mapping: Save hierarchy to single table without discriminator - c#

I have a very extensible and modular application. I want to extend mapped Entites from other assemblies. Yet, I still need to operate on the base classes.
Example:
Parent class mapping in Assembly A:
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Persons");
Id(x => x.Id).GeneratedBy.Assigned();
}
}
Child class mapping in Assembly B:
public class EmployeeMap : SubclassMap<Employee>
{
public EmployeeMap()
{
Table("Persons");
Extends(typeof(Person));
KeyColumn("Id");
LazyLoad();
HasMany<Assignments>(x => x.Assignments).KeyColumn("Id").Inverse().Cascade.AllDeleteOrphan().LazyLoad().NotFound.Ignore();
}
}
Now whenever I create a Person in some code of Assembly A it is saved as Employee by NHibernate. And that leads to a class cast exception due to proxying whenever I save a Person and try to refresh it in Assembly A. Assembly A must not have a dependency on Assembly B.
I need to operate on the parent class in all methods of assembly A. The child class is only used in other assemblies.
How can I map something like that? How can I tell NHibernate to just save it as the parent class? I use SaveOrUpdate to persist the entities; How can I correctly extend entities and yet save them to the same table without discriminator? Can't NHibernate differentiate by object type? Is there a workaround?
I do not want to specify manual proxies as I'd have to create a proxy for every entity! I cannot use the visitor pattern due to dependency problems.
I need a way to extend a mapped entity in a different assembly without such problems! The database is legacy, I cannot change it. How would you work around the issue?

Your goal of mapping a hierarchy to a single table, without a discriminator, presents a conundrum. There must be SOMETHING in the data layer that gives the DAL a clue that a particular record is an Employee as opposed to just a Person. Since you aren't mapping any additional fields on the Persons table for an Employee, and not providing a discriminator, there's nothing about any single record of the Persons table that differentiates Persons from more-derived Employees.
Could you provide the code you're using to retrieve the Person records? In my experience, NHibernate queries virtually always require a domain type to hydrate. It may be that in this case, NHibernate is trying to create objects of the most derived type it knows, and because it can't tell the difference between a base Person and the more derived Employee, all Persons are Employees. You may try Linq, which forces strongly-typed object hydration, as opposed to HQL or other less strongly-referenced query.

Solved it by using a HasOne Mapping on the same Table and not using a subclass. This does not produce ideal code but is reather free of problems.

Related

How to access child tables with Entity Framework

I have following tables: Animal and ConcreteAnimal. I am going database first approach if it makes any difference in this example. Suppose they are separate entities. Then I have 2 DBSet(s) generated, and can update separately either of the table. Things change once I set ConcreteAnimal as child of Animal in entity designer. Entity Set field of ConcreteAnimal in entity designer becomes disabled and is automatically populated with the value of entity set of its parent class Animal. DBSet for ConcreteAnimal is not longer generated in the context. This means that I can no longer access the second, child, table. How am I supposed to add ConcreteAnimals to the database? Is it expected behavior? If yes, what is the logic behind it?
EDIT:
OK, I tried following code and it works.
using(var context = new MyEntities())
{
var concreteAnimal = new ConcreteAnimal
{
//setting properties here
//both for Animal and specific to ConcreteAnimal
};
context.Animals.Add(concreteAnimal);
context.SaveChanges();
}
Magic happened, and both tables were populated with correct data. This is good. However, it just does not look logical to me why we have DBSet for base class only?
You are currently using TPT Inheritance.
Common information are saved in the Animal table and specific information from a type in their own table.
You will find information you are looking in these articles:
Table per Hierarchy (TPH)
Table per Type (TPT)
Table per Concrete class (TPC)
Edit
Why we have DBSet for base class only
It's actually a good question, I cannot give an exact answer but from my opinion this is not implemented by default because it's not necessary and would be very confusing.
Just imagine some very complex TPT scenario with a lot of inheritance, they would have one DbSet for every concrete class (which could be easily hundred of additional DbSet), then now you will have to ask to yourself, from which DbSet to do you have to retrieve, add or remove this concrete type.
You can easily add the code yourself once it's generated (in a partial class) and it's will work.
public DbSet<ConcreteAnimal> ConcreteAnimals { get; set; }

Entity Framework Projections and Return Type

I've the following project class libraries structure on my solution:
Application.Domain.Models : Entities like User, Customer.
Application.DataAcess : IUserRepository, ICustomerRepository
Application.Business : IUserService, ..
For one operation i need only the CustomerName and CustomerAddress then i will use entity framework projection to return only this properties.
My question is, should i create a entity for store only this properties and return it from this operation or should i return a Customer entity with only these two properties and all others with no value?
If should create a new entity, what layer it should be put in? Domain.Models, Domain.AnotherFolder or Business?
Technically you should not be constructing invalid objects. So just fetch your entire entity and use what you need. This will also mean you can reuse some existing code. You can make another entity but this should compliment your domain model. It will go in domain. If you are simply retrieving the data to use outside your domain you can consider having a lightweight readonly query layer that just passes data to whoever wants to read it.

Mapping Properties of type of Complex Type to DB Columns in EF 6 using code first and custom EntityTypeConfiguration implementation

This is my first activity with great stack overflow, but absolutely not my first time to visit the website.
My Question:
In our project, we are extending the EntityTypeConfiguration to take control over the mapping of entities to the DB in code first fluent API.
What I am confused with is how it is possible to include the configuration of complex types in the ctor of derived configuration, i.e. The configuration of primitive properties is possible through "Property" method of base EntityTypeConfiguration, but when calling this method on properties of type of Complex Types, the project builds will stop at called method.
Actually, the DB will be generated by entity table containing multiple columns, representing the fields of the embedded Complex Type using "PROPERTYNAME_INNERFIELDNAME" naming convention, even though if the complex type be of class type.
So, I do not know how to overcome with the issue if the complex type be of class type.
Thank you for your kind consideration.
My Code:
class Entity
{
long Id;
Address Addr;
}
class Address
{
string City;
string ZipCode;
}
class EntityConfig : EntityTypeConfiguration<Entity>
{
EntityConfig()
{
Property(p=>p.Addr); //Build error
}
}
Firstly, thanks to dear Bardware.
My problem has been solved by using following configuration:
Property(p=>p.Addr.City);
Property(p=>p.Addr.ZipCode);
Actually, I could not use "ComplexTypeConfiguration" instead of EntityTypeConfiguration because I would loose my chance to use HasKey, HasRequired etc.

EF 4.3.1 How to map a sub sub class with table per type

I have an abstract base class called Party. There are several concrete subclasses (Company, Person, Department). Party has a property called PartyType which is use as the discriminator. Each type is in its own table with configurations like
Map<Person>(p => p.Requires("PartyType").HasValue("Person").ToTable("People");
Everything works well.
Now I want to add a subclass of Person called Employee. How do I map this? I've tried
Map<Employee>(e => e.Requires("PartyType").HasValue("Employee")
.ToTable("Employees");
but this gives a runtime error of
(43,10) : error 3032: Problem in mapping fragments starting at lines
43, 84:EntityTypes WOL.EFData.Person, WOL.EFData.Employee are being
mapped to the same rows in table People. Mapping conditions can be
used to distinguish the rows that these types are mapped to.
In table per type mapping EF does not expect a discriminator configuration.
modelBuilder.Entity<Person>().ToTable("People");
modelBuilder.Entity<Employee>().ToTable("Employees");
See this article for more information.

Is there any way to view Entity Framework Code First's column mappings at runtime?

I'm trying to write an add-on to Entity Framework Code First and I need a way to get the configuration of the model columns at run time. For example, this is the code setup on OnModelCreating by the DbModelBuilder:
builder.Entity<NwdEmployee>()
.Property(n => n.ReportsToID).HasColumnName("ReportsTo");
Once this is done, EntityFramework knows that my property's name is different to the column name in the table, but how can I find that the string "ReportsTo" relates to ReportsToID myself at runtime? Ideally, I'm trying to write a method such as a following:
public string GetMappedColumnName<TFrom>(DbContext context,
Func<TFrom, object> selector);
Which would be used like:
string mappedColumnName = GetMappedColumnName<NwdEmployee>(context,
x => x.ReportsToID);
I just don't know where to find the mapped column names within the DbContext. Are they even accessible?
Theoretically yes. Practically I'm not sure because with simple test I wasn't able to get those information at runtime - I see them in debugger but I cannot get them because the type I need to use is internal in entity framework.
The theory. All mapping information are available at runtime but not through reflection. They are stored in the instance on MetadataWorkspace class which was definitely not designed for direct usage because every interaction with this class demands some time spend in debugger before you find how to get data you need. This data are not accessible through DbContext API. You must convert DbContext back to ObjectContext and access the MetadataWorkspace.
ObjectContext objContext = ((IObjectContextAdapter)dbContext).ObjectContext;
GlobalItem storageMapping = objContext.MetadataWorkspace.GetItem<GlobalItem>("NameOfYourContextClass", DataSpace.CSSpace);
Now storageMapping is instance of System.Data.Mapping.StorageEntityContainerMapping class which is internal. As I understand it this class should be runtime representation of MSL = mapping between storage and conceptual model.
If you use debugger you can explore the instance and you will find information about mapping between properties and columns (its quite deep nested) so you can also use reflection to get them but it is reflection on non public interface of classes you don't own so any .NET framework patch / fix / update can break your application.

Categories