我正在存储记录,以便在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
,但是另一条记录可能会将Dog
与DogHouse
相关联,我想找到所有内容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);
}
我尝试过多种命名属性的方法,但找不到有效的方法 我当然可以在不使用接口的情况下构建我需要的特定查询。这让我相信应该可以使用界面构建相同的查询。
我是否构建了错误的表达方式,这是错误的方法,还是整个想法很难实现?
答案 0 :(得分:1)
如果选择更改映射(可能还有界面),则应尝试将实体映射为继承自公共接口,如inheritance mapping documentation中所示。它应该没有基类。
但是这意味着你的接口会定义一些HomeBase
属性(类型为基类或另一个公共接口),这些属性将被映射,并且在你的实体中将存在。
然后,您将在实体上添加一些专门的ConcreteHome
或DogHouse
属性,而不是映射,并将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
对于我目前的需求,这已经足够了,但这只是因为我能够提供查询所需的记录。
我不接受这个答案,希望有人可以提供一个完整的答案,允许在查询中使用引用的记录。