[英]NHibernate QueryOver with IUserType fails
我在NHibernate中有一個自定義用戶類型,該自定義類型可以很好地保存和更新。 但是,在此自定義用戶類型上使用QueryOver
時會發生錯誤。 我收到錯誤消息: could not resolve property: SpecialType.Code of: NHibernateComplexIUserTypeExample.Person
。
我知道我可以使用Component()而不是具有自定義類型的Map()映射SpecialType類,但是在此示例之外還有其他考慮因素使之不合適。 如果可能的話,我想解決這個問題,同時將其保留為IUserType。
這是可能導致此錯誤的示例代碼。
該錯誤在QueryOver<>
行上的Program.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()
{
}
}
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]
快速(可能不理想)的解決方案:
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();
清理上面內容的一種方法是為SpecialType
實現一個explicit
運算符,該運算符允許將string
從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
s顯式轉換為SpecialType
可能不是您想要的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.