使用LINQ-to-SQL模拟SQL插入触发器

6

使用LINQ-to-SQL时,我希望在插入父实体时自动创建子记录。基本上,模仿SQL Insert触发器的工作方式,但是在代码中进行,以便进行一些额外的处理。

父实体与子实体有关联,但似乎我不能在DataContext的SubmitChanges()期间简单地添加新的子记录。

例如,

public partial class Parent 
{
    partial void OnValidate(System.Data.Linq.ChangeAction action)
    {
        if(action == System.Data.Linq.ChangeAction.Insert)
        {
            Child c = new Child();
            ... set properties ...
            this.Childs.Add(c);
        }
    }
}

这将是理想的情况,但不幸的是,新创建的Child记录没有插入到数据库中。这很有道理,因为DataContext有一个对象/语句列表,可能不喜欢在其中添加新项目。
同样地,拦截DataContext中的partial void InsertParent(Parent instance)函数并尝试添加Child记录会产生相同的结果-没有错误,但数据库中没有添加任何内容。
有没有办法在不向表示层添加代码的情况下获得这种行为?
更新: OnValidate()InsertParent()函数都从DataContext的SubmitChanges()函数中调用。我怀疑这就是我尝试做的事情的固有困难-当DataContext正在提交现有更改到数据库时,它不允许插入其他对象(例如通过InsertOnSubmit())。
理想情况下,我希望将所有内容保留在一个事务中,以便在插入/更新期间发生任何错误时,实际上在数据库中不会更改任何内容。因此,我试图模仿SQL触发器功能,通过对DataContext的SubmitChanges()函数进行单个调用来自动插入子记录。
3个回答

6
如果您希望在保存之前进行操作,可以覆盖SubmitChanges方法,并调用GetChangeSet()获取待处理的更改内容。查找您感兴趣的内容(例如,delta.Inserts.OfType<Customer>()),然后进行所需的更改。 接下来调用base.SubmitChanges(...)
这里有一个相关的示例,处理删除操作。 (链接)

Marc,这个建议(以及你提供的链接)非常好,对我也非常有帮助,谢谢! - Funka

2
Add 方法仅设置两个对象之间的链接:它不会标记要插入到数据库中的添加项。为此,您需要在 DataContext 中包含的 Table<Child> 实例上调用 InsertOnSubmit。当然,问题在于没有本质的方法可以从您所描述的方法中访问您的 DataContext。
您可以通过在 DataContext 中实现 InsertParent 来访问它,因此我建议使用该路线(当然要使用 InsertOnSubmit 而不是 Add)。 编辑 我假设部分方法 InsertParent 将在某个时候由 DataContext 调用,但在查看自己的代码时,该方法似乎被定义但未被生成类引用。那么这有什么用呢?

我的想法完全一样 - “子”对象需要通过“InsertOnSubmit”添加到DataContext中,以便在调用SubmitChanges()时存储。 - marc_s
Ben: 如果一个部分类方法被实现了,它就会被调用。只要它只是被定义了(但没有被实现),什么也不会发生。 - marc_s
没错,但仍然需要一个调用点。在我的示例DataContext中,我找不到任何由生成的代码调用这些部分方法的地方。 - Ben M
Ben: 好的,我明白你的意思了- 我假设这些调用是在 Linq-to-SQL 运行时内部进行的(而不是生成的代码)。只要未实现部分类方法,这些调用将在编译/链接期间被优化掉 - 但一旦实现了部分类方法,它就会被调用。 - marc_s
使用Reflector进行了一些调查。部分声明仅用于提示正确的方法签名;L2S运行时使用反射来查找并调用它们。partial的一个有趣用法! - Ben M
谢谢大家 - 是的,InsertParent由DataContext调用,假设它已经实现了,就像Marc提到的那样。事实上,在DataContext SubmitChanges()期间,InsertParent和OnValidate函数都会被调用,这就是问题所在 - 在调用SubmitChanges()时不能调用InsertOnSubmit(),因为DataContext已经在对数据库进行更改。我将更新原始问题并尝试澄清这一点。 - BrandonB

0
在linq to sql中,您可以通过创建一个到dbml文件的部分类并插入一个局部方法来创建一个"trigger"。以下是一个示例,由于调用了内置删除,因此不会执行任何操作。
partial void DeleteMyTable(MyTable instance)
{
    //custom code here
    ExecuteDynamicDelete(instance);
    //or here :-)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接