2009-09-11 15 views
9

Me preguntaba si hay alguna forma de interceptar y modificar el sql generado desde linq a Sql antes de que se envíe la consulta.Cómo interceptar y modificar consulta SQL en Linq a SQL

Básicamente, tenemos una capa de seguridad de registro, que dada una consulta como 'SELECT * FROM registros' se modifique la consulta a ser algo así como 'seleccionar * de registros en los que [somesecurityfilter]'

estoy tratando para encontrar la mejor manera de interceptar y modificar el sql antes de que sea ejecutado por el proveedor linq a sql.

+6

¿Hay alguna razón específica por la que el filtro deba implementarse en el punto en que Linq genera el SQL SQL? Posiblemente sería más sencillo si los filtros: a) se implementaron a través de vistas en su base de datos ob) modelando su modelo de objetos de seguridad en su aplicación e implementando los filtros en el punto en que define sus expresiones de consulta de linq. –

Respuesta

2

Si desea interceptar el SQL generado por L2S y manipularlo, su mejor opción es crear clases de contenedor para SqlConnection, SqlCommand, DbProviderFactory etc. Proporcione una instancia envuelta de SqlConnection a la sobrecarga del constructor de contexto de datos L2S que tome una conexión db. En la conexión completa, puede reemplazar el DbProviderFactory con su propia clase derivada de DbProviderFactory personalizada que devuelve versiones empaquetadas de SqlCommand, etc.

P. ej.:

//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... 
    } 
} 

En el uso:

using (SomeDataContext dc = new SomeDataContext(new MySqlConnectionWrapper("connect strng")) 
{ 
    var q = from x in dc.SomeTable select x; 
    //...etc... 
} 

Dicho esto, es lo que realmente desea ir por ese camino? Tendrá que ser capaz de analizar las declaraciones SQL y las consultas generadas por L2S para modificarlas adecuadamente. Si puede modificar las consultas de linq para agregar lo que quiera agregar a ellas, esa es probablemente una mejor alternativa.

Recuerde que las consultas de Linq son compostables, por lo que puede agregar "extras" en un método diferente si tiene algo que desea agregar a muchas consultas.

+0

Esta es la ruta que he bajado. ¡Afortunadamente ya tenemos un analizador de SQL disponible! – mrwayne

+0

@mrwayne Esperaba que aparecieras para elegir la respuesta que querías. Gracias por tomar la decisión más difícil para mí; O) – Keng

+6

¿Cómo se las arregló para heredar de SqlConnection cuando está sellado? La clase no se compilará así. – chrishey

2

primero que vienen a la mente es modificar la consulta y devuelve el resultado en formato no 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; 
     } 

intenta agregar a su condición SelectCommand y ver si ayuda.

1

Intente configurar una vista en el DB que aplique el filtro de seguridad a los registros según sea necesario, y luego al recuperar registros a través de L2S. Esto asegurará que los registros que necesita no serán devueltos.

Como alternativa, agregue un .Where() a la consulta antes de enviarlo que aplicará el filtro de seguridad. Esto le permitirá aplicar el filtro mediante programación (en caso de que tenga que cambiar en función del escenario).

3

Ok, primero que responda directamente a su pregunta (pero siga leyendo para ver las palabras de advertencia;)), hay una manera, aunque delicada, de hacer lo que quiera.

// 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[] { }); 

Ahora, las advertencias.

  1. Debe saber qué consulta se genera para que sepa cómo modificarla, esto prolonga el desarrollo.
  2. Se sale del marco L2S y crea un agujero abierto para el desarrollo sostenible, si alguien modifica un Linq, le dolerá.
  3. Si su LINQ causa parámetros (donde tiene una u otra extensión causando una DONDE sección a aparecer con constantes) que complica las cosas, que tendrá que extraer y pasar esos parámetros a ExecuteQuery

Con todo, posible pero muy problemático Dicho esto, debería considerar usar la extensión .Where() como Yaakov sugerida. Si desea seguridad controll centralmente en el nivel de objeto utilizando este enfoque puede crear una extensión para manejarlo para usted

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

por lo que si se modifica ApplySecurity que se aplicará automáticamente a todas las consultas LINQ en el objeto al cliente.

+0

¿Hay alguna forma de forzar a linq a aplicar la "applysecurity" donde extensión a todas las consultas antes de ejecutarlas automáticamente, así que desde la perspectiva del desarrollador escribirían: var cs = (de c en db.Clientes select c); pero antes de que la consulta llegue al db, ¿aplica la cláusula security where? – Mustafakidd

+0

Que yo sepa, el código de construcción de árbol se genera en tiempo de compilación. – mmix