使用Linq而不是SQL字符串构造查询

时间:2010-09-22 12:22:50

标签: c# .net sql linq

我有一个2.0服务器控件,它大致采用以下形式的动态查询:

string sql = "Select " + columnvariable + " FROM " + tablenamevariable

因此,显然,您可以从数据库中的任何有效表中为其提供任何有效的列名,它会将列中的值返回到DataReader中(在本例中)。

我正在尝试减少代码库中明确的零碎SQL的数量,并且更愿意在LINQ中执行此操作。是否有捷径可寻?它甚至可取吗?我想在这种情况下产生的零碎SQL会如此通用,以至于在这种情况下并没有真正造成安全问题。

即便如此,它似乎是一些相当基本的功能,所以我很好奇。我已经在我的项目中包含了System.Linq.Dynamic,但这似乎不再容易让程序员动态选择他们想要动态列的表。

我不是说这是一个讨论。我想要一个答案,例如“是的,这是可能的和微不足道的,这里是如何...”或“是的,但只有你构建这个精心设计的处理程序类集并基本上重写LINQ的部分,这里是如何......”

然而,我会有兴趣知道人们是否认为在LINQ中做这种事情最好被描述为a)一个快乐的好主意或b)疯狂的谈话。

7 个答案:

答案 0 :(得分:1)

因为你正在寻找一个直截了当的答案......不,这在LINQ中是不可能的。

LINQ本质上是强类型的,而你正在寻找一些完全动态的东西。如果您的表和列是运行时变量(即字符串),那么您的SQL将需要是一个字符串......或者您需要使用ORM(NHibernate,L2S,EF等)来访问数据。

答案 1 :(得分:0)

Rick Strahl博客中的这篇文章似乎在谈论这个主题:

http://www.west-wind.com/Weblog/posts/314663.aspx

如果没有更清楚的东西,我想我会根据这个做出选择。

任何其他潜在的回答者都应该考虑他们的潜在答案是否比上述链接更清晰。如果没有,那么可能会浪费你的时间参与其中。

编辑:虽然这与上述文章有关:

http://www.west-wind.com/WebLog/ShowPost.aspx?id=134706

完美地澄清了我的问题,实际上是另一篇文章的前传。有趣的东西。

答案 2 :(得分:0)

以下是好人'Scott Gu(thrie)的一个例子:

Dynamic LINQ, Part 1

答案 3 :(得分:0)

你是从错误的角度来看待这件事。你认为你想要这个:

void DoStuff(tablenamevariable, columnvariable)
{
    string sql = "Select " + columnvariable + " FROM " + tablenamevariable 
    // read data 
}


DoStuff("MyTable", "MyColumn");

为什么那个界面?这有什么不妥:

void DoStuff<T>(IQueryable<T> query)
{
      // read data
}


DoStuff(from t in MyTable select t.MyColumn);

这仍然定义了表&amp;调用函数中的列,但具有更好的类型安全性&amp;灵活性。例如,如果需要,您现在可以包含Where子句。

DoStuff(from t in MyTable where t.OtherColumn == 5 select t.MyColumn);

答案 4 :(得分:0)

不幸的是,你的问题没有说明columnvariable的确切来源,但如果我允许做出以下假设,那么我认为我可以为你提供一个近乎无足轻重的解决方案:

  • 假设1:columnvariable是一个通过方法调用传递的值,但最终它是一个常量。即它不是用户提供的字符串,不是从文件等中读取的。

  • 假设2:您可以访问传递columnvariable常量的所有代码。

如果这些假设成立,那么我的建议是更改包含此方法的方法:

string sql = "Select " + columnvariable + " FROM " + tablenamevariable

更像这样的事情:

public IQueryable<Customer> GetCustomers<T>(Expression<Func<Customer, T>> column)
{
    return db.Customers.Select(column);
}

然后代替

// Assuming "CustomerName" is the name of a column in the Customers table
var myCustomers = GetCustomers("CustomerName");
你会说

// Properly type-safe and compile-time checked
var myCustomers = GetCustomers(c => c.CustomerName);

答案 5 :(得分:0)

如果你需要它是完全动态的,即你可以有表名和列名的任何字符串,那么我怀疑你唯一的选择是手动构建表达式树。有点像...

public IQueryable<TResult> GetColumn<TResult>(string tableName, string columnName)
{
    // Get the table, e.g. for "Customers" get db.Customers
    var table = db.GetType().GetField(tableName).GetValue(db);

    // Find the specific type parameter (the T in IQueryable<T>)
    var iqueryableT = table.GetType().FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
    var type = iqueryableT.GetGenericArguments()[0];

    // Construct a query that retrieves the relevant column
    var param = Expression.Parameter(type);
    var prop = Expression.Property(param, columnName);
    var expression = Expression.Lambda(prop, param);

    // Call Queryable.Select() with the appropriate parameters
    // Notice there are two Select methods, need to get the right one...
    return (IQueryable<TResult>) typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
        .Where(meth => meth.Name == "Select")
        .Select(meth => meth.MakeGenericMethod(type, typeof(TResult)))
        .Where(meth => meth.GetParameters()[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(type, typeof(TResult))))
        .First()
        .Invoke(null, new object[] { table, expression });
}

然后你可以这样称呼它:

var customerNames = GetColumn<string>("Customers", "CustomerName");

当然,您仍然需要知道列的类型,因为您希望它返回一个正确的强类型查询。或者,如果您希望将所有数据自动转换为字符串,则可以添加Expression.Convert;那么你不需要每次都指定string,但是你的列数据当然会被转换成可能有损的字符串。

答案 6 :(得分:-1)

您的查询:

string sql = "Select " + columnvariable + " FROM " + tablenamevariable

可以像LINQ一样'翻译':

var sql = (from tablenamevariable select columnvariable).ToList();

现在'sql'变量包含'tablenamevariable'中存储的'columnvariable'列表。

使用LINQ可以使它变得简单。