захватить linq к записи sql primarykey, не зная его типа - PullRequest
1 голос
/ 31 января 2011

как я могу получить запись (и в конечном итоге удалить ее), используя 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 происходит бизнес-логика

можно ли это как-нибудь сделать?

Большое спасибо!

EDIT

в ответ на 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, что некоторые "ненулевые" поля пусты, что, я думаю, верно для фиктивной записи , так как единственное, что у него есть - это ключ первичной записи, иначе все пусто. поэтому я попробовал другой опубликованный вами код (что-то, что я всегда хотел иметь), и это прекрасно работает!

мой текущий код:

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

Спасибо

1 Ответ

4 голосов
/ 31 января 2011

Единственный способ, которым я могу придумать, - это использовать информацию о модели из сопоставления вашей базы данных, чтобы выяснить, какой элемент представляет первичный ключ:

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()

Имеет ли это смысл?

Редактировать

Когда вы только удаляете объект, вам не обязательно получать запись из базы данных. Если вы установите значение идентификатора объекта и затем присоедините его к контексту (как показано выше), 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)

Тогда вам нужно будет использовать отражение, чтобы вызвать универсальный метод First:

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})
...