通过主键获取linq到sql记录而不知道它的类型

时间:2011-01-31 16:45:06

标签: linq-to-sql delete-record

如何在编译时不知道类型的情况下使用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 partial class中删除了业务逻辑

这可以以某种方式完成吗?

非常感谢你!

修改

为了回应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

这确实触发了删除代码correclty,但似乎也尝试将记录首先添加到db中,因为我得到一个sqlexception,一些“not null”字段为空,我猜这是关于虚拟记录的,因为唯一的东西是主键,否则全是空的。所以我尝试了你张贴的其他代码(我总是想要的东西),这非常好!

她现在的代码:

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 个答案:

答案 0 :(得分:4)

我能想到的唯一方法是使用数据库映射中的模型信息来确定哪个成员代表主键:

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应该告诉上下文根据该对象的主键值在SQL中构造DELETE语句。

但是,如果您需要为删除以外的某些目的检索对象,则需要构造一个表达式来表示该对象的查询。因此,例如,如果您手动编写查询,您会说:

Dim obj = tbl.First(Function(e) e.Id = ID)

因此,要在括号内动态构建lambda表达式,您可以执行以下操作:

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