захватывайте запись linq to sql с помощью primarykey, не зная ее типа
-
27-10-2019 - |
Вопрос
как я могу получить запись (и в конечном итоге удалить ее) с помощью linq2sql, не зная типа во время компиляции?
пока что у меня есть
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
но, конечно, манекен не является реальной записью, это просто манекен
я не хочу использовать прямой sql (или executecommand), поскольку при удалении в частичном классе datacontext выполняется бизнес-логика
можно ли это как-то сделать?
большое вам спасибо!
Редактировать
в ответ на striplinwarior я отредактировал свой код, чтобы:
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
это запускает корректность кода удаления, но также, похоже, пытается сначала добавить запись в базу данных, поскольку я получаю sqlexception о том, что некоторые поля "not null" пусты, что, я думаю, верно для фиктивной записи, поскольку единственное, что у этого есть, это primarykey, остальное все пустое.итак, я попробовал другой опубликованный вами код (то, что я в любом случае всегда хотел иметь), и это работает отлично!
это мой текущий код:
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
Спасибо
Решение
Единственный способ, который я могу придумать, - это использовать информацию о модели из сопоставления вашей базы данных, чтобы выяснить, какой элемент представляет первичный ключ:
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()
(Здесь я использую LINQPad:Я предполагаю, что типичные модели LINQ to SQL имеют доступ к этой информации о сопоставлении.)
Затем используйте отражение, чтобы установить значение этого ключевого элемента для созданного вами фиктивного элемента.После этого вам нужно прикрепить манекен к таблице, прежде чем пытаться удалить его, передав false
в качестве второго параметра, сообщающего LINQ to SQL, что на самом деле вы не хотите обновлять объект, используя его текущие значения, но что с этого момента он должен отслеживать изменения.
tbl.Attach(dummy, false)
tbl.DeleteOnSubmit(dummy)
db.SubmitChanges()
Есть ли в этом смысл?
Редактировать
Когда вы только удаляете объект, вам не обязательно получать запись из базы данных.Если вы зададите значение ID объекта, а затем присоедините его к контексту (как показано выше), LINQ to SQL будет обрабатывать его так, как если бы он был извлечен из базы данных.В этот момент вызов DeleteOnSubmit должен сообщить контексту о необходимости создания DELETE
оператор в SQL, основанный на значении первичного ключа этого объекта.
Однако, если вам нужно извлечь объект для какой-либо цели, отличной от удаления, вам нужно будет создать выражение для представления запроса к этому объекту.Так, например, если бы вы писали запрос вручную, вы бы сказали что-то вроде:
Dim obj = tbl.First(Function(e) e.Id = ID)
Итак, чтобы динамически построить лямбда-выражение внутри круглых скобок, вы могли бы сделать что-то вроде этого:
Dim eParam = Expression.Parameter(rowType, "e")
Dim lambda = Expression.Lambda(Expression.Equal(Expression.MakeMemberAccess(eParam, idMember), Expression.Constant(ID)), eParam)
Затем вам нужно будет использовать отражение для вызова общего первого метода:
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})