FluentNHibernate - How to map KeyProperty with db generated value? - nhibernate

I have this mapping:
public sealed class AcessoMap : ClassMap<Acesso>
{
public AcessoMap()
{
CompositeId()
.KeyReference(x => x.Associado)
.KeyProperty(x => x.DataHora, k => k.ColumnName("aceDtHor").Type("Timestamp"));
Map(x => x.IP, "aceEndIP");
Map(x => x.NumeroAcesso).Not.Nullable().Generated.Insert();
Map(x => x.DataAcessoAnterior).Not.Nullable().Generated.Insert();
Map(x => x.ServerVariables).LazyLoad().Generated.Insert();
}
}
How can I configure it to DataHora property use database generated value? (Current it have default value on db, that sets it to current timestamp)
Thank you

I don't know if this will work, but it's something to try. Map your timestamp column regularly, outside the CompositeId definition.
Map(x=>x.RecordVersion).Column("aceDtHor")
.CustomSqlType( "timestamp" )
.Not.Nullable()
.CustomType( "BinaryBlob" )
.Generated.Always();

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;
}

Why NHibernate UPDATE reference entity?

I have a test mothod like below:
public void Add_Update_Delete_a_Registration() {
ISessionFactory sessionFactory = SessionFactory.GetSessionFactory(connString);
using (ISession session = sessionFactory.OpenSession()) {
Course course = new CourseRepository(session).GetById(12019);
Registration entity = new Registration();
entity.Course = course; //Assign the Course to register
//assign other entity members
//...
RegistrationRepository repository = new RegistrationRepository(session);
repository.Add(entity);
}
The Registration entity was inserted correctly.
The problem is, NHibernate also made an UPDATE database call to update the Course entity which is not changed at all in the test method. What could be the possible reasons?
The mappings:
public class CourseMap : ClassMap<Course>{
public CourseMap() {
Id(x => x.Id).GeneratedBy.HiLo("100");
Map(x => x.WeekDay)
.Not.Nullable()
.CustomType<int>(); //WeekDay is type of DayOfWeek enums
References(x => x.Room)
.Not.Nullable();
Map(x => x.StartTime)
.Not.Nullable();
Map(x => x.EndTime)
.Not.Nullable();
Map(x => x.CreatedTime)
.Not.Nullable();
Map(x => x.UpdatedTime);
Map(x => x.CreatedBy)
.Not.Nullable();
Map(x => x.UpdatedBy);
Version(x => x.Version).Column("RCB_Version")
.CustomSqlType("timestamp")
.Generated.Always()
.Not.Nullable();
}
public class RegistrationMap : ClassMap<Registration>{
public RegistrationMap() {
Id(x => x.Id)
.GeneratedBy.HiLo("100");
Map(x => x.OwnerWindowsAccount)
.Not.Nullable()
.Length(50);
References(x => x.Course)
.Not.Nullable();
Map(x => x.TrainingDate)
.Not.Nullable();
Map(x => x.RegistreeName)
.Not.Nullable()
.Length(50);
Map(x => x.RegistreeWindowsAccount)
.Nullable()
.Length(50);
Map(x => x.CreatedTime)
.Not.Nullable();
Map(x => x.UpdatedTime);
Map(x => x.CreatedBy)
.Not.Nullable();
Map(x => x.UpdatedBy);
Version(x => x.Version)
.CustomSqlType("timestamp")
.Generated.Always()
.Not.Nullable();
}
}
Much appreciated!
Leo
You have version column specified. Which means that any property change (even collection) trigger version update.
In order to prevent a certain property/collection to change version, optimistic-lock="false" property should be set in xml mapping.
Though not sure how it'd be in fluent syntax. Probably .OptimisticLock.False() or something.
There are some various issues that can cause this. I often get it when I have mapped my enums wrong or is using the inverse="true|false" the wrong way.

fluent nhibernate mapping

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")
}

Fluent nhibernate table-per-hierarchy mapping

I have a simple scenario where I have an entity Action (this is a workflow style application) that has a DueDate calculated property.
Now id like to introduce a SlidingAction, whose only difference (at this stage) is to override the DueDate calculation, as such has none of its own mapping.
Im having difficulty mapping this scenario since Fluent Nhibernate seems to be forcing me to map 'something' on the subclass.
Could someone shed some light?
Cheers,
Byron
public class ActionMap : ClassMap<Action>
{
public ActionMap()
{
WithTable("Actions");
Id(x => x.ID);
Map(x => x.Description);
Map(x => x.TimeLine);
Map(x => x.Template);
Map(x => x.StageOrder);
Map(x => x.CorrespondenceType).CustomTypeIs(typeof (ActionCorrespondenceTypeEnumType));
References(x => x.Matter).FetchType.Join();
HasMany(x => x.FileNotes).Cascade.SaveUpdate();
DiscriminateSubClassesOnColumn("Type")
.SubClass<SlidingAction>(/*its forcing me to map something here*/);
}
}
Just put an empty lambda in, c => {}.
.SubClass<SlidingAction>(c => {});