带有 IUserType 的 NHibernate QueryOver 失败
NHibernate QueryOver with IUserType fails
我有一个带有 NHibernate 的自定义用户类型,自定义类型可以很好地保存和更新。但是,在此自定义用户类型上使用 QueryOver
时会发生错误。我收到错误消息:could not resolve property: SpecialType.Code of: NHibernateComplexIUserTypeExample.Person
.
我知道我可以使用具有自定义类型的 Component() 而不是 Map() 来映射 SpecialType class,但是在这个示例之外还有其他一些考虑因素使得这样做不合适。如果可能的话,我想在将其保留为 IUserType 的同时解决此问题。
这是我的示例代码,可能会导致此错误。
错误发生在 Program.cs
与 QueryOver<>
的行上。
Person.cs
public class Person
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual SpecialType SpecialType { get; set; }
public Person()
{
}
}
PersonMap.cs
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.Id);
Map(x => x.Name).Not.Nullable();
Map(x => x.SpecialType)
.CustomType<SpecialTypeUserType>()
.Not.Nullable()
.Column("SpecialType_Code");
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
// create db session
var sessionFactory = Program.CreateSessionFactory();
var session = sessionFactory.OpenSession();
// query db using complex iusertype
var results = session.QueryOver<Person>().Where(x => x.SpecialType.Code == "1").List();
if (results != null)
{
foreach (var result in results)
{
Console.WriteLine("Person {0} has code {1}.", result.Name, result.SpecialType.Code);
}
}
}
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
MsSqlConfiguration
.MsSql2008
.ConnectionString("..."))
.Mappings(
m =>
{
m.FluentMappings.AddFromAssemblyOf<Person>();
})
.BuildSessionFactory();
}
}
SpecialTypeUserType.cs
public class SpecialTypeUserType : global::NHibernate.UserTypes.IUserType
{
#region IUserType Members
public object Assemble(object cached, object owner)
{
// used for caching, as our object is immutable we can just return it as is
return cached;
}
public object DeepCopy(object value)
{
//? should we implement deep copy for this?
return value;
}
public object Disassemble(object value)
{
// used for caching, as our object is immutable we can just return it as is
return value;
}
public new bool Equals(object x, object y)
{
// implements equals itself so we use this implementation
if (x == null)
{
return false;
}
else
{
return x.Equals(y);
}
}
public int GetHashCode(object x)
{
if (x == null)
{
throw new ArgumentNullException("x");
}
// object itself implements GetHashCode so we use that
return x.GetHashCode();
}
public bool IsMutable
{
get
{
return false;
}
}
public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
if (names == null)
{
throw new ArgumentNullException("names");
}
// we get the string from the database using the NullSafeGet used to get strings
string codeString = (string)global::NHibernate.NHibernateUtil.String.NullSafeGet(rs, names[0]);
SpecialType newSpecialType = new SpecialType(codeString, "Test...");
return newSpecialType;
}
public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
// set the value using the NullSafeSet implementation for string from NHibernateUtil
if (value == null)
{
global::NHibernate.NHibernateUtil.String.NullSafeSet(cmd, null, index);
return;
}
value = ((SpecialType)value).Code;
global::NHibernate.NHibernateUtil.String.NullSafeSet(cmd, value, index);
}
public object Replace(object original, object target, object owner)
{
// as our object is immutable we can just return the original
return original;
}
public Type ReturnedType
{
get
{
return typeof(SpecialType);
}
}
public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get
{
// we store our SpecialType.Code in a single column in the database that can contain a string
global::NHibernate.SqlTypes.SqlType[] types = new global::NHibernate.SqlTypes.SqlType[1];
types[0] = new global::NHibernate.SqlTypes.SqlType(System.Data.DbType.String);
return types;
}
}
#endregion
}
SpecialType.cs
public class SpecialType
{
public string Code { get; private set; }
public string Description { get; private set; }
public SpecialType(string code, string description)
{
this.Code = code;
this.Description = description;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
SpecialType type = obj as SpecialType;
if (type == null)
{
return false;
}
if (object.ReferenceEquals(this, type))
{
return true;
}
if (type.Code == null && this.Code != null)
{
return false;
}
else if (type.Code != null && this.Code == null)
{
return false;
}
else if (type.Code != null && this.Code != null)
{
if (!type.Code.Equals(this.Code, StringComparison.OrdinalIgnoreCase))
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
return this.Code.GetHashCode();
}
}
数据库Table定义
CREATE TABLE [dbo].[Person](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[SpecialType_Code] [nvarchar](255) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
一个快速(可能不理想)的解决方案:
var specialTypeToCompare = new SpecialType("1", "some_description");
var results = session.QueryOver<Person>()
.Where(x => x.SpecialType.Code == specialTypeToCompare).List();
虽然这可能并不理想,因为您必须为描述填写一些假值。不过 NHibernate 应该生成正确的 SQL。
另一个稍微复杂一点的解决方案是修改 NullSafeSet
以允许字符串 和 SpecialType
s 由自定义类型处理:
public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
// set the value using the NullSafeSet implementation for string from NHibernateUtil
if (value == null)
{
global::NHibernate.NHibernateUtil.String.NullSafeSet(cmd, null, index);
return;
}
/* Allow for the possibility of a string */
string valueToSet = null;
if (value.GetType() == typeof(string))
{
valueToSet = (string)value;
}
else if (value.GetType() == typeof(SpecialType))
{
valueToSet = ((SpecialType)value).Code;
}
global::NHibernate.NHibernateUtil.String.NullSafeSet(cmd, valueToSet, index);
}
然后修改您的查询以使用 Restrictions.Where
,这有点冗长:
var results = session.QueryOver<Person>()
.Where(
Restrictions.Eq(
Projections.Property<Person>(p => p.SpecialType), "1"))
.List();
清理上述问题的一种方法是为 SpecialType
实现一个 explicit
运算符,允许从 string
到 SpecialType
的转换:
public static explicit operator SpecialType(string s)
{
return new SpecialType(s, null);
}
现在您可以缩短 QueryOver 代码:
var results = session.QueryOver<Person>()
.Where(p => p.SpecialType == (SpecialType)"1")
.List();
然而,这样做的巨大缺点是人们将能够在您的应用程序中将 string
显式转换为 SpecialType
——可能不是您想要的。
我有一个带有 NHibernate 的自定义用户类型,自定义类型可以很好地保存和更新。但是,在此自定义用户类型上使用 QueryOver
时会发生错误。我收到错误消息:could not resolve property: SpecialType.Code of: NHibernateComplexIUserTypeExample.Person
.
我知道我可以使用具有自定义类型的 Component() 而不是 Map() 来映射 SpecialType class,但是在这个示例之外还有其他一些考虑因素使得这样做不合适。如果可能的话,我想在将其保留为 IUserType 的同时解决此问题。
这是我的示例代码,可能会导致此错误。
错误发生在 Program.cs
与 QueryOver<>
的行上。
Person.cs
public class Person
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual SpecialType SpecialType { get; set; }
public Person()
{
}
}
PersonMap.cs
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.Id);
Map(x => x.Name).Not.Nullable();
Map(x => x.SpecialType)
.CustomType<SpecialTypeUserType>()
.Not.Nullable()
.Column("SpecialType_Code");
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
// create db session
var sessionFactory = Program.CreateSessionFactory();
var session = sessionFactory.OpenSession();
// query db using complex iusertype
var results = session.QueryOver<Person>().Where(x => x.SpecialType.Code == "1").List();
if (results != null)
{
foreach (var result in results)
{
Console.WriteLine("Person {0} has code {1}.", result.Name, result.SpecialType.Code);
}
}
}
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
MsSqlConfiguration
.MsSql2008
.ConnectionString("..."))
.Mappings(
m =>
{
m.FluentMappings.AddFromAssemblyOf<Person>();
})
.BuildSessionFactory();
}
}
SpecialTypeUserType.cs
public class SpecialTypeUserType : global::NHibernate.UserTypes.IUserType
{
#region IUserType Members
public object Assemble(object cached, object owner)
{
// used for caching, as our object is immutable we can just return it as is
return cached;
}
public object DeepCopy(object value)
{
//? should we implement deep copy for this?
return value;
}
public object Disassemble(object value)
{
// used for caching, as our object is immutable we can just return it as is
return value;
}
public new bool Equals(object x, object y)
{
// implements equals itself so we use this implementation
if (x == null)
{
return false;
}
else
{
return x.Equals(y);
}
}
public int GetHashCode(object x)
{
if (x == null)
{
throw new ArgumentNullException("x");
}
// object itself implements GetHashCode so we use that
return x.GetHashCode();
}
public bool IsMutable
{
get
{
return false;
}
}
public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
if (names == null)
{
throw new ArgumentNullException("names");
}
// we get the string from the database using the NullSafeGet used to get strings
string codeString = (string)global::NHibernate.NHibernateUtil.String.NullSafeGet(rs, names[0]);
SpecialType newSpecialType = new SpecialType(codeString, "Test...");
return newSpecialType;
}
public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
// set the value using the NullSafeSet implementation for string from NHibernateUtil
if (value == null)
{
global::NHibernate.NHibernateUtil.String.NullSafeSet(cmd, null, index);
return;
}
value = ((SpecialType)value).Code;
global::NHibernate.NHibernateUtil.String.NullSafeSet(cmd, value, index);
}
public object Replace(object original, object target, object owner)
{
// as our object is immutable we can just return the original
return original;
}
public Type ReturnedType
{
get
{
return typeof(SpecialType);
}
}
public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get
{
// we store our SpecialType.Code in a single column in the database that can contain a string
global::NHibernate.SqlTypes.SqlType[] types = new global::NHibernate.SqlTypes.SqlType[1];
types[0] = new global::NHibernate.SqlTypes.SqlType(System.Data.DbType.String);
return types;
}
}
#endregion
}
SpecialType.cs
public class SpecialType
{
public string Code { get; private set; }
public string Description { get; private set; }
public SpecialType(string code, string description)
{
this.Code = code;
this.Description = description;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
SpecialType type = obj as SpecialType;
if (type == null)
{
return false;
}
if (object.ReferenceEquals(this, type))
{
return true;
}
if (type.Code == null && this.Code != null)
{
return false;
}
else if (type.Code != null && this.Code == null)
{
return false;
}
else if (type.Code != null && this.Code != null)
{
if (!type.Code.Equals(this.Code, StringComparison.OrdinalIgnoreCase))
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
return this.Code.GetHashCode();
}
}
数据库Table定义
CREATE TABLE [dbo].[Person](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[SpecialType_Code] [nvarchar](255) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
一个快速(可能不理想)的解决方案:
var specialTypeToCompare = new SpecialType("1", "some_description");
var results = session.QueryOver<Person>()
.Where(x => x.SpecialType.Code == specialTypeToCompare).List();
虽然这可能并不理想,因为您必须为描述填写一些假值。不过 NHibernate 应该生成正确的 SQL。
另一个稍微复杂一点的解决方案是修改 NullSafeSet
以允许字符串 和 SpecialType
s 由自定义类型处理:
public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
// set the value using the NullSafeSet implementation for string from NHibernateUtil
if (value == null)
{
global::NHibernate.NHibernateUtil.String.NullSafeSet(cmd, null, index);
return;
}
/* Allow for the possibility of a string */
string valueToSet = null;
if (value.GetType() == typeof(string))
{
valueToSet = (string)value;
}
else if (value.GetType() == typeof(SpecialType))
{
valueToSet = ((SpecialType)value).Code;
}
global::NHibernate.NHibernateUtil.String.NullSafeSet(cmd, valueToSet, index);
}
然后修改您的查询以使用 Restrictions.Where
,这有点冗长:
var results = session.QueryOver<Person>()
.Where(
Restrictions.Eq(
Projections.Property<Person>(p => p.SpecialType), "1"))
.List();
清理上述问题的一种方法是为 SpecialType
实现一个 explicit
运算符,允许从 string
到 SpecialType
的转换:
public static explicit operator SpecialType(string s)
{
return new SpecialType(s, null);
}
现在您可以缩短 QueryOver 代码:
var results = session.QueryOver<Person>()
.Where(p => p.SpecialType == (SpecialType)"1")
.List();
然而,这样做的巨大缺点是人们将能够在您的应用程序中将 string
显式转换为 SpecialType
——可能不是您想要的。