使用Orchard CMS中的接口查询NHibernate

时间:2016-03-24 04:51:42

标签: c# nhibernate orchardcms

我正在存储记录,以便在Orchard (CMS)中将2个部分记录链接在一起 记录将引用他们所链接的2个项目 这样做有几个记录表,它们都实现了相同的接口。

//One example of a record implementing the common interface
//This relation links a member to a home
public class HomeToMemberRelationRecord : IRelationResultable
{
    public virtual int Id { get; set; }
    [Aggregate]
    public virtual MemberPartRecord MemberPartRecord { get; set; }
    [Aggregate]
    public virtual HomePartRecord HomePartRecord { get; set; }

    //The interface is implemented here
    //...
}

我试图使用界面以通用方式查询这些记录 我有一个通用方法接受该类型并从该类型解析IRepository 问题是我想在查询中使用的属性因每条记录而异 在上述记录中,我可能希望从Member获取Home,但是另一条记录可能会将DogDogHouse相关联,我想找到所有内容Dog中的DogHouse 获取关系的UI元素将在需要时解析存储库:

Resolve<IRepository<T>>();

所以无法知道查询是否应该是:

.Where(x => x.HomePartRecord.ContentItemRecord.Id == id)
//or
.Where(x => x.DogHousePartRecord.ContentItemRecord.Id == id)

因此,关系记录实现的接口必须定义它们自己的查询方式。

我尝试使用方法返回我需要查询的属性,但NHibernate不喜欢这样,并为我提供NotSupportedException

// Method in the interface
int GetId();

// Attempt to call above method from a query.
var repo = _services.WorkContext.Resolve<IRepository<T>>(); //Constraint: where T : IMemberSearchResultable, new()
var relations = repo.Table.Where(x => x.GetRelevantId() == id);

我尝试在我的界面中使用属性,但是NHibernate只会在数据库中的记录中查找属性(它只存在于模型中)。

// Attempt to use a property in the interface instead of a method.
int RelevantId { get; set; } //Usage: .Where(x => x.RelevantId == id)

所以我尝试建立一个表达式:
(剧透:这也失败了。)

    public static Expression<Func<T, bool>> GetExpression<T>(string propertyName, int filterValue)
    {
        var parameter = Expression.Parameter(typeof(T));
        var property = Expression.Property(parameter, propertyName);
        var method = typeof(int).GetMethod("Equals", new[] { typeof(int) });
        var body = Expression.Call(property, method, Expression.Constant(filterValue));
        return (Expression<Func<T, bool>>)Expression.Lambda(body, parameter);
    }

    public Expression<Func<T, bool>> TestGet<T>(int id)
    {
        return GetExpression<T>("MemberPartRecord.ContentItemRecord.Id", id);
    }

我尝试过多种命名属性的方法,但找不到有效的方法 我当然可以在不使用接口的情况下构建我需要的特定查询。这让我相信应该可以使用界面构建相同的查询。

我是否构建了错误的表达方式,这是错误的方法,还是整个想法很难实现?

2 个答案:

答案 0 :(得分:1)

更改映射

如果选择更改映射(可能还有界面),则应尝试将实体映射为继承自公共接口,如inheritance mapping documentation中所示。它应该没有基类。

但是这意味着你的接口会定义一些HomeBase属性(类型为基类或另一个公共接口),这些属性将被映射,并且在你的实体中将存在。

然后,您将在实体上添加一些专门的ConcreteHomeDogHouse属性,而不是映射,并将HomeBase转换为具体的主页。

可能的问题

请注意代理,此类设置可能会强制您在lazy="no-proxy"属性映射上使用lazy="false"HomeBase

此外,a post写道,Fluent映射不支持此功能(我使用.hbm文件)。

最重要的是,如果您需要查询相关的Home特定属性(不属于HomeBase),那么您将遇到新的麻烦。

进入运行时类型检查

您可以改用当前的方法。但这需要相当多的修补。

目标是获得更有能力的帮助者:

public static Expression<Func<T, bool>> GetExpression<T, U>(
    Expression<Func<I, BaseHome>> interfaceHomePropertySelector, U filterValue)

帮助器实现应获取接口属性memberInfo,然后推断T实体中的相应成员。由此,它将构建适当的表达。

出于性能原因,应该缓存生成的推断,以降低后续使用的运行时成本。

肮脏的推理

您可以通过检查所有属性来推断T中的具体属性,并获取与interface属性兼容的第一个属性,但不具有与interface属性相同的名称。这需要在每个实体中只有一个Home属性。

更难推断

您可以采用更加详细的方式来获取正确的属性:运行时评估在访问T实例上的接口属性时实际调用的内容。为此,您需要使用与NHibernate用于处理延迟加载相同的代理方法。

实例化T代理的新虚拟实例,该实例用于在每次属性访问时调用您的回调。访问接口属性。你的回调应该至少触发两次:在接口属性访问时,然后在接口实现应该具体的属性访问。

因此,在您的回调中,您必须检查调用堆栈以检查您的情况,并推断出T的具体属性。

答案 1 :(得分:0)

我发布了迄今为止的答案,但我希望那里有人有更好的解决方案:

我已经设法为我的具体问题找到了一个可行的解决方案,但它不是一个完美的解决方案,因为它不允许您在查询中使用引用。 我基本上只能引用映射到数据库的C#记录中存在的属性。

这是我目前使用的方法:

    public static Expression<Func<T, bool>> GetExpression<T, U>(string propertyName, U filterValue)
    {
        var parameter = Expression.Parameter(typeof(T));
        var predicate = Expression.Lambda<Func<T, bool>>(
            Expression.Equal(Expression.Property(parameter, propertyName), 
            Expression.Constant(filterValue)), parameter);
        return predicate;
    }

我这样说:

GetExpression<T, U>("MemberPartRecord", rec);

这里的重大区别是我现在正在查询MemberPartRecord而不是尝试访问MemberPartRecord.ContentItemRecord.Id
对于我目前的需求,这已经足够了,但这只是因为我能够提供查询所需的记录。

我不接受这个答案,希望有人可以提供一个完整的答案,允许在查询中使用引用的记录。