簡體   English   中英

NHibernate QueryOver與IUserType失敗

[英]NHibernate QueryOver with IUserType fails

我在NHibernate中有一個自定義用戶類型,該自定義類型可以很好地保存和更新。 但是,在此自定義用戶類型上使用QueryOver時會發生錯誤。 我收到錯誤消息: could not resolve property: SpecialType.Code of: NHibernateComplexIUserTypeExample.Person

我知道我可以使用Component()而不是具有自定義類型的Map()映射SpecialType類,但是在此示例之外還有其他考慮因素使之不合適。 如果可能的話,我想解決這個問題,同時將其保留為IUserType。

這是可能導致此錯誤的示例代碼。

該錯誤在QueryOver<>行上的Program.cs發生。

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();
    }
}

數據庫表定義

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運算符,該運算符允許將stringstringSpecialType

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM