Linq to SQL中如何拦截和修改SQL查询
Posted
技术标签:
【中文标题】Linq to SQL中如何拦截和修改SQL查询【英文标题】:How to intercept and modify SQL query in Linq to SQL 【发布时间】:2009-09-11 06:00:42 【问题描述】:我想知道有没有什么办法可以在查询发送出去之前拦截和修改linq to Sql生成的sql?
基本上,我们有一个记录安全层,给定一个类似“select * from records”的查询,它会将查询修改为“select * from records WHERE [somesecurityfilter]”之类的内容
我正在尝试找到在 linq to sql 提供程序执行之前拦截和修改 sql 的最佳方法。
【问题讨论】:
是否有任何特定原因必须在 Linq to SQL 生成 SQL 时实施过滤器?如果过滤器是 a) 通过数据库中的视图实现,或者 b) 通过在应用程序中对安全对象模型进行建模并在定义 linq 查询表达式时实现过滤器,可能会更直接? 【参考方案1】:好的,首先直接回答您的问题(但请继续阅读以谨慎起见;)),有一种方法,尽管很挑剔,可以做您想做的事情。
// IQueryable<Customer> L2S query definition, db is DataContext (AdventureWorks)
var cs = from c in db.Customers
select c;
// extract command and append your stuff
DbCommand dbc = db.GetCommand(cs);
dbc.CommandText += " WHERE MiddleName = 'M.'";
// modify command and execute letting data context map it to IEnumerable<T>
var result = db.ExecuteQuery<Customer>(dbc.CommandText, new object[] );
现在,注意事项。
-
您必须知道生成了哪个查询,以便知道如何修改它,这会延长开发时间。
它脱离了 L2S 框架,从而为可持续发展创造了一个可能的漏洞,如果有人修改 Linq,它会受到伤害。
如果您的 Linq 导致参数(具有 where 或其他扩展名导致 WHERE 部分与常量一起出现)会使事情复杂化,您必须提取这些参数并将其传递给 ExecuteQuery
总而言之,可能但很麻烦。话虽如此,您应该考虑使用 .Where() 扩展名作为Yaakov 的建议。如果您想使用这种方法集中控制对象级别的安全性,您可以创建一个扩展来为您处理它
static class MySecurityExtensions
public static IQueryable<Customer> ApplySecurity(this IQueryable<Customer> source)
return source.Where(x => x.MiddleName == "M.");
//...
// now apply it to any Customer query
var cs = (from c in db.Customers select c).ApplySecurity();
因此,如果您修改 ApplySecurity,它将自动应用于 Customer 对象上的所有 linq 查询。
【讨论】:
是否有强制 linq 在自动执行所有查询之前应用“applysecurity”扩展名 - 所以从开发人员的角度来看,他们会写: var cs = (from c in db.客户选择 c);但是在查询到达数据库之前,它会应用安全 where 子句? 据我所知,树构建代码是在编译时生成的。【参考方案2】:如果你想拦截 L2S 生成的 SQL 并对其进行修改,最好的选择是为 SqlConnection、SqlCommand、DbProviderFactory 等创建一个包装类。将一个包装好的 SqlConnection 实例提供给 L2S 数据上下文构造函数重载,它需要一个数据库连接。在包装连接中,您可以将 DbProviderFactory 替换为您自己的自定义 DbProviderFactory 派生类,该类返回包装版本的 SqlCommand 等。
例如:
//sample wrapped SqlConnection:
public class mysqlConnectionWrapper : SqlConnection
private SqlConnecction _sqlConn = null;
public MySqlConnectionWrapper(string connectString)
_sqlConn = new SqlConnection(connectString);
public override void Open()
_sqlConn.Open();
//TODO: override everything else and pass on to _sqlConn...
protected override DbProviderFactory DbProviderFactory
//todo: return wrapped provider factory...
使用时:
using (SomeDataContext dc = new SomeDataContext(new MySqlConnectionWrapper("connect strng"))
var q = from x in dc.SomeTable select x;
//...etc...
也就是说,你真的想走那条路吗?您需要能够解析 L2S 生成的 SQL 语句和查询才能正确修改它们。如果您可以改为修改 linq 查询以附加您想要添加的任何内容,那可能是一个更好的选择。
请记住,Linq 查询是可组合的,因此如果您想要添加到多个查询中,可以在单独的方法中添加“附加”。
【讨论】:
这是我走的路线。幸运的是,我们手头已经有了 SQL 解析器! @mrwayne 我希望你能来选择你想要的答案。感谢您为我做出艰难的决定;O) 在 SqlConnection 被密封的情况下,你是如何设法继承它的?该类不会像那样编译。 否决,因为您不能从 SqlConnection 继承(并且 DataContext 不会接受使用 IDbConnection 构造,因为它显然会尝试在内部将其强制转换为 SqlConnection)。如果我错了,请告诉我。 @mrwayne 还在用这个吗?有没有关于如何在 SqlConnection 被密封时扩展它的示例代码?【参考方案3】:我首先想到的是修改查询并以非 LINQ 格式返回结果
//Get linq-query as datatable-schema
public DataTable ToDataTable(System.Data.Linq.DataContext ctx, object query)
if (query == null)
throw new ArgumentNullException("query");
IDbCommand cmd = ctx.GetCommand((IQueryable)query);
System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter();
adapter.SelectCommand = (System.Data.SqlClient.SqlCommand)cmd;
DataTable dt = new DataTable("sd");
try
cmd.Connection.Open();
adapter.FillSchema(dt, SchemaType.Source);
adapter.Fill(dt);
finally
cmd.Connection.Close();
return dt;
尝试将您的条件添加到 selectCommand 中,看看是否有帮助。
【讨论】:
【参考方案4】:尝试在数据库中设置一个视图,根据需要将安全过滤器应用于记录,然后在通过 L2S 检索记录时。这将确保您需要的记录不会被退回。
或者,在查询提交之前添加一个 .Where() 来应用安全过滤器。这将允许您以编程方式应用过滤器(以防它需要根据场景进行更改)。
【讨论】:
以上是关于Linq to SQL中如何拦截和修改SQL查询的主要内容,如果未能解决你的问题,请参考以下文章