如何在没有泛型类型的情况下进行查询?

时间:2011-10-26 15:14:00

标签: c# .net linq-to-sql dynamic-linq

我将从一段代码开始:

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<>()的泛型变体,但我显然无法使用它。我可能忽视了一些事情......

4 个答案:

答案 0 :(得分:1)

如果您使用的是.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");
}

答案 1 :(得分:1)

每当我构建表达式树时,我都想从我正在构建的示例开始:

() => 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>函数的主体。

(未测试的)

编辑:参数化数据上下文和值

现在我们正在建立这个功能:

(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);    
}

答案 2 :(得分:0)

答案 3 :(得分:0)

我会亲自使用Reflection和LINQ dynamic query library

您可以获取包含dataContext.Mapping.GetMetaType(objectType).IdentityMembers的表格的所有密钥列表,然后使用dataContext.GetTable(objectType).Where(key.Name + "==@0", id)行的内容访问数据。

显然,我在那里省了几步 - 如果你有多个键,你需要建立一个更全面的谓词,循环超过.IdentityMembers,如果你总是只有一个键,你可以使用.First()。我也没有测试过它,但它应该非常接近。它可能总共有6到7行代码 - 如果你需要,我可以把它编写(并测试)。


编辑:LINQ动态查询库可以从Microsoft http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx下载 - 只需在项目中包含DynamicLINQ.cs即可。