registros - update en c#
El método más rápido para inserciones, actualizaciones y selecciones de SQL Server (6)
Yo uso SPs y esta no es una pregunta SP versus código "Build your SQL command". Estoy buscando un método de alto rendimiento para una aplicación backend que maneje muchas transacciones pequeñas. Uso SQLDataReader para la mayoría de las declaraciones, ya que el reenvío solo funciona en la mayoría de los casos para mí.
Lo he visto hecho de muchas maneras, y he usado la mayoría de ellas yo mismo.
Métodos que definen y aceptan los parámetros del procedimiento almacenado como parámetros en sí mismos y compilación usando cmd.Parameters.Add (con o sin especificar el tipo y / o la longitud del valor de DB)
Reúna sus parámetros SP y sus valores en una matriz o hashtable, luego pase a un método más abstracto que analiza la colección y luego ejecuta cmd.Parameters.Add
Clases que representan tablas, inicialización de la clase según necesidad, establecimiento de las propiedades públicas que representan los campos de tabla y métodos de llamada como Guardar, Cargar, etc.
Estoy seguro de que hay otros que he visto pero que tampoco puedo pensar en este momento. Estoy abierto a todas las sugerencias.
¿Más rápido para el tiempo de ejecución o más rápido para el tiempo de programación? Lo único que podría hacer para aumentar el rendimiento en el n. ° 1 es utilizar varios subprocesos y conexiones para hacer los insertos: puede hacer esto con SQLCommand.BeginExecuteNonQuery
Personalmente, soy un gran admirador de la generación de código. Lanzo mi propio XML casero, y en tiempo de compilación lo ejecuto a través de un XSLT para generar mis archivos .CS. Describo el proceso en esta publicación Usando XSLT para generar código de Contadores de rendimiento . Aunque el enlace analiza la generación de códigos de contadores de rendimiento, uso el mismo proceso para generar mi DAL.
Entonces crearía un XML como:
<query name="LoadCustomerByName" returns="Customer">
<parameter name="name" type="String"/>
<text>SELECT ... FROM Customers WHERE name=@name</text>
</query>
y luego el XLST transformaría esto en algo así como:
public Customer LoadCustomerByName(
SqlConnection conn,
SqlTransaction trn,
String name)
{
using (Sqlcommand cmd = new SqlCommand(@"SELECT ... FROM ...", conn, trn))
{
cmd.Parameters.AddWithValue("@name", name);
using (SqlDataReader rdr = cmd.ExecuteReader ())
{
Customer c = new Customer();
// Load c from rdr
return c;
}
}
}
Ahora explico muchos detalles de lo que realmente hace la transformación XSLT, pero lo realmente importante es que este método me da un control absoluto sobre cómo creo mi DAL y es flexible en todos los aspectos, ya que el código .CS generado es totalmente impulsado por mis XSLT. Puedo cambiar el XLST y esto dará como resultado la regeneración de cada método en mi DAL. Facilita la exploración de diversas soluciones, me permite agregar instrumentación al código (como contadores para medir el rendimiento de cada consulta y la frecuencia de uso) y muchas más.
Esto es lo que básicamente hacen los diversos diseñadores de VS para ti, pero si das el paso adicional para controlar el proceso de generación de código, tienes mucha más flexibilidad y control sobre el resultado.
Lo único que no me gusta es que el retorno de rendimiento no puede residir en un intento ... atrapar bloque. Por lo tanto, el manejo / registro de excepciones centralizado no puede ser acomodado.
He usado un appraoch similar, pero paso un IEnumerable como parámetro. Entonces no tengo que usar retorno de rendimiento.
Esta respuesta se centra principalmente en las operaciones ''seleccionar'' frente a actualizar / crear / eliminar. Creo que es más raro actualizar más de uno o pocos registros a la vez, por lo que también creo que ''select'' es donde suelen ocurrir los cuellos de botella. Dicho esto, debe conocer su aplicación (perfil). El mejor lugar para enfocar el tiempo de optimización es casi siempre en el nivel de la base de datos en las consultas en sí, en lugar del código del cliente. El código del cliente es solo la plomería: no es la fuerza principal de su aplicación. Sin embargo, como la plomería tiende a ser reutilizada en muchas aplicaciones diferentes, simpatizo con el deseo de que sea lo más óptima posible, y por lo tanto, tengo mucho que decir sobre cómo construir ese código.
Tengo un método genérico para seleccionar consultas / procedimientos en mi capa de datos que se parece a esto:
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
{
//ConnectionString is a private static property in the data layer
// You can implement it to read from a config file or elsewhere
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
rdr.Close();
}
}
}
Y eso me permite escribir métodos de capa de datos públicos que usan métodos anónimos para agregar los parámetros. El código que se muestra funciona con .Net 2.0+, pero puede escribirse aún más corto con .Net 3.5:
public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID)
{
//I could easily use a stored procedure name instead of a full sql query
return Retrieve(
@"SELECT c.*
FROM [ParentTable] p
INNER JOIN [ChildTable] c ON c.ParentID = f.ID
WHERE f.ID= @ParentID", delegate(SqlParameterCollection p)
{
p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
}
);
}
Voy a detenerme aquí para que pueda volver a señalarte el código anterior que utiliza el método anónimo para la creación de parámetros.
Este es un código muy limpio, en el sentido de que pone la definición de la consulta y la creación de parámetros en el mismo lugar, al mismo tiempo que le permite abstraer la base de datos de la conexión / código de llamada a un lugar más reutilizable. No creo que esta técnica esté cubierta por ninguno de los puntos en su pregunta, y también es bastante rápido. Creo que esto cubre la esencia de tu pregunta.
Sin embargo, quiero continuar para explicar cómo encaja todo esto. El resto es bastante sencillo, pero también es fácil incluirlo en una lista o algo similar y hacer las cosas mal, lo que en última instancia perjudica el rendimiento. Entonces, pasando a la capa empresarial, se utiliza una fábrica para traducir los resultados de la consulta a objetos (c # 3.0 o posterior):
public class Foo
{
//various normal properties and methods go here
public static Foo FooFactory(IDataRecord record)
{
return new Foo
{
Property1 = record[0],
Property2 = record[1]
//...
};
}
}
En lugar de tener estos en vivo en su clase, también podría agruparlos a todos en una clase estática específicamente diseñada para contener los métodos de fábrica.
Necesito hacer un cambio al método de recuperación original. Ese método "produce" el mismo objeto una y otra vez, y esto no siempre funciona tan bien. Lo que queremos hacer de manera diferente para que funcione es forzar una copia del objeto representado por el registro actual, de modo que cuando el lector muta para el siguiente registro estamos trabajando con datos limpios. Esperé hasta después de mostrar el método de fábrica para que podamos usar eso en el código final. El nuevo método Retrieve tiene este aspecto:
private static IEnumerable<T> Retrieve(Func<IDataRecord, T> factory,
string sql, Action<SqlParameterCollection> addParameters)
{
//ConnectionString is a private static property in the data layer
// You can implement it to read from a config file or elsewhere
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return factory(rdr);
rdr.Close();
}
}
}
Y ahora llamaríamos a ese nuevo método Retrieve () así:
public IEnumerable<Foo> GetFooChildrenByParentID(int ParentID)
{
//I could easily use a stored procedure name instead of a full sql query
return Retrieve(Foo.FooFactory,
@"SELECT c.*
FROM [ParentTable] p
INNER JOIN [ChildTable] c ON c.ParentID = f.ID
WHERE f.ID= @ParentID", delegate(SqlParameterCollection p)
{
p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
}
);
}
Obviamente, este último método se puede ampliar para incluir cualquier lógica comercial adicional necesaria. También resulta que este código es excepcionalmente rápido, ya que aprovecha las características de evaluación diferida de IEnumerable. La desventaja es que tiende a crear una gran cantidad de objetos efímeros, y eso puede dañar el rendimiento transaccional que usted solicitó. Para evitar esto, a veces rompo un buen n-tier y paso los objetos IDataRecord directamente al nivel de presentación y evito la creación innecesaria de objetos para registros que están simplemente vinculados a un control de grilla de inmediato.
Actualizar / Crear código es similar, con la diferencia de que generalmente solo está cambiando un registro a la vez en lugar de muchos.
O bien, podría ahorrarte la lectura de esta larga publicación y solo decirte que utilices Entity Framework;)
Este ha sido por un tiempo, pero si estás abierto a usar un Micro-ORM, solo usa dapper-dot-net .
Tiene una sintaxis muy clara, es extremadamente rápida y fácil de incluir en cualquier proyecto. Se está utilizando en producción en , y es probable que esta página le haya llegado. Tiene soporte para todos los métodos SQL habituales como métodos de extensión y admite asincronización en todo.
Algunas comparaciones de rendimiento:
No se trata de insertar o actualizar, sino que hice algunos puntos de referencia para velocidades de lectura con diversos enfoques. De todas, las rutas de DataTable
parecían más lentas del grupo. . El enfoque de Joel es básicamente el más rápido que puedes obtener ...