(отредактировано, чтобы не вести по неправильной дороге)
Прежде чем дать подробности, очень короткая версия: у меня есть оператор SQL, который мне нужен, чтобы выйти из NHibernate, и у меня есть операторы Criteria API, которые мне его дают. NHibernate пытается сделать что-то не так с оператором, который он генерирует, и я пытаюсь выяснить, почему.
У меня есть следующие сопоставления в Fluent NHibernate:
public class StaffClass {
public virtual int Staff_DBID { get; private set; }
public virtual int Class_DBID { get; private set; }
// Equals and GetHasCode overrides not shown
// other code originally here removed as it was distracting from the point and didn't change anything
}
public class StaffClassMap : ClassMap<StaffClass> {
public StaffClassMap() {
CompositeId()
.KeyProperty(x => x.Staff_DBID)
.KeyProperty(x => x.Class_DBID);
}
}
Аналогичный набор кода присутствует для таблицы с именем ClassStudent. Обе эти таблицы являются таблицами соединения.
У меня есть следующий код, который работает с объектами домена:
using (transaction = session.BeginTransaction()) {
var criteria = session.CreateCriteria(typeof(Student));
var staffClasses = DetachedCriteria.For<StaffClass>()
.Add(Restrictions.Eq("Staff_DBID", desiredStaffDBID))
.SetProjection(Projections.Property("Class_DBID"));
var studentClasses = DetachedCriteria.For<ClassStudent>()
.Add(Subqueries.In("Class_DBID", staffClasses))
.SetProjection(Projections.Property("Student_DBID"));
criteria.Add(Subqueries.In("Student_DBID", studentClasses));
var students = criteria.List<Student>();
foreach (var student in students) {
Console.WriteLine(string.Format("Student: {0}, {1}", student.LastName, student.FirstName));
}
}
Когда я пытаюсь запустить этот код, я получаю следующее исключение:
NHibernate.ADOException occurred
Message=could not execute query
[ SELECT this_.Student_DBID as Student1_7_0_, this_.DistrictStudentID as District2_7_0_, this_.LastName as LastName7_0_, this_.FirstName as FirstName7_0_, this_.MidName as MidName7_0_, this_.School_DBID as School6_7_0_ FROM [Student] this_ WHERE @p0 in (SELECT this_0_.Student_DBID as y0_ FROM [ClassStudent] this_0_ WHERE @p1 in (SELECT this_0_0_.Class_DBID as y0_ FROM [StaffClass] this_0_0_ WHERE this_0_0_.Staff_DBID = @p2)) ]
Positional parameters: #0>Student_DBID #1>Class_DBID #2>3664
[SQL: SELECT this_.Student_DBID as Student1_7_0_, this_.DistrictStudentID as District2_7_0_, this_.LastName as LastName7_0_, this_.FirstName as FirstName7_0_, this_.MidName as MidName7_0_, this_.School_DBID as School6_7_0_ FROM [Student] this_ WHERE @p0 in (SELECT this_0_.Student_DBID as y0_ FROM [ClassStudent] this_0_ WHERE @p1 in (SELECT this_0_0_.Class_DBID as y0_ FROM [StaffClass] this_0_0_ WHERE this_0_0_.Staff_DBID = @p2))]
Source=NHibernate
SqlString=SELECT this_.Student_DBID as Student1_7_0_, this_.DistrictStudentID as District2_7_0_, this_.LastName as LastName7_0_, this_.FirstName as FirstName7_0_, this_.MidName as MidName7_0_, this_.School_DBID as School6_7_0_ FROM [Student] this_ WHERE @p0 in (SELECT this_0_.Student_DBID as y0_ FROM [ClassStudent] this_0_ WHERE @p1 in (SELECT this_0_0_.Class_DBID as y0_ FROM [StaffClass] this_0_0_ WHERE this_0_0_.Staff_DBID = @p2))
StackTrace:
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet`1 querySpaces, IType[] resultTypes)
at NHibernate.Loader.Criteria.CriteriaLoader.List(ISessionImplementor session)
at NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results)
at NHibernate.Impl.CriteriaImpl.List(IList results)
at NHibernate.Impl.CriteriaImpl.List[T]()
at Test.Program.Main(String[] args) in C:\Projects\Test\Test\Program.cs:line 86
InnerException: System.FormatException
Message=Failed to convert parameter value from a String to a Int32.
Source=System.Data
StackTrace:
at System.Data.SqlClient.SqlParameter.CoerceValue(Object value, MetaType destinationType)
at System.Data.SqlClient.SqlParameter.GetCoercedValue()
at System.Data.SqlClient.SqlParameter.Validate(Int32 index, Boolean isCommandProc)
at System.Data.SqlClient.SqlCommand.BuildParamList(TdsParser parser, SqlParameterCollection parameters)
at System.Data.SqlClient.SqlCommand.BuildExecuteSql(CommandBehavior behavior, String commandText, SqlParameterCollection parameters, _SqlRPC& rpc)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader()
at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd)
at NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session)
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)
InnerException: System.FormatException
Message=Input string was not in a correct format.
Source=mscorlib
StackTrace:
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at System.String.System.IConvertible.ToInt32(IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at System.Data.SqlClient.SqlParameter.CoerceValue(Object value, MetaType destinationType)
Больше всего меня сейчас раздражает то, что сгенерированный SQL - это именно то, как я хочу, чтобы SQL выглядел. Если я запускаю SQL в SQL Server Managmeent Studio (SQL 2008) с ручной заменой параметров, я получаю правильный набор результатов:
SELECT
this_.Student_DBID as Student1_7_0_,
this_.DistrictStudentID as District2_7_0_,
this_.LastName as LastName7_0_,
this_.FirstName as FirstName7_0_,
this_.MidName as MidName7_0_,
this_.School_DBID as School6_7_0_
FROM
[Student] this_
WHERE
Student_DBID in (
SELECT
this_0_.Student_DBID as y0_
FROM
[ClassStudent] this_0_
WHERE Class_DBID in (
SELECT
this_0_0_.Class_DBID as y0_
FROM
[StaffClass] this_0_0_
WHERE
this_0_0_.Staff_DBID = 3664
)
)
Откуда берется попытка преобразования параметров? Почему это происходит? И что я могу сделать, чтобы избежать проблемы?
Спасибо!