NHibernate ISet of Value Objects error in production NULL values in DELETE Statement on fields that have value - nhibernate

I have a error in production that is built with 2 web servers against a common SQL-server database.
I have one root entity with a ISet of Value Objects.
The mapping looks like this.
mapping.HasMany(x => x.DayInfos)
.Access.CamelCaseField(Prefix.Underscore)
.Table("WeeklyMailDayInfo")
.Component(c =>
{
c.Map(x => x.DayOfWeek);
c.Map(x => x.ImageText);
c.Map(x => x.ImageUrl);
});
When the user changes a ImageUrl the code simply removes and adds a new DayInfo to the ISet.
The problem I see in production is from the logfiles generated by NHibernate.
The log indicates
DELETE FROM WeeklyMailDayInfo WHERE WeeklyMailFk = #p0 AND DayOfWeek = #p1 AND ImageText = #p2 AND ImageUrl = #p3;#p0 = 3003, #p1 = 'Tuesday', #p2 = NULL, #p3 = NULL
Note the 2 nulls even though there is a value in ImageUrl and ImageText already.
I'm unable to reproduce this in any unittest or in my development enironment.
DevWeb and unittests are executed against SQLite

Since you're mapping a list of components, NHibernate has no primary key to uniquely identify the row. It therefore has to perform the DELETE based on equality with all columns as you indicate.
As for the NULLs, have you tried running your test suite against a copy of production? Are you certain that you don't have any DayInfo objects in the collection with no ImageText and ImageUrl?

Related

How to Union two Queries in LINQ to Fluent NHibernate?

How to Union two Queries in LINQ to Fluent NHibernate?
They return the same type but the queries are over separate entities:
IQueryable<Event> eventQuery1 = session.Query<Event>().Where(e => e.EventType.Id == eventTypeId);
IQueryable<Event> eventQuery2 = session.Query<Nomination>().Select(n => n.Event).Distinct();
I tried the Union() and Concat() methods but they failed:
eventQuery1 = eventQuery1.Union(eventQuery2);
The UnionResultOperator result operator is not current supported
I don't want to load the objects from database then apply the concat, I'd like it to be done before the objects are returned from database so that I can apply some Fetches on the final list from the union.
Not sure if it works with NHibernate LINQ, but a work-around is:
IQueryable<Event> eventQuery =
session.Query<Event>()
.Where(e => e.EventType.Id == eventTypeId
|| session.Query<Nomination>()
.Select(n => n.Event.Id)
.Contains(e.Id))

nhibernate hilo Id generator generates weird Id number

I am using nhibernate 3.2 mapping by convention.
here's my code. in my hilo table, NextHi is 1. I delete all records in User table, then insert a new User there. Instead getting a really small number like 10 or 11, the first user I inserted has Id 32768. I dont know how i get this number. please let me know what should I check?
mapper.BeforeMapClass += (modelInspector, type, classCustomizer) =>
{
classCustomizer.Id(c => c.Column("Id"));
classCustomizer.Id(c => c.Generator(Generators.HighLow,
gmap => gmap.Params(
new{
max_low = 10,
table = "Hilo",
column = "NextHi",
where = string.Format("TableKey = '{0}'", type.Name.ToLower())
})));
};
The parameter should be named max_lo, not max_low. It has probably changed between initial (and only by now) description by Fabio Maulo and the production 3.2 release.

Truncate DateTime in NHibernate QueryOver SelectGroup

I have a fairly run-of-the-mill QueryOver query containing the following,
.SelectList(list => list
.SelectGroup(() => txn.GroupField)
.SelectGroup(() => txn.Date))
.List<object[]>()
This query works as expected however I now have a requirement to group by the truncated Date as some of the Date's for these objects may contain a time component. This seems like it should be a trivial change but I can't find a way that is supported by NHibernate.
The obvious solution would be the change below but is not supported.
.SelectGroup(() => txn.Date.Date))
Any ideas?
Thanks
Your QueryOver could look like this:
Status alias = null;
var query = QueryOver.Of(() => alias)
.Select(Projections.GroupProperty(
Projections.SqlFunction("date", NHibernateUtil.Date, Projections.Property(() => alias.Date))));
Output (pseudo sql):
SELECT
...
FROM [Status] this_
GROUP BY dateadd(dd, 0, datediff(dd, 0, this_.Date))
Hope this helps, cheers!
You might want to add a helper property to your map, using the Formula command, to be able to use the date (instead of datetime) in queries.
here's an example from my code; it uses a decimal value, but this works fine with any subquery:
model class has this property, to be mapped to a formula:
public virtual decimal Profit
{
get { return this.SellPrice - this.Cost; }
set { return; }
}
fluentNHibernate map:
//SellPrice and Cost are columns in the object's table
Map(v => v.Profit).Formula("(SellPrice - Cost)"); // this field is calculated, not read
be sure to put the formula between () brackets though.
If you'd make your formula a select query that trunks the datetime into a date, you could then group by that property in your query.

How to determine if an IQueryable expression needs additional processing

By additional processing, I mean besides the standard LINQ to SQL translation to Transact SQL (and possible work arounds)
I’ve two issues that I was hoping for some insight on and/or some appropriate links or Google terms to use to find more information on as I’m not finding anything. It boils down to the fact that I would like to find out when/how an IQueryable expression that is going to be executed determines that some of the expression result needs to be run ‘client side’, meaning that the LINQ expression cannot be translated directly to Transact SQL to return the entire result.
This is in regards to my post/code.
Situation 1
string.Format() does not translate to Transact SQL. I can accept that, but guess I was just looking for some advice on how to detect if an Expression will be able to fully translate to Transact SQL.
LINQ Expression:
from h in HistoryData
select new { NewData = string.Format( "New Data: {0}", h.hisData ) };
Provider Context SQL:
SELECT [t0].[hisData] AS [arg0]
FROM [HistoryData] AS [t0]
My Workaround: Currently, I’m using DataContext.GetCommand() method then looking at the Transact SQL and searching for [arg0]. Obviously not ideal, so I was looking for a more robust mechanism (possibly spotting something with an ExpressionTree visitor??)
Situation 2
Using the tertiary operator in some situations seems to return some Transact SQL that obviously has some post/client processing applied to it to get the proper values. In the examples below, I’m running within the context of LINQPad (thus the Dump() extension method is available). There seems to be two situations when post processing is needed when a tertiary operator is in play:
When a boolean variable along with a boolean field is used (var testBool… expression below) and
When two variables, regardless of type, are used and no database fields are queried (the second testNumber expression below).
This situation surprised me that client side processing was needed. So in addition to learning how to properly detect when an expression needs additionally processing outside of the normal Transact SQL, if anyone has any insight as to why L2S couldn’t execute a simple CASE statement like the other situations and more importantly a possible workaround that could be used to avoid any ‘client side’ processing that would be great!
LINQ Expressions:
var before9_1_2009 = DateTime.Today < new DateTime( 2009, 9, 1 );
var fiveThousand = 5000;
var tenThousand = 10000;
var testNumber =
Profiles.Where( p => p.pAuthID == "111111111" )
.Select( p => new { Number = before9_1_2009 ? fiveThousand : p.pKey } );
var cmd = GetCommand( testNumber );
cmd.CommandText.Dump( "Number Property: Works (i.e. evaluates on SQL Server)" );
var testString =
Profiles.Where( p => p.pAuthID == "111111111" )
.Select( p => new { String = before9_1_2009 ? "StringConstant" : p.pAuthID } );
cmd = GetCommand( testString );
cmd.CommandText.Dump( "String Property: Works (i.e. evaluates on SQL Server)" );
var testBool =
Profiles.Where( p => p.pAuthID == "111111111" )
.Select( p => new { Boolean = boolExp ? true : p.pProcessed } );
cmd = GetCommand( testBool );
cmd.CommandText.Dump( "Boolean Property: Has NULL AS [EMPTY] - Post Processing Needed" );
testNumber =
Profiles.Where( p => p.pAuthID == "111111111" )
.Select( p => new { Number = before9_1_2009 ? fiveThousand : tenThousand } );
cmd = GetCommand( testNumber );
cmd.CommandText.Dump( "Number Property (using two constants): Has NULL AS [EMPTY] - Post Processing Needed" );
Provider Context SQL (in order):
▪ Number Property: Works (i.e. evaluates on SQL Server)
SELECT
(CASE
WHEN #p1 = 1 THEN #p2
ELSE [t0].[pKey]
END) AS [Number]
FROM [Profile] AS [t0]
WHERE [t0].[pAuthID] = #p0
▪ String Property: Works (i.e. evaluates on SQL Server)
SELECT
(CASE
WHEN #p1 = 1 THEN CONVERT(NVarChar(255),#p2)
ELSE [t0].[pAuthID]
END) AS [String]
FROM [Profile] AS [t0]
WHERE [t0].[pAuthID] = #p0
▪ Boolean Property: Has NULL AS [EMPTY]
SELECT NULL AS [EMPTY]
FROM [Profile] AS [t0]
WHERE [t0].[pAuthID] = #p0
▪ Number Property (using two constants): Has NULL AS [EMPTY]
SELECT NULL AS [EMPTY]
FROM [Profile] AS [t0]
WHERE [t0].[pAuthID] = #p0
My Workaround: Again, I’m using DataContext.GetCommand() method then looking at the Transact SQL and searching for NULL AS [EMPTY]. Wondering if there are any other ‘magic strings’ that are going to appear for expression code that cannot run on the server…so as above, I’m looking for a correct, more robust way of detecting these situations.
Quick observations:
Where did you declare boolExp variable?
Number Property (using two constants): 2 var objects return ambiguous type.
(for first Number case p.pKey defined type)

Determine field by formula on Insert in NHibernate

I am using NHibernate 2.1. I am inserting records into a table successfully, but I have a SortOrder column that needs to be calculated on insert. The formula will roughly be:
SortOrder = (SELECT (MAX(SortOrder) + 1) FROM MyTable WHERE CategoryID = #CategoryID)
How can I accomplish this in NHibernate on inserts? Normally I use Stored Procedures and I would just include this there.
Thanks for any help!
I am not aware of a way of doing what you ask through the mapping files. I do not think there is one.
How I would approach the problem is to plug in a PreInsertListener and do the select statement you provide in your question there to retrieve the value for the SortOrder answer just before the entity is saved.
Here is how it would roughly look:
public class NHibernateEventListeners : IPreInsertEventListener
{
public bool OnPreInsert(PreInsertEvent auditEvent)
{
var audit = auditEvent.Entity as YourEntityTypeHere;
if (audit == null)
return false;
int sortOrderValue =
(int)auditEvent.Session.CreateCriteria<YourEntityTypeHere>()
.SetProjection(Projections.Max("SortOrder"))
.Add(Restrictions.Eq("CategoryID", CatID)).UniqueResult();
SetValue(auditEvent.Persister, auditEvent.State, "SortOrder", sortOrderValue + 1);
audit.DateCreated = sortOrderValue + 1;
return false;
}
}
You can still use stored procs with nhibernate. You need to implement <sql-insert>, <sql-update> and <sql-delete> in the xml mapping file to do any specialized CRUD. I think that will be your best bet given your scenario.
In your class mapping file, you can specify a SQL formula for a column / property. Using XML mapping files:
<property name="SortOrder" formula=" (SELECT (MAX(SortOrder) + 1) FROM MyTable WHERE CategoryID = #CategoryID)"/>
Or if you are using fluent mapping:
Map(x => x.SortOrder).FormulaIs(" (SELECT (MAX(SortOrder) + 1) FROM MyTable WHERE CategoryID = #CategoryID)");
See if that works.
You could write a trigger in your database but be aware of the problems with this approach as NHibernate won't be aware of the changes in this calculated field. You have to use Flush() method after saving and Refresh(your_data_object) method after it.
There is a good detailed explanation about dealing with triggers in "NHibernate in Action" book: "9.2.4 Working with triggers".