NHibernate taking longer time executing simple query - nhibernate

I am facing very strange issue re NHibernate and now it is become headache.
NHibernate is taking longer time (2-3 minute) than expected time (few milliseconds) to execute such a simple query. The database is Oracle and I am using ODP driver. I have checked all necessary configuration re NHibernate and Spring which looks ok to me. When I execute the same query in sqldeveloper, it is giving result in milliseconds.
FYI - When I execute another query which has three inner join with complex model with the same NHibernate configuration, I am getting the result as expected.
In the debug log, I could see below lines where it is wasting time:
2012-08-17 09:53:20,754 [TestRunnerThread] DEBUG - NHibernate.Connection.DriverConnectionProvider - Obtaining IDbConnection from Driver
2012-08-17 09:55:09,369 [TestRunnerThread] DEBUG - NHibernate.AdoNet.AbstractBatcher - ExecuteReader took 108720 ms
NHibernate property settings:
<nhibernatePropertiesSettings>
<setting name="nhibernate.connection.provider" serializeAs="String">
<value>NHibernate.Connection.DriverConnectionProvider</value>
</setting>
<setting name="nhibernate.connection.driver.class" serializeAs="String">
<value>NHibernate.Driver.OracleDataClientDriver</value>
</setting>
<setting name="nhibernate.dialect" serializeAs="String">
<value>NHibernate.Dialect.Oracle10gDialect</value>
</setting>
<setting name="nhibernate.show.sql" serializeAs="String">
<value>true</value>
</setting>
<setting name="nhibernate.query.substitutions" serializeAs="String">
<value>true 1, false 0, yes 'Y', no 'N'</value>
</setting>
<setting name="nhibernate.use.proxy.validator" serializeAs="String">
<value>false</value>
</setting>
<setting name="nhibernate.template.flush.mode" serializeAs="String">
<value>Never</value>
</setting>
</nhibernatePropertiesSettings>
Spring property setting:
<springPropertiesSettings>
<setting name="spring.db.provider" serializeAs="String">
<value>OracleODP-11-2.0</value>
</setting>
</springPropertiesSettings>
Mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="XYZ.PaymentInvestigation.Service.Model.PaymentMessageHistory, PaymentInvestigation.Service.Model" table="MESSAGE_HIST_T" lazy="false">
<composite-id name="PaymentMessageHistoryId" class="XYZ.PaymentInvestigation.Service.Model.PaymentMessageHistoryId, PaymentInvestigation.Service.Model" unsaved-value="undefined">
<key-property name="TransactionDate" column="TRN_DATE" />
<key-property name="TransactionReferenceNumber" column="TRN_NUMBER" />
<key-property name="TransactionTimeStamp" column="TRN_TIMESTAMP" />
<key-property name="HistoryNumber" type="AnsiString" column="HIST_NO" />
<key-property name="SubHistoryNumber" type="AnsiString" column="SUB_HIST_NO" />
</composite-id>
<property name="EntryType" column="ENTRY_TYPE" update="false" insert="false" />
<property name="Location" column="LOC" update="false" insert="false" />
<property name="QueLineId" column="QUE_LINE_ID" update="false" insert="false" />
<property name="DateTime" column="DATE_TIME" update="false" insert="false" />
<property name="SequenceNo" column="SEQUENCE_NO" update="false" insert="false" />
<property name="OperatorInitials" column="OPR_INITIALS" update="false" insert="false" />
<property name="Amount" column="AMOUNT" update="false" insert="false" />
<property name="MsgInfo" column="MSG_INFO" update="false" insert="false" />
<property name="RecordExpired" column="RECORD_EXPIRED" update="false" insert="false" />
<property name="RecordUpdated" column="RECORD_UPDATED" update="false" insert="false" />
<property name="Details" column="DETAILS" update="false" insert="false" />
</class>
Query:
IList<PaymentMessageHistory> paymentMessageHistories =
HibernateTemplate.ExecuteFind(session => session
.QueryOver<PaymentMessageHistory>()
.Where(x =>
x.PaymentMessageHistoryId.TransactionDate == paymentMessageId.TransactionDate &&
x.PaymentMessageHistoryId.TransactionReferenceNumber == paymentMessageId.TransactionReferenceNumber)
.List());
PaymentMessageHistoryId Model:
public class PaymentMessageHistoryId : IEquatable<PaymentMessageHistoryId>
{
public virtual DateTime TransactionDate { get; set; }
public virtual int TransactionReferenceNumber { get; set; }
public virtual double TransactionTimeStamp { get; set; }
public virtual string HistoryNumber { get; set; }
public virtual string SubHistoryNumber { get; set; }
public bool Equals(PaymentMessageHistoryId other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.TransactionDate.Equals(TransactionDate) && other.TransactionReferenceNumber == TransactionReferenceNumber && other.TransactionTimeStamp.Equals(TransactionTimeStamp) && Equals(other.HistoryNumber, HistoryNumber) && Equals(other.SubHistoryNumber, SubHistoryNumber);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (PaymentMessageHistoryId)) return false;
return Equals((PaymentMessageHistoryId) obj);
}
public override int GetHashCode()
{
unchecked
{
int result = TransactionDate.GetHashCode();
result = (result*397) ^ TransactionReferenceNumber;
result = (result*397) ^ TransactionTimeStamp.GetHashCode();
result = (result*397) ^ (HistoryNumber != null ? HistoryNumber.GetHashCode() : 0);
result = (result*397) ^ (SubHistoryNumber != null ? SubHistoryNumber.GetHashCode() : 0);
return result;
}
}
public static bool operator ==(PaymentMessageHistoryId left, PaymentMessageHistoryId right)
{
return Equals(left, right);
}
public static bool operator !=(PaymentMessageHistoryId left, PaymentMessageHistoryId right)
{
return !Equals(left, right);
}
public override string ToString()
{
return string.Format("TransactionDate: {0}, TransactionReferenceNumber: {1}, TransactionTimeStamp: {2}, HistoryNumber: {3}, SubHistoryNumber: {4}", TransactionDate, TransactionReferenceNumber, TransactionTimeStamp, HistoryNumber, SubHistoryNumber);
}
PaymentMessageHistory Model:
public class PaymentMessageHistory : IEquatable<PaymentMessageHistory>
{
public virtual PaymentMessageHistoryId PaymentMessageHistoryId { get; set; }
public virtual string EntryType { get; set; }
public virtual string Location { get; set; }
public virtual string QueLineId { get; set; }
public virtual string DateTime { get; set; }
public virtual string SequenceNo { get; set; }
public virtual string OperatorInitials { get; set; }
public virtual decimal Amount { get; set; }
public virtual string MsgInfo { get; set; }
public virtual double RecordExpired { get; set; }
public virtual string RecordUpdated { get; set; }
public virtual string Details { get; set; }
public bool Equals(PaymentMessageHistory other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.PaymentMessageHistoryId, PaymentMessageHistoryId) && Equals(other.EntryType, EntryType) && Equals(other.Location, Location) && Equals(other.QueLineId, QueLineId) && Equals(other.DateTime, DateTime) && Equals(other.SequenceNo, SequenceNo) && Equals(other.OperatorInitials, OperatorInitials) && other.Amount == Amount && Equals(other.MsgInfo, MsgInfo) && other.RecordExpired.Equals(RecordExpired) && Equals(other.RecordUpdated, RecordUpdated) && Equals(other.Details, Details);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (PaymentMessageHistory)) return false;
return Equals((PaymentMessageHistory) obj);
}
public override int GetHashCode()
{
unchecked
{
int result = (PaymentMessageHistoryId != null ? PaymentMessageHistoryId.GetHashCode() : 0);
result = (result*397) ^ (EntryType != null ? EntryType.GetHashCode() : 0);
result = (result*397) ^ (Location != null ? Location.GetHashCode() : 0);
result = (result*397) ^ (QueLineId != null ? QueLineId.GetHashCode() : 0);
result = (result*397) ^ (DateTime != null ? DateTime.GetHashCode() : 0);
result = (result*397) ^ (SequenceNo != null ? SequenceNo.GetHashCode() : 0);
result = (result*397) ^ (OperatorInitials != null ? OperatorInitials.GetHashCode() : 0);
result = (result*397) ^ Amount.GetHashCode();
result = (result*397) ^ (MsgInfo != null ? MsgInfo.GetHashCode() : 0);
result = (result*397) ^ RecordExpired.GetHashCode();
result = (result*397) ^ (RecordUpdated != null ? RecordUpdated.GetHashCode() : 0);
result = (result*397) ^ (Details != null ? Details.GetHashCode() : 0);
return result;
}
}
public static bool operator ==(PaymentMessageHistory left, PaymentMessageHistory right)
{
return Equals(left, right);
}
public static bool operator !=(PaymentMessageHistory left, PaymentMessageHistory right)
{
return !Equals(left, right);
}
public override string ToString()
{
return string.Format("PaymentMessageHistoryId: {0}, EntryType: {1}, Location: {2}, QueLineId: {3}, DateTime: {4}, SequenceNo: {5}, OperatorInitials: {6}, Amount: {7}, MsgInfo: {8}, RecordExpired: {9}, RecordUpdated: {10}, Details: {11}", PaymentMessageHistoryId, EntryType, Location, QueLineId, DateTime, SequenceNo, OperatorInitials, Amount, MsgInfo, RecordExpired, RecordUpdated, Details);
}
I went through with this NHibernate taking a long time to run query but didn't work for me.
Please help!
Regards,
Milind

public class OracleDataClientDriver2 : OracleDataClientDriver
{
protected override void InitializeParameter(IDbDataParameter dbParam, string name, SqlType sqlType)
{
switch (sqlType.DbType)
{
//Timestamp columns not indexed by Oracle 11g date columns. - Use Date
case DbType.DateTime:
base.InitializeParameter(dbParam, name, SqlTypeFactory.Date);
break;
default:
base.InitializeParameter(dbParam, name, sqlType);
break;
}
}
}
and change the value of connection.driver_class = nameSpace.OracleDataClientDriver2, AssemblyName

One more simple solution than inheriting oracle driver class is to just add type attribute in your property and value will be "Date". this is NHibernate type.
<key-property name="TransactionDate" type="Date" column="TRN_DATE" />

Related

NHibernate multi column ManyToOne mapping with Mapping By-Code

I am trying to convert my FluentNHibernate mappings to NHibernate Mapping By-code using NHibernate 3.3.3. The goal is to upgrade to NHibernate 3.3.3 and to cut down on the number of assemblies being distributed.
However when I compile and run I get the following exception:
NHibernate.MappingException: Multi-columns property can't be mapped through single-column API.
The XML mapping FluentNHibernate gets my looks like this:
<many-to-one cascade="none" class="TextDto" fetch="join" lazy="false" name="Name" not-found="ignore">
<column name="NameTextId" unique="false" />
<column name="LanguageId" unique="false" />
</many-to-one>
Here is my new By-Code mapping:
this.ManyToOne(u => u.Name, c =>
{
c.Cascade(Cascade.None);
c.Class(typeof(TextDto));
c.Columns(
x =>
{
x.Name("NameTextId");
x.Unique(false);
},
x =>
{
x.Name("LanguageId");
x.Unique(false);
});
c.Fetch(FetchKind.Join);
c.Lazy(LazyRelation.NoLazy);
c.NotFound(NotFoundMode.Ignore);
c.Unique(false);
});
This is the old FluentNHibernate mapping:
References(x => x.Name)
.Columns("NameTextId", "LanguageId")
.Cascade.None()
.Fetch.Join()
.NotFound.Ignore()
.Not.Unique()
.Not.LazyLoad();
For Completeness the property type involved:
public class TextDto
{
public TextCompositeId Id { get; set; }
public string PluralText { get; set; }
public string SingularText { get; set; }
public override bool Equals(object obj)
{
var text = (TextDto)obj;
if (text == null) return false;
return this.Id.Equals(text.Id);
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}
And an example of the property in an entity:
public class CharacteristicValue
{
public CharacteristicValueCompositeId Id { get; set; }
public TextDto Name { get; set; }
public string LanguageIdentity { get; set; }
public string Value
{
get
{
string value = null;
if (this.ValueMultilingual != null) return this.ValueMultilingual.SingularText;
else if (!string.IsNullOrEmpty(this.ValueMeta)) return this.ValueMeta;
return value;
}
}
public TextDto ValueMultilingual { get; set; }
public string ValueMeta { get; set; }
public override bool Equals(object obj)
{
if (obj == null) return false;
if (object.ReferenceEquals(this, obj)) return true;
CharacteristicValue characteristicValue = obj as CharacteristicValue;
if (characteristicValue == null) return false;
if (this.Id != characteristicValue.Id) return false;
return true;
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}
So, how do I get the xml-mapping I used to get with FluentNHibernate but with NHiberbate's Mapping By-Code?
In your mapping, remove the c.Unique(false); from the ManyToOne mapping. This setting we do apply for each column now.
this.ManyToOne(u => u.Name, c =>
{
... // the same as above
// c.Unique(false); // it is setting now related to columns
});
And you will recieve
<many-to-one name="Name" class="TextDto" fetch="join" lazy="false" not-found="ignore">
<column name="NameTextId" unique="true" />
<column name="LanguageId" />
</many-to-one>
If you will change uniqueness on one of the columns:
x =>
{
x.Name("NameTextId");
x.Unique(true); // change here
},
The unique constraint would be added to that column:
<column name="NameTextId" unique="true" />

NHibernate: Composite key, one-to-one relation and "Error performing LoadByUniqueKey"

I have two classes. Class 1:
public class Einsatz
{
public virtual ProjNrPindex Id { get; set; }
public virtual Int32? Knr { get; set; }
public virtual String RessNr { get; set; }
public virtual String AdrNr { get; set; }
public virtual DateTime EndeIst { get; set; }
public virtual DateTime StartIst { get; set; }
public virtual DateTime ArbeitsbeginnIst { get; set; }
public virtual String Kennwort { get; set; }
public virtual Taetigkeit Taetigkeit { get; set; }
public Einsatz()
{
this.Id = new ProjNrPindex();
}
public class ProjNrPindex
{
public virtual Int32? Pindex { get; set; }
public virtual String ProjNr { get; set; }
public override Boolean Equals(Object obj)
{
if (obj as Einsatz == null)
{
return(false);
}
if (Object.ReferenceEquals(this, obj) == true)
{
return(true);
}
ProjNrPindex other = obj as ProjNrPindex;
if (Object.Equals(this.Pindex, other.Pindex) == false)
{
return(false);
}
if (Object.Equals(this.ProjNr, other.ProjNr) == false)
{
return(false);
}
return(true);
}
public override Int32 GetHashCode()
{
Int32 hash = 0;
hash += (this.Pindex != null) ? this.Pindex.GetHashCode() : 0;
hash += 1000 * ((this.ProjNr != null) ? this.ProjNr.GetHashCode() : 0);
return(hash);
}
}
public override bool Equals(object obj)
{
var other = obj as Einsatz;
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return this.Id.Equals(other.Id);
}
public override int GetHashCode()
{
unchecked
{
int hash = GetType().GetHashCode();
hash = (hash * 31) ^ this.Id.GetHashCode();
return hash;
}
}
}
Class 2:
public class Taetigkeit
{
public virtual ProjNrPindex Id { get; set; }
public virtual string Text { get; set; }
public virtual string RessNr { get; set; }
public virtual Einsatz Einsatz { get; set; }
public Taetigkeit()
{
this.Id = new ProjNrPindex();
}
public class ProjNrPindex
{
public virtual Int32? Pindex { get; set; }
public virtual String ProjNr { get; set; }
public override Boolean Equals(Object obj)
{
if (obj as Einsatz == null)
{
return (false);
}
if (Object.ReferenceEquals(this, obj) == true)
{
return (true);
}
ProjNrPindex other = obj as ProjNrPindex;
if (Object.Equals(this.Pindex, other.Pindex) == false)
{
return (false);
}
if (Object.Equals(this.ProjNr, other.ProjNr) == false)
{
return (false);
}
return (true);
}
public override Int32 GetHashCode()
{
Int32 hash = 0;
hash += (this.Pindex != null) ? this.Pindex.GetHashCode() : 0;
hash += 1000 * ((this.ProjNr != null) ? this.ProjNr.GetHashCode() : 0);
return (hash);
}
}
public override bool Equals(object obj)
{
var other = obj as Einsatz;
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return this.Id.Equals(other.Id);
}
public override int GetHashCode()
{
unchecked
{
int hash = GetType().GetHashCode();
hash = (hash * 31) ^ this.Id.GetHashCode();
return hash;
}
}
}
Mappings are:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="RestService"
namespace="RestService">
<class name="Einsatz" table="PLANUNG">
<composite-id name="Id">
<key-property name="ProjNr">
<column name="ProjNr" />
</key-property>
<key-property name="Pindex">
<column name="Pindex" />
</key-property>
</composite-id>
<property name="Knr" column="Knr" />
<property name="AdrNr" column="AdrNr" />
<property name="RessNr" column="RessNr" />
<property name="EndeIst" column="EndeIst" />
<property name="StartIst" column="StartIst" />
<property name="ArbeitsbeginnIst" column="ArbeitsbeginnIst" />
<property name="Kennwort" column="Kennwort" />
<one-to-one name="Taetigkeit" class="Taetigkeit" property-ref="Id"/>
</class>
</hibernate-mapping>
and
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="RestService"
namespace="RestService">
<class name="Taetigkeit" table="PLANBER">
<composite-id name="Id">
<key-property name="ProjNr">
<column name="ProjNr" />
</key-property>
<key-property name="Pindex">
<column name="Pindex" />
</key-property>
</composite-id>
<property name="RessNr" column="RessNr" />
<property name="Text" column="Text" />
<one-to-one name="Einsatz" class="Einsatz" property-ref="Id"/>
</class>
</hibernate-mapping>
The code:
var q = s.CreateQuery("from Einsatz e").List<Einsatz>();
gives me:
Error performing LoadByUniqueKey[SQL: SQL not available]
"The given key was not present in the dictionary."
I am afraid I am doing something horribly wrong but I don't know what. I might add that the database in sql server has no foreign keys so the data is not quite coherent.
your equals method is flawed. it will return false when the other object it gets is not Einsatz which should be ProjNrPindex change it to the much easier implementation:
public override bool Equals(object obj)
{
var other = obj as ProjNrPindex;
return other != null &&
Pindex == other.Pindex &&
ProjNr == other.ProjNr;
}
Also Gethashcode will throw Overflowexception in certain situations use unchecked
public override Int32 GetHashCode()
{
unchecked
{
return Pindex.GetOrDefault().GetHashCode() +
1000 * ((this.ProjNr != null) ? this.ProjNr.GetHashCode() : 0);
}
}

NHibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session

I have an Class with property
[Serializable]
public class MyClass {
public MyClass ()
{
}
public virtual System.DateTime Time {
get;
set;
}
public virtual string Name {
get;
set;
}
public virtual string Department {
get;
set;
}
public virtual string Ip
{
get;
set;
}
public virtual string Address {
get;
set;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
MyClass t = obj as MyClass ;
if (t == null)
return false;
if (this.Time == t.Time && this.Name== t.Name && this.Department== t.Department)
return true;
else
return false;
}
public override int GetHashCode()
{
int hash = 13;
hash = hash +
(null == this.Time ? 0 : this.Time.GetHashCode());
hash = hash +
(null == this.Name? 0 : this.Name.GetHashCode());
hash = hash +
(null == this.Department ? 0 : this.Department.GetHashCode());
return hash;
}
}
I am having my Nhibernate mapping as
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NhibernateTest" assembly="NhibernateTest">
<class name="MyClass" table="NhibernateTest">
<composite-id>
<key-property column="Time" type="DateTime" name="Time"></key-property>
<key-property name="Name" type="string" column="Name" ></key-property>
<key-property name="Department" type="string" column="Department" ></key-property>
</composite-id>
<property column="Ip" type="string" name="Ip" />
<property column="Address" type="string" name="Address" />
</class>
</hibernate-mapping>
I am trying to perform a bulk upload for some 40k data's with using composite key. using the following code.
public void StoreInRDBMS(List<MyClass> FileList)
{
ITransaction transaction = null;
try
{
var stopwatch = new Stopwatch();
stopwatch.Start();
ISession session = OpenSession();
using (transaction = session.BeginTransaction())
{
foreach (var File in FileList)
{
session.SaveOrUpdate(File);
}
session.Flush();
session.Clear();
transaction.Commit();
}
session.Close();
stopwatch.Stop();
var time = stopwatch.Elapsed;
}
catch (Exception ex)
{
transaction.Rollback();
}
}
but the problem is , while iterating in the loop the second record in the list throws this error
{"a different object with the same identifier value was already associated with the session: NhibernateTest.MyClass, of entity: NhibernateTest.MyClass"}
though the records are unique. and also if at all its not it should update the same.
It works file if I flush the session after every iteration in the loop like
foreach (var File in FileList)
{
session.SaveOrUpdate(File);
session.Flush();
session.Clear();
}
which should not be the case and even its talkin 17 mins for 40 k records if done by above method.
Can anybody help regarding the same.
Maybe?
foreach (var File in FileList)
{
session.SaveOrUpdate(File);
session.ExecuteUpdate();
}

NHibernate.Linq System.Nullable throws ArgumentException, the value "" is not type

I have a class of type MetadataRecord:
public class MetadataRecord {
public virtual long? IntegerObject { get; set; }
public virtual string ClassName { get; set; }
public virtual DateTime? DateObject { get; set; }
public virtual double? DecimalObject { get; set; }
public virtual long MetadataId { get; set; }
public virtual long MetadataLabelId { get; set; }
public virtual long ObjectId { get; set; }
public virtual string StringObject { get; set; }
public virtual Asset Asset { get; set; }
}
and a matching mapping file as follows:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ActiveMediaDataAccess"
namespace="ActiveMediaDataAccess.Entities">
<class name="MetadataRecord" table="WM_META_DATA" lazy="true">
<id name="MetadataId" column="META_DATA_ID">
<generator class="seqhilo" />
</id>
<property name="MetadataLabelId" column="META_DATA_LABEL_ID" />
<property name="ObjectId" column="OBJECT_ID" />
<property name="ClassName" column="CLASS_NAME" />
<property name="IntegerObject" column="INTEGER_OBJECT" />
<property name="DecimalObject" column="DECIMAL_OBJECT" />
<property name="DateObject" column="DATE_OBJECT" />
<property name="StringObject" column="STRING_OBJECT" />
<many-to-one name="Asset" column="OBJECT_ID" not-null="true" />
</class>
</hibernate-mapping>
I'm running a unit test against this class to check for values returned for IntegerObject which is a nullable type of long, from an instance of MetadataRecord. I'm using NHibernate.Linq (v 1.1.0.1001) to query as follows:
[TestMethod()]
public void IntegerObjectTest() {
var integerObject = _sessionFactory.OpenSession().Linq<MetadataRecord>()
.Where(m => m.ObjectId == 65675L)
.Select(m => m.IntegerObject)
.FirstOrDefault();
Assert.IsNull(integerObject);
}
The INTEGER_OBJECT column from the corresponding table is nullable, and I expect IsNull to be true or false. However, I get the following error:
Test method ActiveMediaMetadataViewerTestProject.MetadataRecordTest.IntegerObjectTest threw exception: NHibernate.Exceptions.GenericADOException: Unable to perform find[SQL: SQL not available] ---> System.ArgumentException: The value "" is not of type "System.Nullable`1[System.Int64]" and cannot be used in this generic collection.
Parameter name: value.
I can't figure out why it's trying to cast a string to a nullable type. Is there another way in which I should be opening the session, decorating the class, even constructing the mapping file, ..... where am I going wrong here? I could resort to using Criteria, but I was much enjoying the intellisense and "refactorability" with Linq.
Better solution (translated to SQL in whole):
[TestMethod()]
public void IntegerObjectTest() {
var integerObject = _sessionFactory.OpenSession().Linq<MetadataRecord>()
.Where(m => m.ObjectId == 65675L)
.Select(m => new long?(m.IntegerObject))
.FirstOrDefault();
Assert.IsNull(integerObject);
}
My workaround:
[TestMethod()]
public void IntegerObjectTest() {
var integerObject = _sessionFactory.OpenSession().Linq<MetadataRecord>()
.Where(m => m.ObjectId == 65675L)
.Select(m => m.IntegerObject)
.AsEnumerable()
.FirstOrDefault();
Assert.IsNull(integerObject);
}
For some reason, NHibernate.Linq does not like calling First(), FirstOrDefault() (and I'm guessing Single() and SingleOrDefault()) on nullable types, and throws the above error if the field is null. It works fine if the nullable type actually has a value. If I push the results into an in-memory collection via AsEnumerable(), ToArray(), ToList(), etc, then it plays nice and returns my nullable type.

Querying Overriding Entities Using a Self Join and the NHibernate Criteria API

I have a simple Waiver model, and I would like to make a query that returns all the Waivers that are not overridden.
public class Waiver
{
private readonly int id;
protected Waiver()
{
this.id = 0;
}
public virtual int Id { get { return id; } }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual bool IsRequired { get; set; }
public virtual DateTime EffectiveDate { get; set; }
public virtual Waiver OverriddenWaiver { get; set; }
}
Here's the map:
<class name="Waiver" table="Music_Waivers">
<id name="id" access="field" column="WaiverId" unsaved-value="0">
<generator class="native" />
</id>
<property name="Name" column="Name" />
<property name="Description" column="Description" />
<property name="IsRequired" column="IsRequired" />
<property name="EffectiveDate" column="EffectiveDate" />
<many-to-one name="OverriddenWaiver" class="Waiver" column="OverrideWaiverId" />
</class>
Now I want to have a method in my Repository with the signature public IList GetLatest(). For some reason I'm having a hard time implementing this with the CriteriaAPI. I can write this in T-SQL no problem.
I ended up brute forcing a solution. It's not pretty, but since I know the table will be tiny (probably going to end up being only 5 rows) I came up with the following code solution:
public IList<Waiver> GetLatest()
{
using (var session = SessionManager.OpenSession())
{
var criteria = session.CreateCriteria(typeof (Waiver));
var waivers = criteria.List<Waiver>();
var nonOverridenWaivers = new List<Waiver>();
foreach(var waiver in waivers)
{
bool overrideExists = waivers.Any(w => w.Overrides != null &&
w.Overrides.Id == waiver.Id);
if (!overrideExists)
nonOverridenWaivers.Add(waiver);
}
return nonOverridenWaivers;
}
}