1

界面的 XML 注释ICompositeUserType告诉我:

This interface allows a custom type to define "properties".
--> These need not necessarily correspond to physical .NET style properties.

我有这样的情况,我将多个列映射到一个列表中:

// Each column is mapped into this object
public class IdentifiedValue
{
    public string Name;
    public object Value;
}

public class CompanyContainer
{
    public virtual int Id { get; set; }
    public virtual IList<IdentifiedValue> Values { get; set; }
}

// The columns being mapped:
public class GeneratedColumns
{
    public static string[] ColumnsInfo
    {
        get
        {
            return new[]
                {
                    "CODE_TYPE",
                    "CODE_VALUE",
                    "CODE_DESC_NL",
                    "CODE_DESC_FR",
                    "CODE_DESC_EN"
                };
        }
    }
}

ICompositeUserType 的实现:(无关方法省略)

public class IdentifiedValueMapper : ICompositeUserType
{
    public string[] PropertyNames
    {
        get
        {
            var result = new List<string>();
            foreach (var columnInfo in GeneratedColumns.ColumnsInfo)
            {
                result.Add(columnInfo);
            }
            return result.ToArray();
        }
    }

    public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
    {
        if (dr == null)
        {
            return null;
        }
        var result = new List<IdentifiedValue>();
        for (int i = 0; i < names.Length; i++)
        {
            var value = NHibernateUtil.String.NullSafeGet(dr, names[i], session, owner) as string;
            result.Add(new IdentifiedValue()
                {
                    Name = names[i],
                    Value = value
                });
        }
        return result;
    }

    public IType[] PropertyTypes
    {
        get
        {
            var result = new List<IType>();
            foreach (var columnInfo in GeneratedColumns.ColumnsInfo)
            {
                result.Add(NHibernateUtil.String);
            }
            return result.ToArray();
        }
    }

    public Type ReturnedClass
    {
        get { return typeof(List<IdentifiedValue>); }
    }
}

映射本身

public class CompanyContainerMap : ClassMap<CompanyContainer>
{
    public CompanyContainerMap()
    {
        Table("S1073_CODE");
        Id(x => x.Id).Column("CODE_ID");
        var cols = Map(x => x.Values)
            .CustomType<IdentifiedValueMapper>()
            .Columns.Clear();

        foreach (var col in GeneratedColumns.ColumnsInfo)
        {
            cols.Columns.Add(col);
        }
    }
}

失败的测试代码:

    [TestMethod]
    public void TestMethod1()
    {
        var factory = BuildSessionFactory();

        using (var session = factory.OpenSession())
        {
            var query = session.QueryOver<CompanyContainer>();
            query.Where(Restrictions.Eq("CODE_VALUE", "1"));
            var result = query.List();
            var blah = result;
        }
    }

执行查询时的异常: could not resolve property: CODE_VALUE of: CompanyContainer

所以看起来它仍在尝试访问不存在的 CompanyContainer.CODE_VALUE。我不确定它为什么要访问 .NET 属性,因为我只想生成WHERE CODE_VALUE='1'.

在没有 Where 子句的情况下执行可以正常工作。

4

1 回答 1

3

因为文档不是很好,所以识别这类东西并不容易。

我通过 nhibernate 代码进行了调试,发现 nhibernate 只需要某个属性路径用于存储在 Value 中的属性列表。

路径是<PropertyName>.<key>,所以你可以使用这样的东西:

query.Where(Restrictions.Eq("Values.CODE_VALUE", "1"));

顺便说一句,您也可以通过使用动态组件部分映射来实现相同的结果。

在你的映射中,使用这个

DynamicComponent(p => p.Values, m =>
{
    foreach (var col in GeneratedColumns.ColumnsInfo)
    {
        m.Map(col).CustomSqlType("nvarchar(50)");
    }
});

而不是您的值映射并使用例如IDictionary作为值的类型

public virtual IDictionary Values { get; set; }

这将生成相同的表结构并简单地将所有值检索到字典中。

查询的工作方式完全相同。

我认为这样代码更容易理解,你可以摆脱ICompositeUserType实现。

要创建更安全的类型,您可以为列定义添加对象类型和 sql 类型列表

public static string[] ColumnSqlTypes
{
    get
    {
        return new[]
        {
            "nvarchar(50)",
            "int",
            "nvarchar(255)",
            "nvarchar(255)",
            "nvarchar(255)"
        };
    }
}

public static Type[] ColumnTypes
{
    get
    {
        return new[]
        {
            typeof(string),
            typeof(Int32),
            typeof(string),
            typeof(string),
            typeof(string)
        };
    }
}

并将映射更改为

public class CompanyContainerMap : ClassMap<CompanyContainer>
{
    public CompanyContainerMap()
    {
        Table("S1073_CODE");
        Id(x => x.Id).Column("CODE_ID");
        DynamicComponent(p => p.Values, m =>
        {
            for(var index = 0; index < GeneratedColumns.ColumnsInfo.Length; index++)
            {
                m.Map(GeneratedColumns.ColumnsInfo[index])
                    .CustomSqlType(GeneratedColumns.ColumnSqlTypes[index])
                    .CustomType(GeneratedColumns.ColumnTypes[index]);
            }
        });
    }
}

现在你会看到,如果你查询

query.Where(Restrictions.Eq("Values.CODE_VALUE", "1"));

它将引发异常,因为它需要 CODE_VALUE 的整数!

将其更改为

query.Where(Restrictions.Eq("Values.CODE_VALUE", 1));

工作得很好。

于 2013-11-16T11:19:28.103 回答