c# - NHibernate QueryOver 与 IUserType 失败

标签 c# nhibernate fluent-nhibernate queryover iusertype

我有一个 NHibernate 的自定义用户类型,并且该自定义类型可以很好地保存和更新。但是使用 QueryOver 时会出现错误在此自定义用户类型上。我收到错误消息:could not resolve property: SpecialType.Code of: NHibernateComplexIUserTypeExample.Person .

我知道我可以使用 Component() 而不是 Map() 与自定义类型来映射 SpecialType 类,但除此示例之外还有其他考虑因素,导致这种做法不合适。如果可能的话,我想解决这个问题,同时将其保留为 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");
    }
}

程序.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 运算符,该运算符允许从 string 转换为 特殊类型:

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 - 可能不是您想要的。

关于c# - NHibernate QueryOver 与 IUserType 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28456132/

相关文章:

c# - 如何通过 API 而不是参数文件传递 ARM 模板参数?

c# - Read 和 Load 的语义差异

c# - webclient 远程服务器返回错误 : (500) Internal Server Error

子查询中的 NHibernate HQL SELECT TOP

c# - NHibernate手动设置id自动递增

c# - npgsql/PostgreSQL 在 LAN 上的性能非常低

c# - 使用 NHibernate 的简单获取(使用按代码映射)非常慢

c# - Fluent Nhibernate 在新的父级和子级上保存 child.parentid =parentId

unit-testing - Fluent NHibernate - 对一对多 * 逆 * 映射进行单元测试

asp.net-mvc - ASP.NET MVC + fluent nHibernate,什么IoC工具?