Possibly a dumb question but I have a number of entities all inheriting from a base entity. The base entity does not have a table in the database. Each entity has its own table and the table definition is exactly the same. Extremely simplified example of the code is below.
public abstract class BaseEntity
{
public virtual string someProperty {get; set;}
}
public class Entity1 : BaseEntity{}
public class Entity2 : BaseEntity{}
public class CompletelyDifferentEntity
{
public virtual IList<BaseEntity> {get; set;}
}
I created the mappings for the entities. In my other domain classes if I reference the concrete classes everything works fine but if I change my other classes to reference BaseEntity instead I get a mapping Exception because the BaseEntity is not mapped. Is this something where I should use a subclass discriminator? I guess I'm not seeing the correct way to do this if the base doesn't have an associated table and the subclasses don't have a specific column that is different between the table definitions.
You have to use one of three available inheritance mappings strategies. From your description, you should consider using table-per-concrete-class mapping, or change your db scheme.
You can find more information about pros and cons of strategies here: https://www.hibernate.org/hib_docs/nhibernate/html/inheritance.html.
Related
My goal is to use NHibernate schema generation along with Fluent NHibernate's automapper to generate my database. I'm having trouble with what I'll call "unidirectional many-to-many relationships."
Many of my entities have localized resources. A single class might look like this:
public class Something {
public virtual int Id {get; private set;}
public virtual Resource Title {get;set;}
public virtual Resource Description {get;set;}
public virtual IList<Resource> Bullets {get;set;}
}
The Resource class doesn't have any references back; these are entirely unidirectional.
public class Resource {
public virtual int Id {get; private set;}
public virtual IList<LocalizedResource> LocalizedResources {get;set;}
// etc.
}
public class LocalizedResource { //
public virtual int Id {get; private set; }
public virtual string CultureCode {get;set;}
public virtual string Value {get;set;}
public virtual Resource Resource {get;set;}
}
Without the IList<Resource>, everything is generated as I'd want -- Resource ID's are in the Title and Description fields. When I add in the IList though, NHibernate adds the field something_id to the Resource table. I understand why it does this, but in this situation it's not a sustainable approach.
What I want is to create a junction table for the bullets. Something like:
CREATE TABLE SomethingBullet (
Id int NOT NULL PRIMARY KEY IDENTITY(1,1),
Something_Id int NOT NULL,
Resource_Id int NOT NULL
)
This way when I add the other twenty-odd entities into the database I won't end up with a ridiculously wide and sparse Resource table.
How do I instruct the Automapper to treat all IList<Resource> properties this way?
Every many-to-many is in fact composed with one-to-many's in object model. If your relationship doesn't need to be bidirectional, just don't map the second side. The mapping on your mapped side is not affected at all:
HasManyToMany(x => x.Bullets).AsSet();
In this case, NHibernate already knows that it needs to generate the intermediate table.
See also this article for many-to-many tips.
:)
The only way I found to make this work with automapping is by constructing your own custom automapping step and replacing the "native" HasManyToManyStep. It's either that or an override, I'm afraid.
I lifted mine off of Samer Abu Rabie, posted here.
The good news is that Samer's code, so far, seems to work flawlessly with my conventions and whatnots, so, once it was in place, it was completely transparent to everything else in my code.
The bad news is that it costs you the ability to have unidirectional one-to-many relationships, as Samer's code assumes that all x-to-many unidirectional relationships are many-to-many. Depending on your model, this may or may not be a good thing.
Presumably, you could code up a different implementation of ShouldMap that would distinguish between what you want to be many-to-many and what you want to be one-to-many, and everything would then work again. Do note that that would require having two custom steps to replace the native HasManyToManyStep, although, again, Samer's code is a good starting point.
Let us know how it goes. :)
Cheers,
J.
Take this following code from an example HR system. The user has the ability to log an absence and can be of various types including holiday and sickness. This would be a domain model over an ORM such as NHibernate.
public class Absence
{
public long Id {get;set;}
public Employee Employee {get;set;}
public DateTime StartDate {get;set;}
public DateTime EndDate {get;set;}
public virtual void DoSomething()
{ ... }
}
public class Holiday : Absence
{
public string Location {get;set;}
public override void DoSomething()
{ ... }
}
public class Sickness : Absence
{
public bool DoctorsNoteProvided {get;set;}
public override void DoSomething()
{ ... }
}
This is an example - please don't question why location would be required, assume it is a specification.
The user wants to change the type - he thought the employee was off sick but then remembered it was a holiday. Again, you may think this is a bad design but treat it like a requirement - this represents a problem that has come up many times for me.
The problem is that you cannot change the type of an object from Sickness to Absence. Generally, the advice would be to Favour Composition Over Inheritance (Gang of Four) and do this:
public class Absence
{
public long Id {get;set;}
public Employee Employee {get;set;}
public DateTime StartDate {get;set;}
public DateTime EndDate {get;set;}
public AbsenceType Type {get;set;}
public void DoSomething()
{
Type.DoSomething();
}
}
But when I do this, when do the properties specific to Holiday and Sickness go (Location and DoctorsNoteProvided respectively)?
Why do you need to change the type of an object?
You will have some kind of collection of Absences, just replace the item in question.
Conceivably rather than replacing you even keep the original request and mark it as superceded, that might be important for audit trail purposes.
It's not the right place for Composition over Inheritance. Here the inheritance is appropriate. And if you need to change the type of absence just create a new one and delete old.
Hmmm, without knowing more about your requirements, I would say the right design is not to change an Absence object to a Sickness object (or vice versa) but to just delete the one you don't want and create a new one of the type you do. Somewhere you must be maintaining a collection of absences, right?
You are correct that classes don't change.
I would model this by having a type hierarchy for an AbsenceType, or AbsenseReason:
abstract class AbsenseReason {
}
class HolidayAbsenseReason : AbsenseReason {
public string Name { get; }
}
I like this model because now AbsenseReason is a value object and is independent of an employee Absence, which is an entity object. This, as you stated, solves the issue with changing the absence reason. Generally speaking, I would favor this over deleting a record, because there may be many associations to consider as well.
Things to consider:
NHibernate does not support inheritance mappings on components so you will have to provide a custom implementation of IUserType.
Consider storing all the data for the different absence reason sub types together with the record for the employee absence entity. Possibly as XML so that you can have collections, etc.
So try to move all type specific functionality to AbsenceType derivatives. If they require something from parent class Absence, you could pass them its reference. Though I would try to avoid that.
If you manipulated Absence object via base class interface, nothing changes, you can keep your old code. Now, if you manipulated specific derivatives, then you will have to grab AbsenceType object from specific Absence and do all the same things on them - still not much to change. If you had holiday.DoSomething(), now you have holiday.Type.DoSomething().
At this moment we are keeping all Entities and Mappings into same assembly. Our entities derived from a basic class Entity which is an EntityWithTypedId
Also we are having a table name Convention telling to pluralize the table names.
Now I want to create other two base types e.q. AggregateRootEntity and AggregateEntity, both derive from Entity.
And I would like to create two set of conventions for both base entities:
Let's say:
For for all entities derived from AggregateRootEntity tables should be prefixed with "ag_" and Id is incremental generated, but for all entities derived from AggregateEntity tables should be prefixed with "a_" and Ids should be assigned.
Is it possible to Set Conventions based on some conditions?
You can do it with multiple conventions, each checking for a specific type in their Accept methods
something like:
public class LegacyEntityTableConvention : IClassConvention, IClassConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.EntityType.IsAny(typeof(OldClass), typeof(AnotherOldClass)));
}
public void Apply(IClassInstance instance)
{
instance.Table("tbl_" + instance.EntityType.Name);
}
}
Just a block of code out of the FNH Wiki
http://wiki.fluentnhibernate.org/Acceptance_criteria
I know in NHibernate you can have inheritance mappings, and I know you can have table-per-class, table-per-subclass and table-per-concrete-class but they don't quite fit the scenario I have.
Basically what I want is to be able to have a base class called product that looks like this:
public class BaseProduct
{
public virtual int ProductId {get;set;}
public virtual string ProductName {get;set;}
}
which maps directly to a product table.
Then I want to have a Product class which inherits from BaseProduct like this:
public class Product : BaseProduct
{
public virtual IList<Category> Categories {get;set;}
}
The thing is that the Product class should still map to the product table, the only difference being that this implementation has a list of Categories attached.
Without going into the technical reasons for why I need to do this, I would like to know if it's at all possible?
From your question and comment, I get that you want «Single Table Inheritance» [PoEAA, Fowler], but don't have the luxury of being able to add the needed discriminator to the table.
I've never run into this situation myself, but try to add a discriminator to your mapping which is a calculated value/derived field that uses sql to find out if there are foreign keys from Category (won't work for Products with empty Category collections though).
If your scenario is a read-only one, and you can add views to the DB, it's an option to map to a Product view with the discriminator calculated as stated above.
You are looking for a table perclass you need to set a discriminator-value
NHDoc for inheritance
Do you have other classes that inherit from BaseProduct? If not, you can just map the Product class only.
I have a following mapping:
<set name="People" lazy="true" table="ProjectPeople">
<key column="ProjectId" />
<composite-element class="PersonRole">
<many-to-one name="Person" column="PersonId" cascade="save-update" not-null="true" />
<many-to-one name="Role" column="RoleId" cascade="save-update" not-null="true" />
</composite-element>
</set>
Now, I do not really want to have a separate class for Role in domain, I need only the Role name. However, in DB Roles should still be normalized to a separate table Role (Id, Name).
How do I map it so that People use following PersonRole class?
public class PersonRole {
public virtual Person Person { get; set; }
public virtual string Role { get; set; }
}
Update: added bounty, seems like a question useful not only to me.
You won't actually get the answer you hope for, simply because it is not possible. (N)Hibernate is an Object-Relational-Mapping framework and support three kinds of mapping strategies:
table per class hierarchy
table per subclass
table per concrete class
It also allows you to deviate from this by using formula or sql-insert etc, but as you've found out, these only cause you more pain in the end, are not encouraged by the Hibernate community and are bad for the maintainability of your code.
Solution?
Actually, it is very simple. You do not want to use a class for Role. I assume you mean that you do not want to expose a class of type Role and that you do not want to have to type prObject.Role.Name all the time. Just prObject.Role, which should return a string. You have several options:
Use an inner class in, say, PersonRole, this class can be internal or private. Add a property Role that sets and updates a member field;
Use an internal class. Add a property Role that sets and updates a member field;
Let's examine option 2:
// mapped to table Role, will not be visible to users of your DAL
// class can't be private, it's on namespace level, it can when it's an inner class
internal class Role
{
// typical mapping, need not be internal/protected when class is internal
// cannot be private, because then virtual is not possible
internal virtual int Id { get; private set; }
internal virtual string Name { get; set; }
}
// the composite element
public class PersonRole
{
// mapped properties public
public virtual Person Person { get; set; }
// mapped properties hidden
internal virtual Role dbRole { get; set; }
// not mapped, but convenience property in your DAL
// for clarity, it is actually better to rename to something like RoleName
public string Role /* need not be virtual, but can be */
{
get
{
return this.dbRole.Name;
}
set
{
this.dbRole.Name = value; /* this works and triggers the cascade */
}
}
}
And the mapping can look as expected. Result: you have not violated the one-table-per-class rule (EDIT: asker says that he explicitly wants to violate that rule, and Hib supports it, which is correct), but you've hidden the objects from modification and access by using typical object oriented techniques. All NH features (cascade etc) still work as expected.
(N)Hibernate is all about this type of decisions: how to make a well thought-through and safe abstraction layer to your database without sacrificing clarity, brevity or maintainability or violating OO or ORM rules.
Update (after q. was closed)
Other excellent approaches I use a lot when dealing with this type of issue are:
Create your mappings normally (i.e., one-class-per-table, I know you don't like it, but it's for the best) and use extension methods:
// trivial general example
public static string GetFullName(this Person p)
{
return String.Format("{0} {1}", p.FirstName, p.LastName);
}
// gettor / settor for role.name
public static string GetRoleName(this PersonRole pr)
{
return pr.Role == null ? "" : pr.Role.Name;
}
public static SetRoleName(this PersonRole pr, string name)
{
pr.Role = (pr.Role ?? new Role());
pr.Role.Name = name;
}
Create your mappings normally but use partial classes, which enable you to "decorate" your class any which way you like. The advantage: if you use generated mapping of your tables, you an regenerate as often as you wish. Of course, the partial classes should go in separate files so considering your wish for diminishing "bloat" this probably isn't a good scenario currently.
public partial class PersonRole
{
public string Role {...}
}
Perhaps simplest: just overload ToString() for Role, which makes it suitable for use in String.Format and friends, but of course doesn't make it assignable. By default, each entity class or POCO should have a ToString() overload anyway.
Though it is possible to do this with NHibernate directly, the q. has been closed before I had time to look at it (no ones fault, I just didn't have the time). I'll update if I find the time to do it through Hibernate HBM mapping, even though I don't agree to the approach. It is not good to wrestle with advanced concepts of Hib when the end result is less clear for other programmers and less clear overall (where did that table go? why isn't there a IDao abstraction for that table? See also NHibernate Best Practices and S#arp). However, the exercise is interesting nevertheless.
Considering the comments on "best practices": in typical situations, it shouldn't be only "one class per table", but also one IDaoXXX, one DaoConcreteXXX and one GetDaoXXX for each table, where you use class/interface hierarchy to differentiate between read-only and read/write tables. That's a minimum of four classes/lines of code per table. This is typically auto-generated but gives a very clear access layer (dao) to your data layer (dal). The data layer is best kept as spartan as possible. Nothing of these "best practices" prevent you using extension methods or partial methods for moving Role.Name into Role.
These are best general practices. It's not always possible or feasible or even necessary in certain special or typical sitations.
Personally I would create a Role class like Yassir
But If you want to use the structure that you have at the moment then create a view that contains the foriegn Key to your Person Table and the Role Description.
Modify the Set mapping table to point at your new view
Then modify your Role mapping so that it is a property instead of the many to one mapping.
However taking this approach I think will mean that you will not be able to update your role as it is reerencing a view.
Edit: To update the role you could add <sql-insert>,<sql-update> and <sql-delete> to your mapping file so that the cascade-all will work
i don't think it is possible to map many-to-one to a primitive type if i were you i would add a Role class to the model
This the biggest turn off of the whole OO purist thing.
Surely the goal is to have a working application. Not somebodies version of a perfect class hierarchy. So what if you have to code "prObject.Role.Name " instead of "prObject.Role". How does this help you make a better more reliable program?
From the application design purist point of view what you want is just plain wrong. A person can have several roles, a role can usually be assigned to several people.
Why go to all this trouble to enforce an unrealistic one role per person class hierachy when the undelaying data model is many roles per person?
If you really do have an "only one role per person" rule then it should be refleced in the underlying data model.