c# - 要在自定义 HqlGenerator 中使用的 HqlCase 实现

标签 c# nhibernate hql

我正在尝试构建一个必须构建 Case 构造的自定义 Hql 生成器。此构造将在 order by 子句中使用。我正在尝试以当前用户的语言对枚举(在本例中为 Gender 枚举)按字母顺序排序。如您所见,排序顺序是从 GenderResourceTextAttribute 中检索的。顺序数组中的值必须用在 Case 结构中。这是我目前所拥有的:

public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{        
    GenderResourceTextAttribute attribute = null;
    if (targetObject.Type.IsEnum)
    {
        attribute = targetObject.Type.GetCustomAttributes(typeof(GenderResourceTextAttribute), false).FirstOrDefault() as GenderResourceTextAttribute;
    }

    int[] order = attribute.GetSortOrderPosition();

    return treeBuilder.Case(new HqlWhen(....));            
}


[GenderResourceText]
public enum Gender
{
    Unknown = 0,
    Men,
    Women
}

我最终希望它生成类似于以下 sql 的内容:
case Gender when 0 then 1 when 1 then 2 else 0 end

我该如何实现?

已编辑:根据 Gerben 的建议添加了我的解决方案:

谢谢格本!

根据您提供的示例,我能够完成它:

    public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {        
        EnumResourceTextAttribute attribute = (EnumResourceTextAttribute)targetObject.Type.GetCustomAttributes(typeof(EnumResourceTextAttribute), false).FirstOrDefault();
        IEnumerable<int> sortOrder = attribute.GetSortOrderPositions(arguments[0].ToString() == "Descending" ? System.Data.SqlClient.SortOrder.Descending : SortOrder.Ascending);                       
        List<HqlExpression> parameters = new List<HqlExpression>();

        List<HqlWhen> hqlWhenList = new List<HqlWhen>();

        for(int index = 0; index < sortOrder.Count(); index++)
        {
            int position = sortOrder.ElementAt(index);
            hqlWhenList.Add(
                treeBuilder.When(
                    treeBuilder.Equality(visitor.Visit(targetObject).AsExpression(), treeBuilder.Constant(index)),
                    treeBuilder.Constant(position)
                )
            );
        }

        HqlCase hqlCase = treeBuilder.Case(hqlWhenList.ToArray());
        return hqlCase;            
    }

最佳答案

我的 BuildHql 方法如下所示。它使用下面包含的 CaseBuilder。

    public override HqlTreeNode BuildHql(
        MethodInfo method,
        Expression targetObject,
        ReadOnlyCollection<Expression> arguments,
        HqlTreeBuilder treeBuilder,
        IHqlExpressionVisitor visitor
        )
    {
        // Get the CaseBuilder form the arguments.
        var caseBuilder = (arguments[1] as ConstantExpression).Value as CaseBuilder;
        var hqlWhenList = new List<HqlWhen>();

        // Add a HqlWhen for each CaseBuilderOption. 
        foreach (var option in caseBuilder.Options)
        {
            // add the HqlWhen
            hqlWhenList.Add(
                // create HqlWhen with given treeBuilder.
                treeBuilder.When(
                    // compare given property with the When of the CaseBuilderOption.
                    treeBuilder.Equality(visitor.Visit(arguments[0]).AsExpression(), treeBuilder.Constant(option.When)),
                    // add the Then value of the CaseBuilderOption
                    treeBuilder.Constant(option.Then)
                )
            );
        }

        // 
        return 
            // cast the returned value to returntype of CaseBuilder.
            treeBuilder.Cast(
                // create the HqlCase with the TreeBuilder.
                treeBuilder.Case(
                    // add the created HqlWhen list.
                    hqlWhenList.ToArray(), 
                    // add the final or else value from the CaseBuilder.
                    treeBuilder.Constant(caseBuilder.ElseValue)
                ), 
            // the return type for the cast.
            caseBuilder.ReturnType
        );
    }

CaseBuilder 和 CaseBuilderOption 类。

public class CaseBuilder
{
    /// <summary>
    /// The options of this case.
    /// </summary>
    public List<CaseBuilderOption> Options { get; set; }

    /// <summary>
    /// Else return value.
    /// </summary>
    public object ElseValue { get; set; }

    /// <summary>
    /// Type of return value.
    /// </summary>
    public Type ReturnType { get; set; }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="returnType"></param>
    /// <param name="case1"></param>
    /// <param name="value1"></param>
    /// <param name="elseValue"></param>
    public CaseBuilder(Type returnType, object when, object then, object elseValue)
    {
        ReturnType = returnType;

        if (then.GetType() != returnType || elseValue.GetType() != returnType)
        {
            throw new Exception();
        }
        Options = new List<CaseBuilderOption>();
        Options.Add(new CaseBuilderOption() { When = when, Then = then });
        ElseValue = elseValue;
    }

    /// <summary>
    /// Add a WhenThen option to the case builder.
    /// </summary>
    /// <param name="when"></param>
    /// <param name="then"></param>
    /// <returns></returns>
    public CaseBuilder Append(object when, object then)
    {
        if (then.GetType() != ReturnType)
        {
            throw new Exception();
        }
        Options.Add(new CaseBuilderOption() { When = when, Then = then });

        return this;
    }
}

/// <summary>
/// A When Then option of a Case
/// </summary>
public class CaseBuilderOption
{
    /// <summary>
    /// When
    /// </summary>
    public object When { get; set; }

    /// <summary>
    /// returns this value if When and Case property are equal
    /// </summary>
    public object Then { get; set; }
}

希望对您有所帮助。

关于c# - 要在自定义 HqlGenerator 中使用的 HqlCase 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11671104/

相关文章:

c# - WebMatrix - 内连接和 Foreach 循环

c# - hibernate : Map all decimals with the same precision and scale

hibernate HQL : how to use a complex left join fetch

mysql - NHibernate.Spatial.MySQL : Null geometries and "No persister for: GeoAPI.Geometries.IGeometry" error

hibernate - HQL:在命名查询中使用 boolean 值

java - Hibernate HQL 错误 - 意外标记 :

c# - 将远程 SQL Server 数据库与本地 SQL Server Compact 数据库同步的最佳方法?

c# - 不同aspx页面上的下拉列表需要 "relate"互相

c# - 使用 Rx 订阅 bool 状态

nhibernate - 使用 SQLite 在 Fluent NHibernate 中使用 SchemaExport 的外键