fluent nhibernate mapping - nhibernate

I am trying to create a map to get results as like from below query.
I am having hard time to get set Product mapping to set References to Product_Line on 3 columns as in where condition. How can I achieve this using fluent?
Product table: cId, ProjID, Line, etc., columns
Product_Line table: cId, ProjID, Line, etc., columns
select f.* from Product f
join Product_Line v on f.cId = v.CId and f.ProjID = v.ProjID and f.line = v.line
Thanks in Advance.
RajeshC
First, thank you for looking into it and Here with more info:
//Req: I want to query product such that if there is NO ProductLine, then I want to create a ProductLine, if there is one, then I'll update it.
public class ProductMap : ClassMap<Product>
{
Id(x => x.Id);
Map(x => x.CustomerId, "CustId");
Map(x => x.ProjId, "PROJId");
Map(x => x.LineNumber, "LineNumber");
Map(x => x.ReportType, "ReportType");
// Reference to Product_Line? - this reference should be based on three columns (custId, ProjId, LineNumber)
References(x => x.Line);
}
public class ProductLineMap : ClassMap<ProductLine>
{
Table("Product_Line");
Map(x => x.CustomerId, "CustId"); //same column as Product.CustId
Map(x => x.ProjId, "PROJId"); //Same as Product.ProjId
Map(x => x.LineNumber, "LINENUMBER"); //Same as Product.LineNumber
//etc.,
//for me, this reference is not needed as I need from Product to ProductLine - one way.
//References(x => x.Product).Column("ProjId") //
}

We could give you a much better answer if you showed us your C# code and wrapped the SQL in < code > tags... Here's my guess at what I think you want:
public class ProductMap : ClassMap<Product>
{
Id(x => x.Id);
References(x => x.Line); // Reference to Product_Line?
// etc.
}
public class ProductLineMap : ClassMap<ProductLine>
{
Table("Product_Line");
Id(x => x.Id).Column("cId");
References(x => x.Product).Column("ProjId")
}

Related

nHibernate One to One property null when loaded

I have a One to One relation between a TimeRecord and the Location.
This implementation is exactly the same es described in documentation:
https://github.com/jagregory/fluent-nhibernate/wiki/Fluent-mapping
public class TimeRecordMap : ClassMap<TimeRecord>
{
public TimeRecordMap()
{
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.StartTime);
Map(x => x.EndTime);
HasOne(x => x.Location).Cascade.All();
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.Id);
Map(x => x.Longitude);
Map(x => x.Latitude);
Map(x => x.Adress);
References(x => x.TimeRecord).Unique();
}
}
Now I query my TimeRecords with the following method:
public IList<TimeRecord> GetTimeRecords(string userid)
{
var query = Session.Query<TimeRecord>().Where(tr => tr.User.Id == userid);
return query.ToList();
}
Unfortunalelty my Location object is always null even if there is a coresponding entry in Location table but when I query for the coresponding Location with the desired TimeRecordId it is returned correctly.
See code here (code is inside a loop -> trCurrent is the current object in list received from "GetTimeRecords")
Location location = _locationRepo.getLocationByTimeRecordId(trCurrent.Id);
//trCurrent.Location = location; <- don't want to do it that way
if (trCurrent.Location != null)<- always null
{
... do stuff here
}
Implementation of my LocationRepository method:
public Location getLocationByTimeRecordId(int timeId)
{
var query = Session.Query<Location>()
.Where(tr => tr.TimeRecord.Id == timeId && tr.IsDeleted == false);
List<Location> lstReturn = query.ToList();
if (lstReturn.Count() == 0)
{
return null;
}
else
{
return lstReturn.First();
}
}
Can someone tell me why my Location is not resolved corretly?
Cheers,
Stefan
People claim that
HasOne / one-to-one is usually reserved for a special case. Generally, you'd use a References / many-to-one relationship in most situations (see: I think you mean a many-to-one). If you really do want a one-to-one, then you can use the HasOne method.
If you really do want a one-to-one and use it, you should remember that entities are joined by their ids by default.
If you check generated SQL you'll see something like JOIN Location ON Location.Id = TimeRecord.Id.
In order to get SQL like JOIN Location ON Location.TimeRecordId = TimeRecord.Id you should specify the foreign key via PropertyRef() method. So your mapping could be the folloving:
public class TimeRecordMap : ClassMap<TimeRecord>
{
public TimeRecordMap()
{
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.StartTime);
Map(x => x.EndTime);
HasOne(x => x.Location).Cascade.All().PropertyRef(it => it.TimeRecord);
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.Id);
Map(x => x.Longitude);
Map(x => x.Latitude);
Map(x => x.Adress);
References(x => x.TimeRecord/*, "TimeRecordId"*/).Unique().Not.Nullable();
}
}
In order to make sure that any location has TimeRecord you can add .Not.Nullable() into your LocationMap class.

Problems with updating records through Many-To-Many mappings

I have a problem where I have a many-to-many mappings in my table structure creating headaches when trying to edit a simple record.
Example layout of where I am having problems:
Facilities Many-to-One Locations
Facilities One-to-Many Users
Users Many-to-Many Locations
Users One-to-Many PreviousPasswords
If I make a change to a Facilities record (Change the name field) I get the following error upon save:
collection [Users.PreviousPasswords] was not processed by flush()
Mapping looks like:
public FacilitiesMap()
{
Table("Facilities");
Id(x => x.ID);
Map(x => x.Name);
HasMany(x => x.Users).KeyColumn("FacilitiesID").Cascade.AllDeleteOrphan().Inverse();
HasMany(x => x.Locations).KeyColumn("FacilitiesID").Cascade.AllDeleteOrphan().Inverse();
}
public UsersMap()
{
Table("Users");
Id(x => x.ID);
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Facilities, "FacilitiesID").ForeignKey("ID");
HasMany(x => x.PreviousPasswords).KeyColumn("UsersID").Cascade.AllDeleteOrphan().Inverse();
HasManyToMany<Locations>(x => x.Locations)
.Schema("Members")
.Table("UsersToLocations")
.ParentKeyColumn("UsersID")
.ChildKeyColumn("LocationsID")
.LazyLoad()
.Cascade.SaveUpdate().Inverse();
}
public LocationsMap()
{
Table("Locations");
Id(x => x.ID);
Map(x => x.Name);
References(x => x.Facilities, "FacilitiesID").ForeignKey("ID");
HasMany(x => x.Patients).KeyColumn("LocationsID").Cascade.AllDeleteOrphan().Inverse();
HasManyToMany<Users>(x => x.Users)
.Schema("Members")
.Table("UsersToLocations")
.ParentKeyColumn("LocationsID")
.ChildKeyColumn("UsersID")
.Cascade.SaveUpdate();
}
public PreviousPasswordsMap()
{
Table("PreviousPasswords");
Id(x => x.ID);
Map(x => x.Password);
Map(x => x.DateTime);
References(x => x.Users, "UsersID").ForeignKey("ID");
}
The only way I can do a successful update to the Facilities record is if I use the following function to get the record before changing and saving it:
public Facilities GetFacility(int id)
{
return FluentSessionManager.GetSession()
.CreateCriteria<Facilities>()
.Add(Expression.Eq("ID", id))
.SetFetchMode("Users", FetchMode.Eager)
.SetFetchMode("Locations", FetchMode.Eager)
.UniqueResult<Facilities>();
}
The problem with this method is that where there are 10,000 users it takes a long time to process this query. Or even worse, if we have 100 location as well, then it takes around 2 minutes to get the one Facilities record to edit.
I am sure there is some kind of issue in the Mapping. Not sure how to fix or even where to start. Any help with this would be greatly appreciated.
Thanks in advance.
Do you really need all the users for the facility? When you only add users you can use
HasMany(x => x.Users).ExtraLazyLoad();
and to improve the query when really all subcollections are needed
public Facilities GetFacility(int id)
{
var session = FluentSessionManager.GetSession();
// ignore the result, we only want to cache the results in the session
session.CreateCriteria<Facilities>()
.Add(Expression.Eq("ID", id))
.SetFetchMode("Users", FetchMode.Eager)
.Future<Facilities>();
return session.CreateCriteria<Facilities>()
.Add(Expression.Eq("ID", id))
.SetFetchMode("Locations", FetchMode.Eager)
.FutureValue<Facilities>().Value;
}

Fluent Nhibernate Many-to-Many mapping with extra column, update

Can not update / insert data into the database
This is the tables I have:
Role (Id, Name, ...)
Privilege (Id, Name, ...)
RolePrivilege (Id, RoleId, PrivilegeId, IsAllowed)
I have map:
public class RoleWithPrivilegesEntityMap : ClassMap<RoleWithPrivilegesEntity>
{
public RoleWithPrivilegesEntityMap()
{
Table( "ROLE" );
Not.LazyLoad();
Id( x => x.Id, "ID" ).GeneratedBy.Identity();
Map( x => x.Name, "NAME" ).Not.Nullable().Length( 100 );
HasMany(x => x.RolePrivileges)
.Cascade.AllDeleteOrphan()
.Inverse()
.Fetch.Join().KeyColumn("RoleId");
}
}
public class PrivilegeEntityMap : ClassMap<PrivilegeEntity>
{
public PrivilegeEntityMap()
{
Table("PRIVILEGE");
Id(x => x.Id, "ID").GeneratedBy.Identity();
Map(x => x.Name, "NAME").Not.Nullable();
HasMany(x => x.RolePrivileges)
.Cascade.AllDeleteOrphan()
.Inverse()
.Fetch.Join().KeyColumn("PrivilegeId");
}
}
public class RolePrivilegeEntityMap : ClassMap<RolePrivilegeEntity>
{
public RolePrivilegeEntityMap()
{
Table("ROLEPRIVILEGE");
Id(x => x.Id, "R_ID").GeneratedBy.Identity();
Map(x => x.IsAllowed, "ISALLOWED").Not.Nullable();
References(x => x.Role)
.Not.Nullable()
.Column("R_ID");
References(x => x.Privilege)
.Not.Nullable()
.Column("P_ID");
}
}
I'm trying to update existing record
session.SaveOrUpdate(rolePrivilege)
database make insert into table RolePrivilege, but I need to at first delete record.
what am I doing wrong?
normally nothing will go to the database at all because you never call session.Flush() or transaction.Commit(). However with Id generation Identity you basicly force NHibernate to insert immediately hence you see the INSERT statement.
You can also get rid of the Id in RolePrivilegeEntity and form a compositeId over Role and Priviledge which would also enhance performance (no need to insert immediatly).

How do you make non foreign key constraints in NHibernate?

I am using fluent nhibernate(v1.2) and nhibernate(v3.1) and I am having a weird Problem.
I have this
public class GradeMap : ClassMap<Grade>
{
public GradeMap()
{
Id(x => x.GradeId);
Map(x => x.TaskName).NvarcharWithMaxSize().Not.Nullable();
Map(x => x.Weight).Not.Nullable().Precision(5).Scale(2);
Map(x => x.OutOf).Not.Nullable().Precision(5).Scale(2);
Map(x => x.Mark).Not.Nullable().Precision(5).Scale(2);
Map(x => x.CourseBackgroundColor).Not.Nullable();
Map(x => x.CoursePrefix).Not.Nullable();
References(x => x.Student).Not.Nullable();
References(x => x.Course);
}
}
public class CourseMap : ClassMap<Course>
{
public CourseMap()
{
Id(x => x.Id).Column("CourseId");
Map(x => x.Prefix).NvarcharWithMaxSize().Not.Nullable();
HasMany(x => x.Tasks).Cascade.Delete().Inverse();
HasMany(x => x.CoursePermissions).Cascade.All().Inverse();
HasMany(x => x.CourseSharing).Cascade.All().Inverse();
HasMany(x => x.Grades).Cascade.None().Inverse();
}
}
I then do something like this
return session.Query<Grade>().ToList();
If I would try to grab a propety such as Grade.Course.Id it would crash and I would get.
Grade.Course = {Castle.Proxies.CourseProxy}
Grade.Course.Id = '((new System.Collections.Generic.Mscorlib_CollectionDebugView<OnlGrade>(grades)).Items[0].Course).Id' threw an exception of type 'NHibernate.ObjectNotFoundException'
Grade.Course.Prefix = above error except .Prefix instead of .Id
I would have thought Course Object would be empty or null. Not that it would have some Proxy with all properties throwing exceptions.
Edit
I found this posting but I don't have this attribute anymore so maybe they got rid of it or moved it.
Anyone know?
Is it possible to avoid NHibernate.ObjectNotFoundException when there is a foreign key but the referenced row does not exist?
I think the error here is because you're accessing the Id property of an object which does not exist.
Do you get the same error if you do TableA.TableB ?
What about NotFound.Ignore()?
public class GradeMap : ClassMap<Grade>
{
public GradeMap()
{
Id(x => x.GradeId);
Map(x => x.TaskName).NvarcharWithMaxSize().Not.Nullable();
Map(x => x.Weight).Not.Nullable().Precision(5).Scale(2);
Map(x => x.OutOf).Not.Nullable().Precision(5).Scale(2);
Map(x => x.Mark).Not.Nullable().Precision(5).Scale(2);
Map(x => x.CourseBackgroundColor).Not.Nullable();
Map(x => x.CoursePrefix).Not.Nullable();
References(x => x.Student).Not.Nullable();
References(x => x.Course)
.NotFound.Ignore();
}
}
EDIT:
Let's say database schema looks like this:
Grade(GradeId, TaskName, Course_id)
Course(CourseId, Prefix)
If there is no foreign key constraint on Course_id column, then row in Course table with CourseId that corresponds to Course_id column in Grade table can be deleted. E.g.:
Course table:
CourseId Prefix
1 Course1Prefix
Grade table:
GradeId TaskName Course_id
1 Grade1Task 1
without foreign key constraint you can issue this dml query:
delete from Course where CourseId = 1
And it could be reason of problem explained by sJhonny.
When "NotFound.Ignore()" is used in the mapping then NHibernate tries to load Courses that belong to all Grades that were loaded by the query:
session.Query<Grade>().ToList();
No proxies are generated and when course referenced by grade is not found in database then the Course property is simply null.
If you can modify the database schema it would be helpful to create PK constraint on Course_id column.

Fluent Mapping : using join-table and cascades

having a little trouble with a mapping for the following table setup currently:
Shop
[1] [1]
/ \
[n] [n]
Category-[m]---[n]-Article
The behaviour should be the following :
1 - when deleting a shop, all Articles and Categories Should be deleted
2 - when deleting a Category, related Articles should be unassigned but not deleted
3 - when deleting an Article, related Categories should be unassigned but not deleted
Here's the current mapping:
public class ShopMap: ClassMap<Shop>
{
public ShopMap()
{
this.Table("shop");
Id(x => x.Id).Column("id").GeneratedBy.Native();
Map(x => x.Name).Column("name");
HasMany(x => x.Categories).Cascade.AllDeleteOrphan;
HasMany(x => x.Articles).Cascade.AllDeleteOrphan;
}
}
public class CategoryMap: ClassMap<Category>
{
public CategoryMap()
{
this.Table("category");
Id(x => x.Id).Column("id").GeneratedBy.Native();
Map(x => x.Name).Column("name");
References(x => x.Shop);
HasManyToMany(x => x.Articles).Cascade.AllDeleteOrphan()
.Table("article_category")
.ChildKeyColumn("article_id")
.ParentKeyColumn("category_id")
.Inverse();
}
}
public class ArticleMap: ClassMap<Article>
{
public ArticleMap()
{
this.Table("article");
Id(x => x.Id).Column("id").GeneratedBy.Native();
Map(x => x.Name).Column("name");
References(x => x.Shop);
HasManyToMany(x => x.Categories).Cascade.All()
.Table("article_category")
.ParentKeyColumn("article_id")
.ChildKeyColumn("category_id");
}
}
When deleting a Category (Session.Delete()), NH tries to delete the related Articles as well. Changing the Cascade-Mode to SaveUpdate will fix this, but will leave the entries in the link table *article_category*. Summing up : Cascade.SaveUpdate is too lazy, Cascade.All is too eager.
I tried everything that came to my mind in the mappings, but couldn't find a correct way to map this (rather simple schema).
Any ideas on how to (fluently) map this are greatly appreciated!
Thanks in advance
Sebi
The entries are left in the link table because Category.Articles is defined as the inverse side of the relationship. You need to remove the Category from Article.Categories before deleting it in order for the link record to be removed.