【问题标题】:LINQ to SQL Table Extensibility MethodsLINQ to SQL 表扩展方法
【发布时间】:2009-12-22 23:51:59
【问题描述】:

如果我有一个 LINQ to SQL 表,其中有一个名为 Alias 的字段。

然后有一个方法存根叫做 OnAliasChanging(string value);

我要做的是抓取值,检查数据库中值是否已经存在,然后将值设置为已经输入的值。

所以我可能会将我的别名从“griegs”更改为“slappy”,如果 slappy 存在,那么我想恢复为“griegs”的现有值。

所以我有;

    partial void OnaliasChanging(string value)
    {
        string prevValue = this.alias;
        this.Changed = true;
    }

当我检查 prevValue 的值时,它始终为空。

如何获取字段的当前值?

更新

如果我实现类似的东西;

    partial void OnaliasChanging(string value)
    {
        if (this.alias != null)
            this.alias = "TEST VALUE";
    }

它进入了一个不健康的无限循环。

如果我检查别名是否已经 == "TEST VALUE",则无限循环仍然保留,因为该值始终是原始值。

有没有办法做到这一点?

【问题讨论】:

    标签: linq-to-sql extensibility


    【解决方案1】:

    您发布的代码 sn-ps 无法合理解释您最终会出现无限循环的原因。我认为this.alias 可能是一个属性,而不是字符大小写所暗示的字段,但需要查看更多内容。如果它是一个属性,那么您在设置属性之前调用OnAliasChanging 方法;因此,尝试以相同的方法再次设置它总是会导致无限循环。通常设计这个场景的方法是在你的OnXyzChangingEventArgs派生中实现一个Cancel属性,或者在OnXyzChanging方法中保存旧值,然后在OnXyzChanged方法中执行检查/回滚如果你不能使用第一个(更好的)选项。

    不过,从根本上说,您尝试做的不是很好的设计,而且特别违反了 Linq to SQL 的原则。 Linq to SQL 实体应该是一个 POCO,根本不知道兄弟实体或底层数据库。要对每个属性更改执行重复检查,不仅需要访问 DataContextSqlConnection,而且还会导致技术上所谓的副作用(打开新的数据库连接和/或静默丢弃属性改变)。这种设计只会让路上发生神秘的车祸。

    事实上,您的特定场景是首先使 DataContext 类可扩展的主要原因之一。这种类型的操作属于那里。假设这里的实体名为User,表为Users

    partial class MyDataContext
    {
        public bool ChangeAlias(Guid userID, string newAlias)
        {
            User userToChange = Users.FirstOrDefault(u => u.ID == userID);
            if ((userToChange == null) || Users.Any(u => u.Alias == newAlias))
            {
                return false;
            }
            userToChange.Alias = newAlias;
    
            // Optional - remove if consumer will make additional changes
            SubmitChanges();
    
            return true;
        }
    }
    

    这封装了您要执行的操作,但不阻止消费者直接更改Alias 属性。 如果你能接受,我会停在那里 - 你的数据库本身应该仍然有一个 UNIQUE 约束,所以这个方法可以简单地记录并用作一个安全 尝试更改名称而不冒以后违反约束的风险的方法(尽管总是存在 一些 风险 - 您仍然可以有竞争条件,除非您将这一切都放入事务或存储过程中)。

    如果您绝对必须限制对基础属性的访问,一种方法是隐藏原始属性并制作只读包装器。在 Linq 设计器中,单击 Alias 属性,然后在属性表上,将 Access 更改为 Internal 并将 Name 更改为 AliasInternal(但不要'不要触摸来源!)。最后,为实体创建一个部分类(我会在与MyDataContext 部分类相同的文件中执行此操作)并为该属性编写一个只读包装器:

    partial class User
    {
        public string Alias
        {
            get { return AliasInternal; }
        }
    }
    

    您还必须将ChangeAlias 方法中的Alias 引用更新为AliasInternal

    请注意,这可能会破坏尝试在新的 Alias 包装器上过滤/分组的查询(我相信 Linq 会抱怨它找不到 SQL 映射)。该属性本身可以作为访问器正常工作,但如果您需要在Alias 上执行查找,那么您可能需要MyDataContext 中的另一个GetUserByAlias 辅助方法,该方法可以在@987654347 上执行“真实”查询@。

    当您决定除了域逻辑之外还想弄乱 Linq 的数据访问逻辑时,事情开始变得有点冒险,这就是为什么我在上面建议您只保留 Alias 属性并记录其用法适当。 Linq 是围绕乐观并发设计的;通常,当您需要在应用程序中强制执行 UNIQUE 约束时,您会等到实际保存更改后再处理约束冲突(如果发生)。如果您想立即执行,您的任务就会变得更加困难,这就是这种冗长和普遍笨拙的原因。

    再来一次 - 我建议反对创建只读包装器的额外步骤;无论如何,我已经提供了一些代码,以防您的规范出于某种原因需要它。

    【讨论】:

    • +1 谢谢@Aaron。关于您的实施的 1 个问题。您是否建议我更改数据竞赛 Designer.cs 文件中的属性?考虑到如果我使用设计器添加表格,那么我的更改将丢失,我对此有点犹豫。谢谢
    • 不,您不能更改设计器文件,它是自动生成的。您使用部分类创建一个新文件。如果您右键单击设计器表面并单击“查看代码”,它将为您执行此操作。
    • 哦,我不确定我是否完全回答了您的问题 - 假设您使用的是设计器,您可能无法将设置器更改为内部,但您可以更改“访问”该属性从“公共”到“受保护的内部”,然后创建实体本身的部分类以创建只读包装器(仅当您将其公开给外部程序集时才需要)。
    • @Aaron,我讨厌在这里听起来很愚蠢,但是您有机会发布一些代码吗?我了解您提供的 ChangeAlias 方法,但是如何从 User 对象的部分类中对其中的属性进行现成的包装?
    • 我已经用更多信息更新了帖子。 FWIW,我认为最好在 DataContext 中创建实用程序方法,但不要理会属性本身;您仍在数据库级别强制执行唯一性(我假设)。
    【解决方案2】:

    是否因为 OnaliasChanging 在初始化期间触发而挂起,所以您的支持字段(别名)永远不会被初始化,所以它始终为空?

    没有更多的上下文,这就是我听起来的样子。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多