如何使查询没有泛型类型
本文关键字:泛型类型 查询 何使 | 更新日期: 2023-09-27 18:12:22
我将从一段代码开始:
var objectType = typeof(Department); // Department is entity class from linqdatacontext
using (var dataContext = new DataModel.ModelDataContext())
{
var entity = Expression.Parameter(objectType, "model");
var keyValue = Expression.Property(entity, "Id");
var pkValue = Expression.Constant(reader.Value);
var cond = Expression.Equal(keyValue, pkValue);
var table = dataContext.GetTable(objectType);
... // and here i don't how to proceed
}
我甚至不确定我是否正确地构建了这个表达式。然而,简单地说,我需要在该表上动态调用SingleOrDefault()
以通过主键查找实体。我发现的每个例子都是使用GetTable<>()
的通用变体,但我不能明显地使用它。我可能忽略了一些东西…
每当我构建表达式树时,我喜欢从我正在构建的示例开始:
() => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == 1);
由此,我们可以很容易地解剖目标表达式。你已经成功了一半;你只需要在表达式树中包含对GetTable
方法的调用,然后构建一个外部lambda表达式来调用整个东西:
using(var dataContext = new DataModel.ModelDataContext())
{
var getTableCall = Expression.Call(
Expression.Constant(dataContext),
"GetTable",
new[] { entityType });
var entity = Expression.Parameter(entityType, "entity");
var idCheck = Expression.Equal(
Expression.Property(entity, "Id"),
Expression.Constant(reader.Value));
var idCheckLambda = Expression.Lambda(idCheck, entity);
var singleOrDefaultCall = Expression.Call(
typeof(Queryable),
"SingleOrDefault",
new[] { entityType },
getTableCall,
Expression.Quote(idCheckLambda));
var singleOrDefaultLambda = Expression.Lambda<Func<object>>(
Expression.Convert(singleOrDefaultCall, typeof(object)));
var singleOrDefaultFunction = singleOrDefaultLambda.Compile();
return singleOrDefaultFunction();
}
我们必须将SingleOrDefault
调用转换为具有返回类型的对象,以便它可以作为Func<object>
函数的主体。
(未测试)
Edit:参数化数据上下文和值
现在我们正在构建这个函数:
(dataContext, value) => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == value);
您可以将常量更改为参数,并将这些参数添加到您编译的函数中:
var dataContextParameter = Expression.Parameter(typeof(ModelDataContext), "dataContext");
var valueParameter = Expression.Parameter(typeof(object), "value");
var getTableCall = Expression.Call(
dataContextParameter,
"GetTable",
new[] { entityType });
var entity = Expression.Parameter(entityType, "entity");
var idCheck = Expression.Equal(
Expression.Property(entity, "Id"),
valueParameter);
var idCheckLambda = Expression.Lambda(idCheck, entity);
var singleOrDefaultCall = Expression.Call(
typeof(Queryable),
"SingleOrDefault",
new[] { entityType },
getTableCall,
Expression.Quote(idCheckLambda));
var singleOrDefaultLambda =
Expression.Lambda<Func<ModelDataContext, object, object>>(
Expression.Convert(singleOrDefaultCall, typeof(object)),
dataContextParameter,
valueParameter);
var singleOrDefaultFunction = singleOrDefaultLambda.Compile();
// Usage
using(var dataContext = new DataModel.ModelDataContext())
{
return singleOrDefaultFunction(dataContext, reader.Value);
}
如果你使用的是。net 4,你可以尝试将返回的对象转换为dynamic
,这样你就可以像这样查询了。
using (var dataContext = new DataModel.ModelDataContext())
{
var entity = Expression.Parameter(objectType, "model");
var keyValue = Expression.Property(entity, "Id");
var pkValue = Expression.Constant(reader.Value);
var cond = Expression.Equal(keyValue, pkValue);
var table = dataContext.GetTable(objectType);
var result = table.Where(ent => ((dynamic)ent).SomeField == "SomeValue");
}
我仍然不完全确定你的整个问题(我怀疑关于dynamic
的答案将解决部分问题)。仍然,只是为了回答:
我发现的每个例子都是使用GetTable<>()的通用变体,但我不能明显使用
对于任何T
, Table<T>
实现(除其他接口外)ITable<T>
和ITable
。前者是泛型的,后者不是。
形式GetTable<T>()
返回这样一个Table<T>
。但是,表单GetTable(Type t)
返回一个ITable
。因为ITable
继承自IQueryable
,所以你可以查询它。如果您需要对该查询做一些通常需要类型知识的事情(例如对给定属性进行比较),那么根据Steve Danner给出的先前答案,dynamic
允许这种情况发生。
我个人会使用反射和LINQ动态查询库。
您可以获取具有dataContext.Mapping.GetMetaType(objectType).IdentityMembers
的表的所有键的列表,然后使用沿dataContext.GetTable(objectType).Where(key.Name + "==@0", id)
行的内容访问数据。
显然,我在那里遗漏了几个步骤—如果您有多个键,则需要在.IdentityMembers
上构建一个更完整的谓词,并且如果您总是只有一个键,则可以在其上使用. first()。我也没有测试过,但应该很接近。可能总共有6-7行代码——如果你需要的话,我可以写出来(并测试)。
编辑:LINQ动态查询库可以从微软下载http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx -只要在你的项目中包含DynamicLINQ.cs,你就很好了。