Fluent NHibernate Map Inherited Class - fluent-nhibernate

Working on a legacy application at the moment and we are replacing the data access with NHibernate, since the old data access didn't do eager/lazy loading, parts of the system were doing 2000+ queries to create relationships...
Everything is fine except I have absolutely no idea how to map one scenario.
We have a 'Shift' and a 'MultiResourceShift'
public class Shift
{
public int Id { get; protected set; }
public string EmployeeName { get; set; }
public IEnumerable Breaks { get; set; }
...
}
Multi Resource Shift on the otherhand, is effectively a Shift, that can have multiple employees assigned to it.
public MultiResourceShift : Shift
{
public IEnumerable Employees { get; set; }
public bool IsMultiResource { get; set; }
}
The database structure is mapped as a one-to-many like:
+--------------------+
| Shift |
+--------------------+
| ShiftId |
| EmployeeName |
| IsMultiResource |
| ... |
+--------------------+
+--------------------+
| MultiResourceShift |
+--------------------+
| MultiResourceId |
| ShiftId |
| EmployeeId |
| ... |
+--------------------+
Ideally this needs to be queried where the result returns a collection of Shifts, or MultiResourceShifts.
At the moment this is achieved by iterating over the reader, and if it's a Shift it's Mapped and added to the collection, if it's a Multi Resource, an instance of MultiResourceShift is created and populdated from the shift data, and the Employees are loaded, and it's then added to the collection.
Anyone know if this is possible, how I would map it and query it.

http://nhforge.org/blogs/nhibernate/archive/2011/02/16/get-load-polymorphism-in-nhibernate-3.aspx
This blog post on the NH blog is exactly what I was after and solved the problem. We changed our model slightly to create the discriminator.

Related

How to use a inner join in Fluent NHibernate to fetch a single property from the right table

I have a query that fetches every property from one table, however the query should fetch one additional property from another table.
I'm using a simple example where querying every property from the second table wouldn't have any affect in performance, however the real situation does have many properties.
This is the queried table model:
public class Car
{
public long Id;
public long SellerId;
public string Name;
public string SellerName;
}
However, in the SellerName property is not being mapped, since this property is in another model:
public class Seller
{
public long Id;
public string SellerName;
}
The Car class mapping is currently as follow:
Property(x => x.Id);
Property(x => x.SellerId);
Property(x => x.Name);
This will create a model like this one:
Car Table
|------------------------------------------------|
|Id | SellerId | Name |
|1 | 1 | Car Number one |
|------------------------------------------------|
Employee Table
|---------------------------|
|Id | Name |
|1 | John Doe |
|---------------------------|
Using a simple query I can easily use an inner join to gather the Car data with the related employee name:
select c.*, p.Name from Car c
inner join Employee e on c.Id = e.EmployeeId
I've already tried this query:
var query = from car in session.Query<Car>()
join employee in session.Query<Employee>() on car.SellerId equals employee.Id
select new Car
{
Id = car.Id,
SellerId = car.SellerId,
Name = car.Name,
ClientName = employee.ClientName
};
But this actually creates two queries in the database, one for the Client table and another one for the Employee one. And this isn't using Linq, which is also a requirement.
First point here is, you have to use virtual properties for NHibernate's behaviour. You could map the relation on your model, for sample
public class Car
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual long SellerId { get; set; }
public virtual Employee Seller { get; set; }
}
And your map,
public class CarMap : ClassMap<Car>
{
public CarMap()
{
// map other properties...
// You could map the the SellerId to be easy to access here (just readonly, use the reference to persist)
Map(x => x.SellerId).Column("SellerId").Not.Nullable().ReadOnly();
References(x => x.Seller).Column("SellerId");
}
}
Given you have a right mapped model and you are using Linq, you could use Fetch method from NHibernate.Linq namespace and it will force a join for you avoiding the lazy loading. For sample:
var data = session.Query<Car>()
.Fetch(x => x.Seller)
.ToList();

How to validate a UserProfile at the begining of a Controller to show specific data

I have a UserProfile Model as the following:
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Position { get; set; }
public string Authority { get; set; }
public string Service { get; set; }
public string Area { get; set; }
public int IsWrite { get; set; }
}
the table data looks like the following:
UserID | UseName | Position | Authority | Service | Area | IsWrite
1 | Peter | HSK Manager | Svc-Ar | HSK | WA | 0
2 | Dorothy | Branch Manager| Ar | All | EA |1
3 |Mike | HSK Director | Svc | HSK | All | 0
4 | Roddel | HSK Data Entry | Svc | HSK | All | 1
5 | Susan| WA Data Entry |Ar | All | WA | 1
6 | Nancy| Facility COO | All | All | All | 0
7 | Allan | Food Branch Mananger | Svc-Ar | FSD | EA | 0
Users has many kinds of authority scope; for example: one has an authority upon a specific service, the other has an authority upon a specific area, and may a user has authority to view records upon a service and an area both. I want to make a logic for each List controller action to check the UserProfile authority and then list data considering the scope of authority for each user.
this is another model which its data depends on the authority of users called Projects:
public class Projects
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ProjectID { get; set; }
public string Service { get; set; }
public string Area { get; set; }
[Required]
[DisplayName("Project Name")]
public string ProjectName { get; set; }
[Required]
[DataType(DataType.Currency)]
[DisplayName("Contract Value")]
public double ContractValue { get; set; }
[Required]
[DisplayName ("Contract Total MP")]
public int ContractMP { get; set; }
[Editable(false)]
public int UserID { get; set; }
[DataType(DataType.DateTime )]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime EntryDateTime { get; set; }
}
I want to put a logic in the beginning of the Projects Controller 'Index' action to validate the user's authority; if it's "Svr" it will show only the data of his service, if his authority is "Ar" it will show only data of his Area only, if his Authority is "Svr-Ar" it will show data of his Service and Area only, and if his authority is "All" it will show all data.
I tried many ways to do this, I could not, and I am new to MVC. please help me. Regards.
May be you'd better try to use roles. Then you may simply check:
if(Roles.IsUserInRole("Ar"))
{
//Show data for role Ar
}
But you may do the same with your Authority field. Just make simple method:
public string GetUserAuthority(int UserID)
{
var user = db.UserProfile.Find(id);
return user.Authority;
}
or if you use Membership:
public string GetUserAuthority()
{
var user = Membership.GetUser(User.Identity.Name);
return user.Authority;
}
Then check in Controller Action:
if(GetUserAuthority() == "Ar")
{
//Show data for role Ar
}

NHibernate joining two table into one entity with a composite key

I have the following data structure :
+---------+
|Resume |
+---------+
|Id (PK) |
|IsActive |
|... |
|.. |
|. |
+---------+
+--------------------+
|Resume_Translation |
+--------------------+
|ResumeId (PK, FK) |
|Language (PK) |
|Title |
|Description |
|... |
|.. |
|. |
+--------------------+
So I could have such a data with two joined tables :
+----------------------------------------------------------+
|Id | IsActive | ResumeId | Language | Title | Description |
+----------------------------------------------------------+
|1 | true | 1 | 'fr' | 'One' | 'One desc' |
|1 | true | 1 | 'pl' | 'Raz' | 'Raz Opis' |
|2 | true | 2 | 'fr' | 'B' | 'bla bla' |
|3 | true | 3 | 'fr' | 'C' | 'C bla bla' |
+----------------------------------------------------------+
From my domain point of view I care only about Resume entity. I don't want to have Resume entity with its collection of Resume_Translations because I would only have one Resume entity with a current translation.
public class Resume
{
public virtual int Id{ get; protected internal set; }
public virtual string Language { get; protected internal set; }
public virtual string Title { get; protected internal set; }
public virtual string Description { get; protected internal set; }
public virtual bool IsActive { get; protected internal set; }
}
My current mapping with Fluent NHibernate is as follows :
public class ResumeMap : ClassMap<Resume>
{
public ResumeMap()
{
Table("Resume");
Id(x => x.Id);
Map(x => x.IsActive);
// other properties
Join("Resume_Translation", m =>
{
m.Fetch.Join();
m.Map(x => x.Language).Length(5);
m.Map(x => x.Title).Length(100);
m.Map(x => x.Description).Length(200);
});
}
}
I can get what I want from the repository without problem just passing in the WHERE predicate the Id of Resume and the Language I want to.
However I have some problems with Inserting and Updating the values.
My question is: How I would define a mapping that NHibernate Inserts a new record only in Resume_Translation table instead of Updating the record for the current entity ?
So what I want to achieve is if I have in the database the following record :
|2 | true | 2 | 'fr' | 'B' | 'bla bla' |
Join is good for one to one relationship between tables so if I get this into my entity and I change the language and translation, nhibernate is performing an update and I can understand it. If I try to add a new entity with the same Id by different language and translation, nhibernate yields an error that a key already exists and I understand it also.
So, certainly I'm going down the wrong path, but If some one could point me to the correct solution on how I could achieve a mapping that I want I would greatly appreciate.
Another question, how do you deal with a entities and theirs translations from the business point of view ?
Thanks, in advance for your help.
Thomas
Stefan is on the right track. I've tweaked his suggestion to have a bi-directional association which would make updating a lot easier. One catch with this approach is that you need to manually assign the Resume property of the ResumeTranslation instance when inserting so that NHibernate will properly assign the Resume table key to the ResumeTranslation row. So, given the associations you are mapping, this is how it would look in Fluent NH:
public class ResumeTranslation
{
public virtual string Title { get; protected internal set; }
public virtual string Description { get; protected internal set; }
//Needed for bi-directional association:
public virtual Resume Resume { get; set; }
}
public class ResumeTranslationMap : ClassMap<ResumeTranslation>
{
public ResumeTranslationMap()
{
Table("ResumeTranslation");
CompositeId()
.KeyReference(kp => kp.Resume, "ResumeId")
.KeyProperty(kp => kp.Language, "Language");
Map(x => x.Title);
Map(x => x.Description);
}
}
public class ResumeMap : ClassMap<Resume>
{
public ResumeMap()
{
Table("Resume");
Id(x => x.Id);
Map(x => x.IsActive);
// other properties
HasMany(c => c.Translations)
.Inverse()
.KeyColumn("id") //May not be required but here for reference
.Cascade.All();
}
}
Seems like a one to many relationship to me. I would personally have a collection of ResumeTranslation objects within my Resume object. I would then map this as a standard one to many.
You could then add another property ActiveResumeTranslation to your Resume entity that is representative of your current translation.
What about using a dictionary, using the language as a key?
public class ResumeTranslation
{
public virtual string Title { get; protected internal set; }
public virtual string Description { get; protected internal set; }
}
public class Resume
{
public virtual int Id{ get; protected internal set; }
// language is the key to the translation
// you may even want to hide the dictionary from the public interface of
// this class and only provide access to a "current" language.
public virtual IDictionary<string, ResumeTranslation> Translations { get; private set; }
public virtual bool IsActive { get; protected internal set; }
}
And map it accordingly as a map with a composite-element (sorry, I'm not using fluent, so don't ask me how it would look like). It would exactly match your database model.

Nhibernate ManyToMany with a dynamic where clause

I'm having some issues mapping a complex many to many relationship in fluentnhibernate. I have a legacy db which looks something like this:
Foos: | Id | Foo |
FooBars: | FooId | BarId |
Bars: | Id | Bar | CultureId |
which I am trying to map to the following object model:
class Foo
{
property virtual int Id { get; set; }
property virtual string Foo { get; set; }
property virtual IList<Bar> Bars { get; set; }
}
class Bar
{
property virtual int Id { get; set; }
property virtual int CultureId { get; set; }
}
with the mappings:
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Table("foos");
Id(v => v.Id);
Map(v => v.Foo);
HasManyToMany(v => v.Bars)
.Table("FooBars")
.ParentKeyColumn("FooId")
.ChildKeyColumn("BarId")
.Cascade.All();
}
}
public class BarMapping : ClassMap<Bar>
{
public BarMapping()
{
Table("bars");
Id(v => v.Id);
Map(v => v.Bar);
Map(v => v.CultureId);
}
}
The problem is I have multiple Bar's with the same Id for different CultureIds
e.g.
I would have a table that looks like:
Id|Bar|CultureId
1, Hello, 1
1, Bonjour, 2
1, Gutentag, 3
At the moment, the Bars property for the above table will return 3 elements but the Bar property on it will return "Hello" for all three elements (presumably because they all have the same identity). So my question is, how can I either stop this happening or can anyone suggest a way of filtering rows that do not have the correct culture id (note, this is dynamic & based on the current culture)?
You can't create dynamic where clauses in your mappings. You're going to need to query this collection instead of accessing it via the parent, using a Criteria or HQL query. You could read up on filters, but they still require a query.

Using NHibernate with an EAV data model

I'm trying to leverage NH to map to a data model that is a loose interpretation of the EAV/CR data model.
I have most of it working but am struggling with mapping the Entity.Attributes collection.
Here are the tables in question:
--------------------
| Entities |
--------------------
| EntityId PK |-|
| EntityType | |
-------------------- |
-------------
|
V
--------------------
| EntityAttributes | ------------------ ---------------------------
-------------------- | Attributes | | StringAttributes |
| EntityId PK,FK | ------------------ ---------------------------
| AttributeId FK | -> | AttributeId PK | -> | StringAttributeId PK,FK |
| AttributeValue | | AttributeType | | AttributeName |
-------------------- ------------------ ---------------------------
The AttributeValue column is implemented as an sql_variant column and I've implemented an NHibernate.UserTypes.IUserType for it.
I can create an EntityAttribute entity and persist it directly so that part of the hierarchy is working.
I'm just not sure how to map the EntityAttributes collection to the Entity entity.
Note the EntityAttributes table could (and does) contain multiple rows for a given EntityId/AttributeId combination:
EntityId AttributeId AttributeValue
-------- ----------- --------------
1 1 Blue
1 1 Green
StringAttributes row looks like this for this example:
StringAttributeId AttributeName
----------------- --------------
1 FavoriteColor
How can I effectively map this data model to my Entity domain such that Entity.Attributes("FavoriteColors") returns a collection of favorite colors? Typed as System.String?
here goes
class Entity
{
public virtual int Id { get; set; }
internal protected virtual ICollection<EntityAttribute> AttributesInternal { get; set; }
public IEnumerable<T> Attributes<T>(string attributeName)
{
return AttributesInternal
.Where(x => x.Attribute.Name == attributeName)
.Select(x => x.Value)
.Cast<T>();
}
}
class EntityAttribute
{
public virtual Attribute Attribute { get; set; }
public virtual object Value { get; set; }
}
class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
HasMany(e => e.AttributesInternal)
.Table("EntityAttributes")
.KeyColumn("EntityId")
// EntityAttribute cant be an Entity because there is no real Primary Key
// (EntityId, [AttributeId] is not unique)
.Component(c =>
{
c.References(ea => ea.Attribute, "AttributeId").Not.LazyLoad();
c.Map(ea => ea.Value, "AttributeValue").CustomType<VariantUserType>();
});
}
}