Cómo utilizar EntityFramework para llamar a procedimientos almacenados
Sabemos que EF puede generar SQL automáticamente para operaciones basadas en modelos a través de metadatos (es decir, modelo conceptual, modelo de almacenamiento y mecanismo de seguimiento de estado y mapeo C/S). SQL es ideal para proyectos simples en los que no tiene que preocuparse en absoluto por el almacenamiento de datos y puede contratar desarrolladores sin ningún conocimiento de bases de datos. Pero, después de todo, un ideal es un ideal. Para el desarrollo a nivel empresarial, necesitamos controlar el funcionamiento de los datos a nivel de base de datos. En este sentido, podemos dar dos escenarios de aplicación típicos:
Eliminación lógica: para algunos datos importantes, es posible que necesitemos hacerlos permanentes. Cuando intentamos "eliminar" estos datos, no los eliminaremos de la tabla (eliminación física), sino que marcaremos el registro como eliminado.
Procesamiento concurrente: para resolver el problema de obtener y recuperar; los mismos datos Para problemas que otros usuarios modifican o eliminan durante el envío, generalmente agregamos lógica de control de concurrencia a nivel SQL. El enfoque típico es agregar un campo como VersionNo a cada tabla, puede usar un TimeStamp o usar directamente un INT o GUID.
Para resolver estos problemas, no puede utilizar el SQL que EF genera automáticamente para nosotros, sino solo nuestros procedimientos almacenados personalizados.
En segundo lugar, las condiciones necesarias para realizar la coincidencia automática de procedimientos almacenados
El mecanismo de mapeo automático de procedimientos almacenados proporcionado en este artículo se completa mediante la generación de código. Para decirlo claramente, lee el archivo de modelo .edmx original e importa procedimientos almacenados CUD basados en la tabla de datos utilizada en el modelo de almacenamiento analizando la tabla, luego agrega entidades en el nodo de mapeo de concepto/almacenamiento entre estas relaciones de mapeo de procedimientos almacenados. Para lograr dicha generación de código, se requieren las siguientes tres reglas de mapeo fijas.
Nombre de la tabla de datos - nombre del procedimiento almacenado: esta relación de mapeo puede ayudarnos a encontrar los tres procedimientos almacenados CUD correspondientes a través del nombre de la entidad en el modelo de almacenamiento (si la entidad es una tabla de datos);
Nombre de parámetro de procedimiento almacenado de nombre de columna de tabla de datos: al ejecutar un procedimiento almacenado, la entidad del modelo conceptual utiliza un determinado valor de atributo como parámetro correspondiente a través del mapeo;
Versión de nombre de parámetro de proceso: al asignar Parámetros, esta asignación determina si se utiliza la versión original o la versión actual.
En la práctica, estos procedimientos estándar suelen ser generados por generadores de código (consulte mi artículo "Crear un generador de código es fácil: ¿Cómo generar código a partir de plantillas T4? [Artículo siguiente]"), existe tal una relación de mapeo.
Basado en estos tres mapeos, definí la siguiente interfaz llamada IProcedureNameConverter, donde OperationKind es mi enumeración personalizada utilizada para representar el tipo de operación CUD.
1: Interfaz pública IProcedureNameConverter
2: {
3: cadena GetProcedureName(string tableName, OperationKind OperationKind);
4: cadena GetColumnName(cadena nombre del parámetro);
5: DataRowVersion GetVersion(cadena nombre del parámetro);
6: }
7:
8 : enumeración pública OperationKind
9: {
10: Insertar,
11: Actualizar,
12: Eliminar
13:}
De acuerdo con la convención de nomenclatura adoptada por nuestro proyecto actual, definí el siguiente DefaultNameConverter predeterminado, que representa una relación de mapeo. Por ejemplo, si hay una tabla de datos llamada T_USER (mayúsculas). , Separado por "_", con el prefijo T_), que corresponde al nombre del procedimiento almacenado CUD: P_USER_I, P_USER_U y P_USER_D (mayúsculas, el prefijo P_ representa el procedimiento almacenado, el sufijo _I/U/D representa la operación CUD escriba, en medio del nombre de la tabla elimine el prefijo). Si el nombre de la columna es USER_ID, el nombre del parámetro es p_user_id (minúscula, con el prefijo p_). Si necesita asignar valores a los parámetros, debe cambiar el prefijo p_ al prefijo o_ (o_user_id).
1: clase pública DefaultNameConverter: IProcedureNameConverter
2: {
3: cadena pública GetProcedureName(string tableName, OperationKind OperationKind)
4: {
5: cambiar (tipo de operación)
6: {
7: caso OperationKind.Insert:
8: regresar string.Format("P_{0}_I", tableName.Substring(2));
9: caso OperationKind.Update:
10: devuelve string.Format("P_ {0}_U", tableName.Substring(2));
9: {
10: return string.Substring(2));
11 : Predeterminado:
12: devuelve string.Format("P_{0}_D", tableName.Substring(2));
13: }
14: }
15:
16: cadena pública GetColumnName (nombre de parámetro de cadena)
17: {
18: devuelve nombre de parámetro.
19: }
20:
21: DataRowVersion pública GetVersion(nombre del parámetro de cadena)
22: {
23: if(parameterName.StartsWith(" o"))
24: {
25: return DataRowVersion.Original;
26:}
27: else
28: {
29: devuelve DataRowVersion.
30:}
31: }
32: }
Tres: Generar un nuevo modelo .edmx a través de T4
Utilizamos generación de código basada en T4 Las personas que conocen EF no deberían Will. no estar familiarizado con la T4. Si está interesado en la generación de código, puede consultar mi artículo "Varias soluciones de generación de código integradas con VS [Resumen del blog (***8)]". Aquí, se utilizan el kit de herramientas de código abierto T4 ToolBox y SQL Server SMO para obtener información de procedimientos almacenados. Todos los textos involucrados en la conversión se implementan en el siguiente tipo ProcedimientoMappingTemplate. Debido al gran contenido, se ignora la implementación específica. Los amigos interesados pueden descargar el código fuente. ProcedimientoMappingTemplate tiene dos parámetros de constructor que representan: el archivo .edmx de origen, los nombres del servidor y de la base de datos, el esquema del procedimiento almacenado (el valor predeterminado es dbo) y un ProcedimientoNameConverter específico (el valor predeterminado es DefaultNameConverter).
1: clase pública ProcedimientoMappingTemplate: Plantilla
2: {
3: modelo de origen de documento Xml público { get; conjunto privado }
4. público IProcedureNameConverter ProcedimientoNameConverter { get; conjunto privado }
5: base de datos pública { get; conjunto privado }
6. esquema de cadena pública { get;
7:
8: Public ProcessMappingTemplate (cadena sourceModelFile, cadena serverName, cadena base de datos);
9: public ProcedimientoMappingTemplate (cadena sourceModelFile, cadena serverName, cadena nombre de la base de datos,
10.IProcedureNameConverter procedimientoNameConverter, esquema de cadena);
11:
12: XElement virtual protegido GenerateStorageModelNode(); 13: XElement virtual protegido GenerateMappingNode();
14: cadena de anulación pública TransformText()
15: {
16. );
17: XElement newMappingNode = this.GenerateMappingNode();
18.
19: StorageModels")[0];
20: StorageModelNode.InnerXml = newStorageModelNode.Elements().ToArray()[0].ToString();
21:
22: MappingNode XmlNode = this.SourceModel.GetElementsByTagName("edmx:StorageModels" [0]);