Mapping this design in Fluent NHibernate - nhibernate

I'm trying to figure out the best way to accomplish a design that's persisted with NHibernate. Here's what the data would ideally look like:
Person
===============
Id (int)
Name (nvarchar)
Privileges
==============
Person_Id
Right (int)
AccessLevel (int)
Ideally, I'd be able to create classes like this (pretend I have all the NHibernate virtual modifiers):
public enum Rights
{
Read = 0,
Save = 1,
Delete = 2
}
public enum AccessLevels
{
LevelOne = 0,
LevelTwo = 1,
LevelThree = 2
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Dictionary<Rights, AccessLevels> Privileges { get; set; }
}
Originally I considered the design described in this blog post, but I don't want to make the Rights enum a class because I'd like to be able to test for it without having to keep track of the Id of a given right. A pair of enumerations would be easiest I think.
Anyway, I've been striking out figuring out how to accomplish this in NHibernate, much less Fluent NHibernate. I'm hoping some NH gurus might be able to help.

I'm not sure if it's possible... but try something like this (I did not test):
HasMany(x => x.Privileges)
.WithTableName("Privileges")
.KeyColumnNames.Add("Person_Id")
.Cascade.All()
.AsMap<string>(
index => index.WithColumn("Right").WithType<Rights>(),
element => element.WithColumn("AccessLevel").WithType<AccessLevels>()
);
}
Or something like this:
References(x => x.Privileges).AsMap<Rights>("Right").Element("AccessLevel", c => c.Type<AccessLevels>());.
Take a look at this thread: How do I map a dictionary using Fluent NHibernate automapping?

Related

NHibernate QueryOver distinct

I have this
scenario:
class User
{
Id,
UserName
}
class UserRelationship
{
User GroupUser,
User MemberUser
}
and query
var query = QueryOver.Of<UserRelationship>()
.JoinqueryOver(x=>x.MemberUser)
.Where(x=>x.UserName == "TestUser");
Now I want to return List Distinct User, so I cannot do
TransformUsing(Transformers.DistinctRootEntity)
because this will give me the UserRelationship.
I need something like this:
Select distinct user.ID
from UserRelationship relationship
inner join User user on user.ID = relationship.MemberUser_ID
Please help
thanks
Given the classes:
public class User
{
public virtual int Id {get; set;}
public virtual string UserName {get; set;}
}
public class UserRelationship
{
public virtual int Id {get; set;}
public virtual User GroupUser {get; set;}
public virtual User MemberUser {get; set;}
}
And the fluent mappings of:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x=>x.Id).GeneratedBy.Native();
Map(x=>x.UserName);
}
}
public class UserRelationshipMap : ClassMap<UserRelationship>
{
public UserRelationshipMap(){
Id(x=>x.Id).GeneratedBy.Native();
References(x=>x.GroupUser);
References(x=>x.MemberUser);
}
}
You want to retrieve a list of distinct "User" based on "MemberUser" from the UserRelationship class.
var distinctMemberUsers = QueryOver.Of<UserRelationship>()
.Select(x => x.MemberUser.Id);
var users = QueryOver.Of<User>()
.WithSubquery.WhereProperty(x=>x.Id).In(distinctMemberUsers)
This should use a In clause in the SQL to give you a distinct list of User.
I know this post is old but I just came across the same problem and thought I would share an answer I found to be much simpler.
No matter what - NHibernate will have to query multiple rows for each parent object (unless you use a SubSelect instead of a Join). Because of this, we know we're going to get a list of say, 500 objects, when there are really only 100 unique objects.
Since these objects are already queried, and already in memory - why not use LINQ?
Based on this question: LINQ's Distinct() on a particular property the answer with the most +'s gives a very eloquent solution. Create another list, and have LINQ do the distinct comparison. If we could do distinct at the database it would clearly be the better option - but since that's not an option, LINQ seems to be a good solution.

NHibernate, SQL Server - enum to int mapping

I have a class that has an enum type indicating whether the message type is Email or Sms. The enum type is defined:
public enum ReminderType
{
Email = 1,
Sms = 2
}
The class that utilizes this type looks like:
public class Reminder : EntityBase
{
public virtual string Origin { get; set; }
public virtual string Recipient { get; set; }
public virtual ReminderType Type { get; set; }
public virtual Business Business { get; set; }
public virtual DateTime Created { get; set; }
public Reminder()
{
Created = DateTime.UtcNow;
}
}
When I try to persist an entity of type Reminder to the database however, I get the following error:
System.Data.SqlClient.SqlException (0x80131904): Conversion failed when converting the nvarchar value 'Email' to data type int.
The backing field is of type int, so I'm not sure why NHibernate is trying to map the string representation by default. I'm using Fluent NHibernate, and the relevant mapping code is:
mappings.Override<Reminder>(map =>
{
map.Map(x => x.Type).Column("Type")
});
I'm pretty sure the default behavior of NHibernate is to map enums as ints, so why is it not doing so in this case? I'm using SQL Server 2005, if that matters.
I am doing the same thing and got it working like so...
In my case EmployeeType is the enum class
Map(x => x.EmployeeType, "EmployeeType_Id").CustomType(typeof (EmployeeType));
I don't know why this person keeps posting and then deleting their comment or answer, but the link they provided () does answer my question. I opted not to go with a full blow class definition for the convention, but rather, an inline convention in the mappings code, like so:
var mappings = AutoMap.AssemblyOf<Business>()
.Where(x => x.IsSubclassOf(typeof(EntityBase)))
.IgnoreBase(typeof(EntityBase))
.Conventions.Add
(
ConventionBuilder.Id.Always(x => x.GeneratedBy.Identity()),
ConventionBuilder.HasMany.Always(x => x.Cascade.All()),
ConventionBuilder.Property.Always(x => x.Column(x.Property.Name)),
Table.Is(o => Inflector.Pluralize(o.EntityType.Name)),
PrimaryKey.Name.Is(o => "Id"),
ForeignKey.EndsWith("Id"),
DefaultLazy.Always(),
DefaultCascade.All(),
ConventionBuilder.Property.When(
c => c.Expect(x => x.Property.PropertyType.IsEnum),
x => x.CustomType(x.Property.PropertyType))
);
The last convention builder statement did the trick. I'm curious as to why Fluent NHibernate's default is to map enums as strings now. That doesn't seem to make much sense.
You should never map Enum as int in NHibernate. It becomes a reason of having a ghost updates.
The best way to it is just not setting a type property in XML mappings. To achieve that in Fluent NHibernate you can use .CustomType(string.Empty).
Some additional info you can find here.

Setting CustomSqlType on References

I have a situation where my primary key is a char(2) in SqlServer 2008, and I want to reference it in a one-to-many relationship, but the ManyToOneBuilder (which is returned by ClassMap<>.References()) doesn't have a CustomSqlType() method. Specifically:
public class State
{
// state FIPS code is 2 characters
public virtual string StateCode { get; set; }
public virtual ICollection<County> { get; set; }
}
public class County
{
// state-county FIPS code is 5 characters
public virtual string StateCountyCode { get; set; }
public virtual State State { get; set; }
}
public class StateMap : ClassMap<State>
{
public StateMap()
{
Id(e => e.StateCode).CustomSqlType("char(2)").GeneratedBy.Assigned();
}
}
public class CountyMap : ClassMap<County>
{
public CountyMap()
{
Id(e => e.StateCountyCode).CustomSqlType("char(5)").GeneratedBy.Assigned();
References(e => e.State, "StateCode")
// Here's what I want to do, but can't because the method is not
// implemented on the class ManyToOneBuilder:
.CustomSqlType("char(2)");
}
}
Is there any way to accomplish this without modifying the ManyToOneBuilder? Is there a way to automatically map the FK (i.e. County.StateCode) to the correct type? It's trivial to add CustomSqlType to ManyToOneBuilder, but is that the right thing to do?
Keep your mapping definition as is, add your "State" column definition with
.CustomSqlType("char(2)")
and set for this column Insert=false and update=false.
I've the same problem and in AutoMapping I use this code:
mapping.Map(x => x.IdUniArticolo)
.CustomSqlType("varchar(50)")
.Not.Insert().Not.Update();
mapping.References(x => x.Articolo)
.Column("IdUniArticolo").PropertyRef("IdUniArticolo");
Keep in mind that if NHibernate itself doesn't support it, then Fluent NHibernate can't, and I don't NHibernate supports the scenario you have. I had a similar problem in that I had a 2 column composite key on a table and on one of the fields, I wanted to use an enumerated type which had a custom IUserType to translate it to its appropriate code value in the DB. Couldn't do it, so I was stuck keeping the property of the string type rather than the enumerated type.

Loading a base class through nhibernate incorrectly uses mappings from derived classes

I have a scenario where I have a base class as one entity, then another entity that derives from the other base class. Both have meaning in my domain and can be used separately.
public class MyBaseClass
{
int ID { get; set; }
string Name { get; set; }
}
public class MyChildClass
{
string AdditionalField { get; set; }
}
I have both mapped using Fluent nHibernate using ClassMap like this:
public class MyBaseClassMap : ClassMap<MyBaseClass>
{
Id("MyBaseClassID");
Map(x => x.Name);
}
public class MyChildClassMap : SubclassMap<MyChildClass>
{
Map(x => x.AdditionalField);
}
What is happening is when I try to fetch a copy of the base class, its using the mapping for the child class. Its as if it doesn't know the the difference between the base and child class, or its choosing the wrong mapping for it. I confirmed this by watching the SQL statement and its joining to the child table and fetching the additional column. Any way to get it to use the right map?
That's the 'nature' of NHibernate.
The behaviour you're describing, is called 'polymorphic queries'.
Since MyChildClass is a MyBaseClass, the MyChildClass instances are retrieved as well.
If you want to avoid this behaviour, you can maybe have a look at the answers in this topic. (I've never 'disabled' the polymorphic query ability).

Mapping interface on multiple hierarchies with NHibernate

Given 2 classes that are not related, one of which is a member of
another inheritance hierarchy, how can I map an interface on both of
the classes so that I can query against the interface and have the
appropriate concrete type returned?
E.g.
public abstract class Survey
{
public Guid Id { get; private set; }
}
public class InviteOnlySurvey : Survey
{
public ICollection<Invite> Invites { get; private set; }
}
public class Invite : ISurveyGateway
{
public Guid Id { get; private set; }
public InviteOnlySurvey Survey { get; private set; }
}
public class SharedSurvey : Survey, ISurveyGateway { ... }
public interface ISurveyGateway
{
Guid Id { get; }
}
Currently I have mapped Survey, InviteOnlyLiveSurvey and SharedLiveSurvey using table per class hierarchy and now I am trying to figure out how to map ISurveyGateway so that I can query against it and have NHibernate find the matching entity ( Invite or
SharedLiveSurvey ) seamlessly. ISurveyGateway instances are effectively readonly as all the remaining persistence concerns are managed through the mappings for SharedSurvey and Invite.
If I remove the ISurveyGateway interface from either SharedSurvey or Invite, I can query and retrieve ISurveyGateway instances via NHibernate, but as soon as I apply the interface to 2 different hierarchies I get an exception with the message "Ambiguous persister for ISurveyGateway implemented by more than one hierarchy" (which is expected - I just don't know how to make it work).
The answer with QueryOver and FutureValue works, but here is an even simpler solution:
public ISurveyGateway FindSurveyGatewayById( Guid id )
{
var surveyGateway = session
.QueryOver<ISurveyGateway>
.Where( s => s.Id == id )
.SingleOrDefault<ISurveyGateway>();
return surveyGateway;
}
But you should be careful, your Id should be a Guid. If it's not the case you may get multiple responses...
Given that both Invite and SharedSurvey are already mapped and being used and because the Ids are Guids which ensures that there will not be a SharedSurvey and an Invite with the same Id ( with a fairly high degree of certainty ) I found a far simpler approach.
public ISurveyGateway FindSurveyGatewayById( Guid id )
{
var sharedSurveyGateway = session.QueryOver<SharedSurvey>
.Where( s => s.Id == id )
.FutureValue<ISurveyGateway>();
var inviteGateway = session.QueryOver<Invite>
.Where( i => i.Id == id )
.FutureValue<ISurveyGateway>();
return sharedSurveyGateway.Value ?? inviteGateway.Value;
}
There are some downsides with this - the main one being that this query now has to be extended for every ISurveyGateway that is added to the system, but for now it does the job with the least complexity and with reasonable performance.