Tome Linq al registro SQL de PrimaryKey sin conocer su tipo
-
27-10-2019 - |
Pregunta
¿Cómo puedo obtener un registro (y eventualmente eliminarlo) usando LINQ2SQL sin saber el tipo en el momento de la compilación?
Hasta ahora tengo
Sub Delete(ByVal RecordType As String, ByVal ID As Integer)
Dim dummy = Activator.CreateInstance(MyAssembly, RecordType).Unwrap
Dim tbl = GetTable(dummy.GetType)
tbl.DeleteOnSubmit(dummy)
End Sub
Pero, por supuesto, el muñeco no es un registro real, es solo un muñeco
No quiero usar SQL directo (o ejecutecomano), ya que hay una lógica comercial en la eliminación en la clase parcial de DataContext
¿Se puede hacer esto de alguna manera?
¡Muchas gracias!
EDITAR
En respuesta a Stringlinwarior, edité mi código a:
Sub Delete(ByVal RecordType As ObjectType, ByVal ID As Integer)
Dim dummy = Activator.CreateInstance(ObjectType.Account.GetType.Assembly.FullName, RecordType.ToString).Unwrap
SetObjProperty(dummy, PrimaryKeyField(RecordType), ID)
Dim tbl = GetTable(dummy.GetType)
tbl.Attach(dummy)
tbl.DeleteOnSubmit(dummy)
SubmitChanges()
End Sub
Esto dispara el código de deleción corrección, pero también parece tratar de agregar el registro primero a la DB, ya que obtengo una Sqlexception de que algunos campos "no nulos" están vacíos, lo que supongo que es cierto sobre el registro ficticio, como el registro, como el Lo único que esto tiene es el primario, de lo contrario, está todo vacío. Así que probé el otro código que publicaste (algo que de todos modos siempre quise tener) ¡y eso funciona excelente!
suy mi código actual:
Function LoadRecord(ByVal RecordType As String, ByVal RecordID As Integer) As Object
Dim dummy = Activator.CreateInstance(AssemblyName, RecordType).Unwrap
Dim rowType = dummy.GetType
Dim eParam = Expression.Parameter(rowType, "e")
Dim idm = rowType.GetProperty(PrimaryKeyField(RecordType))
Dim lambda = Expression.Lambda(Expression.Equal(Expression.MakeMemberAccess(eParam, idm), Expression.Constant(RecordID)), eParam)
Dim firstMethod = GetType(Queryable).GetMethods().[Single](Function(m) m.Name = "Single" AndAlso m.GetParameters().Count() = 2).MakeGenericMethod(rowType)
Dim tbl = GetTable(rowType)
Dim obj = firstMethod.Invoke(Nothing, New Object() {tbl, lambda})
Return obj
End Function
Sub Delete(ByVal RecordType As String, ByVal RecordID As Integer)
Dim obj = LoadRecord(RecordType, RecordID)
Dim tbl = GetTable(obj.GetType)
tbl.DeleteOnSubmit(obj)
SubmitChanges()
End Sub
Gracias
Solución
La única forma en que puedo pensar es usar la información del modelo de la asignación de su base de datos para determinar qué miembro representa la clave principal:
Dim primaryKey = (From t In db.Mapping.GetTables() _
Where t.RowType.Type = tableType _
Let keyMember = (From dm In t.RowType.DataMembers where dm.IsPrimaryKey).FirstOrDefault() _
Select keyMember.Member.Name).First()
(Estoy usando LinqPad aquí: supongo que los modelos típicos de LINQ a SQL tienen esta información de mapeo disponible).
Luego use Reflection para establecer el valor de ese miembro clave en el elemento ficticio que ha creado. Después de eso, debe conectar el muñeco a la mesa antes de tratar de eliminarlo, pasando false
Como segundo parámetro para decirle a LINQ a SQL que realmente no desea actualizar el objeto usando sus valores actuales, pero que debe rastrear los cambios de aquí en adelante.
tbl.Attach(dummy, false)
tbl.DeleteOnSubmit(dummy)
db.SubmitChanges()
¿Tiene sentido?
Editar
Cuando solo elimina un objeto, no necesariamente tiene que obtener el registro de la base de datos. Si establece el valor de identificación del objeto y luego lo adjunta al contexto (como se muestra arriba), Linq a SQL lo tratará como si se recuperara de la base de datos. En ese momento, llamar a DeleteonSubmit debe decirle al contexto que construya un DELETE
Declaración en SQL basado en el valor de clave principal de ese objeto.
Sin embargo, si necesita recuperar el objeto para algún propósito que no sea la eliminación, deberá construir una expresión para representar la consulta para ese objeto. Entonces, por ejemplo, si estaba escribiendo la consulta manualmente, diría algo como:
Dim obj = tbl.First(Function(e) e.Id = ID)
Entonces, para construir dinámicamente la expresión de Lambda dentro de los paréntesis, es posible que haga algo como esto:
Dim eParam = Expression.Parameter(rowType, "e")
Dim lambda = Expression.Lambda(Expression.Equal(Expression.MakeMemberAccess(eParam, idMember), Expression.Constant(ID)), eParam)
Entonces necesitaría usar la reflexión para invocar el primer método genérico:
Dim firstMethod = GetType(Queryable).GetMethods().[Single](Function(m) m.Name = "Single" AndAlso m.GetParameters().Count() = 2).MakeGenericMethod(rowType)
Dim obj = firstMethod.Invoke(Nothing, New Object() {tbl, lambda})