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查询的主要内容,如果未能解决你的问题,请参考以下文章

LinQ to Sql中的增删改查

C# 中的 LINQ to SQL 查询

LinQ to SQL 查询

EF 6.x,LINQ-to-SQL和原始SQL子句

如何编写这个 Linq-to-SQL 选择查询

如何在 Web 服务 asp.net 中返回 linq to sql 查询结果?