ventajas que framework first example español desventajas code database entity-framework orm

database - que - ¿Qué marco ORM puede manejar mejor un diseño de base de datos MVCC?



entity framework que es (6)

A mi leal saber y entender, los marcos ORM van a querer generar el código CRUD para usted, por lo que deberían estar explícitamente diseñados para implementar una opción MVCC; No conozco ninguno que lo haga de la caja.

Desde el punto de vista de un marco de Entity, CSLA no implementa la persistencia para usted en absoluto; solo define una interfaz de "Adaptador de datos" que utiliza para implementar cualquier persistencia que necesite. De modo que podría configurar plantillas de generación de código (CodeSmith, etc.) para generar automáticamente la lógica CRUD para sus entidades CSLA que vayan junto con una arquitectura de base de datos MVCC.

Este enfoque funcionaría con cualquier marco de entidad, muy probablemente, no solo CSLA, sino que sería una implementación muy "limpia" en CSLA.

Al diseñar una base de datos para usar MVCC (Control de Concurrencia de Varias Versiones), usted crea tablas con un campo booleano como "IsLatest" o un entero "VersionId", y nunca hace ninguna actualización, solo inserta nuevos registros cuando cambian las cosas.

MVCC le ofrece auditoría automática para aplicaciones que requieren un historial detallado, y también alivia la presión sobre la base de datos con respecto a los bloqueos de actualización. Las desventajas son que hace que su tamaño de datos sea mucho más grande y ralentiza las selecciones, debido a la cláusula adicional necesaria para obtener la última versión. También hace que las claves foráneas sean más complicadas.

(Tenga en cuenta que no estoy hablando de la compatibilidad con MVCC nativa en RDBMS, como el nivel de aislamiento de instantáneas de SQL Server)

Esto se ha discutido en otras publicaciones aquí en Stack Overflow. [todo - enlaces]

Me pregunto, ¿cuál de los marcos de entidad / ORM prevalentes (Linq a Sql, ADO.NET EF, Hibernate, etc.) puede respaldar este tipo de diseño? Este es un cambio importante en el patrón de diseño de ActiveRecord típico, por lo que no estoy seguro de si la mayoría de las herramientas que existen podrían ayudar a alguien que decida ir por esta ruta con su modelo de datos. Estoy particularmente interesado en cómo se manejarán las claves externas, porque ni siquiera estoy seguro de la mejor manera de modelar los datos para admitir MVCC.


Podría considerar implementar el nivel de MVCC puramente en el DB, usando procesos y vistas almacenados para manejar mis operaciones de datos. Luego, podría presentar una API razonable para cualquier ORM que fuera capaz de mapear desde y hacia procs almacenados, y podría dejar que la DB se ocupe de los problemas de integridad de los datos (ya que se trata de una compilación para eso). Si fue por este camino, es posible que desee ver una solución de mapas más pura como IBatis o IBatis.net.


Diseñé una base de datos de manera similar (solo INSERTs - no UPDATEs, no DELETE).

Casi todas mis consultas SELECT estaban en contra de vistas de solo las filas actuales para cada tabla (número de revisión más alto).

Las vistas se veían así ...

SELECT dbo.tblBook.BookId, dbo.tblBook.RevisionId, dbo.tblBook.Title, dbo.tblBook.AuthorId, dbo.tblBook.Price, dbo.tblBook.Deleted FROM dbo.tblBook INNER JOIN ( SELECT BookId, MAX(RevisionId) AS RevisionId FROM dbo.tblBook GROUP BY BookId ) AS CurrentBookRevision ON dbo.tblBook.BookId = CurrentBookRevision.BookId AND dbo.tblBook.RevisionId = CurrentBookRevision.RevisionId WHERE dbo.tblBook.Deleted = 0

Y mis insertos (y actualizaciones y eliminaciones) fueron manejados por procedimientos almacenados (uno por tabla).

Los procedimientos almacenados se veían así ...

ALTER procedure [dbo].[sp_Book_CreateUpdateDelete] @BookId uniqueidentifier, @RevisionId bigint, @Title varchar(256), @AuthorId uniqueidentifier, @Price smallmoney, @Deleted bit as insert into tblBook ( BookId, RevisionId, Title, AuthorId, Price, Deleted ) values ( @BookId, @RevisionId, @Title, @AuthorId, @Price, @Deleted )

Los números de revisión se manejaron por transacción en el código de Visual Basic ...

Shared Sub Save(ByVal UserId As Guid, ByVal Explanation As String, ByVal Commands As Collections.Generic.Queue(Of SqlCommand)) Dim Connection As SqlConnection = New SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("Connection").ConnectionString) Connection.Open() Dim Transaction As SqlTransaction = Connection.BeginTransaction Try Dim RevisionId As Integer = Nothing Dim RevisionCommand As SqlCommand = New SqlCommand("sp_Revision_Create", Connection) RevisionCommand.CommandType = CommandType.StoredProcedure RevisionCommand.Parameters.AddWithValue("@RevisionId", 0) RevisionCommand.Parameters(0).SqlDbType = SqlDbType.BigInt RevisionCommand.Parameters(0).Direction = ParameterDirection.Output RevisionCommand.Parameters.AddWithValue("@UserId", UserId) RevisionCommand.Parameters.AddWithValue("@Explanation", Explanation) RevisionCommand.Transaction = Transaction LogDatabaseActivity(RevisionCommand) If RevisionCommand.ExecuteNonQuery() = 1 Then ''rows inserted RevisionId = CInt(RevisionCommand.Parameters(0).Value) ''generated key Else Throw New Exception("Zero rows affected.") End If For Each Command As SqlCommand In Commands Command.Connection = Connection Command.Transaction = Transaction Command.CommandType = CommandType.StoredProcedure Command.Parameters.AddWithValue("@RevisionId", RevisionId) LogDatabaseActivity(Command) If Command.ExecuteNonQuery() < 1 Then ''rows inserted Throw New Exception("Zero rows affected.") End If Next Transaction.Commit() Catch ex As Exception Transaction.Rollback() Throw New Exception("Rolled back transaction", ex) Finally Connection.Close() End Try End Sub

Creé un objeto para cada tabla, cada uno con constructores, propiedades y métodos de instancia, comandos de crear-actualizar-eliminar, un conjunto de funciones de búsqueda y funciones de clasificación IComparable. Fue una gran cantidad de código.

Tabla de BD uno a uno para el objeto VB ...

Public Class Book Implements iComparable #Region " Constructors " Private _BookId As Guid Private _RevisionId As Integer Private _Title As String Private _AuthorId As Guid Private _Price As Decimal Private _Deleted As Boolean ... Sub New(ByVal BookRow As DataRow) Try _BookId = New Guid(BookRow("BookId").ToString) _RevisionId = CInt(BookRow("RevisionId")) _Title = CStr(BookRow("Title")) _AuthorId = New Guid(BookRow("AuthorId").ToString) _Price = CDec(BookRow("Price")) Catch ex As Exception ''TO DO: log exception Throw New Exception("DataRow does not contain valid Book data.", ex) End Try End Sub #End Region ... #Region " Create, Update & Delete " Function Save() As SqlCommand If _BookId = Guid.Empty Then _BookId = Guid.NewGuid() End If Dim Command As SqlCommand = New SqlCommand("sp_Book_CreateUpdateDelete") Command.Parameters.AddWithValue("@BookId", _BookId) Command.Parameters.AddWithValue("@Title", _Title) Command.Parameters.AddWithValue("@AuthorId", _AuthorId) Command.Parameters.AddWithValue("@Price", _Price) Command.Parameters.AddWithValue("@Deleted", _Deleted) Return Command End Function Shared Function Delete(ByVal BookId As Guid) As SqlCommand Dim Doomed As Book = FindByBookId(BookId) Doomed.Deleted = True Return Doomed.Save() End Function ... #End Region ... #Region " Finders " Shared Function FindByBookId(ByVal BookId As Guid, Optional ByVal TryDeleted As Boolean = False) As Book Dim Command As SqlCommand If TryDeleted Then Command = New SqlCommand("sp_Book_FindByBookIdTryDeleted") Else Command = New SqlCommand("sp_Book_FindByBookId") End If Command.Parameters.AddWithValue("@BookId", BookId) If Database.Find(Command).Rows.Count > 0 Then Return New Book(Database.Find(Command).Rows(0)) Else Return Nothing End If End Function

Tal sistema preserva todas las versiones pasadas de cada fila, pero puede ser un verdadero dolor para administrar.

PROS:

  • Historia total preservada
  • Menos procedimientos almacenados

CONTRAS:

  • se basa en la aplicación de bases de datos para la integridad de datos
  • gran cantidad de código para ser escrito
  • No se administraron claves externas dentro de la base de datos (adiós a la generación automática de objetos estilo Linq-to-SQL)
  • Todavía no se me ocurrió una buena interfaz de usuario para recuperar todo lo preservado en el pasado.

CONCLUSIÓN:

  • No me tomaría tantas molestias en un nuevo proyecto sin una solución ORM lista para usar fácil de usar.

Tengo curiosidad si Microsoft Entity Framework puede manejar bien esos diseños de bases de datos.

Jeff y el resto del equipo de deben haber tenido que lidiar con problemas similares al desarrollar : las revisiones anteriores de las preguntas y respuestas editadas se guardan y recuperan.

Creo que Jeff ha declarado que su equipo usó Linq para SQL y MS SQL Server.

Me pregunto cómo manejaron estos problemas.


Siempre pensé que utilizarías un desencadenador db en la actualización y eliminaría esas filas en una tabla TableName_Audit.

Eso funcionaría con los ORM, le daría su historial y no reduciría el rendimiento de selección en esa tabla. ¿Es una buena idea o me estoy perdiendo algo?


Echa un vistazo al proyecto Envers: funciona bien con las aplicaciones JPA / Hibernate y básicamente lo hace por ti: realiza un seguimiento de las diferentes versiones de cada Entity en otra tabla y te brinda posibilidades similares a SVN ("Dame la versión de Person que se usa 2008-11 -05 ... ")

http://www.jboss.org/envers/

/ Jens


Lo que hacemos, es usar un ORM (hibernación) normal y manejar el MVCC con vistas + en lugar de desencadenantes.

Entonces, hay una vista v_emp, que simplemente se ve como una tabla normal, puede insertarla y actualizarla bien, sin embargo, cuando hace esto, los desencadenantes se encargan de insertar los datos correctos en la tabla base.

No ... Odio este método :) Iría con una API de procedimiento almacenado como lo sugiere Tim.