我有一个NHibernate的自定义用户类型,自定义类型可以正常保存和更新。但是,在此自定义用户类型上使用QueryOver
时会发生错误。我收到错误消息:could not resolve property: SpecialType.Code of: NHibernateComplexIUserTypeExample.Person
。
我知道我可以使用Component()而不是带有自定义类型的Map()来映射SpecialType类,但是在此示例之外还有其他一些注意事项会使其不合适。如果可能的话,我想在保持它作为IUserType的同时解决这个问题。
以下是我的示例代码,可能会导致此错误。
错误发生在Program.cs
的行QueryOver<>
上。
public class Person
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual SpecialType SpecialType { get; set; }
public Person()
{
}
}
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");
}
}
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();
}
}
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
}
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();
}
}
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]
答案 0 :(得分:1)
快速(可能不是理想的)解决方案:
var specialTypeToCompare = new SpecialType("1", "some_description");
var results = session.QueryOver<Person>()
.Where(x => x.SpecialType.Code == specialTypeToCompare).List();
这可能不太理想,因为你必须为描述填写一些假值。 NHibernate应该生成正确的SQL。
另一个更复杂的解决方案是修改NullSafeSet
以允许字符串和 SpecialType
由自定义类型处理:
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();
清除上述内容的一种方法是为explicit
实施SpecialType
运算符,允许从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
- 可能不是您想要的。