Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Entity framework 实体框架每种类型更新5个表,更改子类型但保持相同的基本类型_Entity Framework_Table Per Type - Fatal编程技术网

Entity framework 实体框架每种类型更新5个表,更改子类型但保持相同的基本类型

Entity framework 实体框架每种类型更新5个表,更改子类型但保持相同的基本类型,entity-framework,table-per-type,Entity Framework,Table Per Type,我有一个简单的层次结构 public abstract class CommunicationSupport { public SupportTypeEnum Type { get; set; } public Country Origin { get; set; } // National or Foreign support } public class TelecomSupport : CommunicationSupport { public string Nu

我有一个简单的层次结构

public abstract class CommunicationSupport
{
    public SupportTypeEnum Type { get; set; }
    public Country Origin { get; set; } // National or Foreign support
}

public class TelecomSupport : CommunicationSupport
{
    public string Number { get; set; }
}

public class PostalSupport : CommunicationSupport
{
    public Address Address { get; set; }
}
我计划为我的数据库使用每个类型的表层次结构。因此,将创建3个表,一个基本表和两个子表,使用与基本表相同的主键

我的问题是,我希望能够通过更改通信支持的类型来更新它。 假设我创建了一个TelecomSupport,保存它,然后将其类型更改为PostalSupport并再次保存(更新)。我期望的结果是EF保留相同的基本记录(通信支持Id),但删除TelecomSupport表中的记录,并在PostalSupport中创建一个新记录。 因此,TelecomSupport和PostalSupport是互斥的,不能共享相同的基础通信支持

如何使用EntityFramework 5实现这一点


谢谢你的帮助

我没有一个好的答案,但我可以想出四个真正可行的“解决方案”:

  • 不要对主键使用DBMS计算值(如果已经使用自然键,也可以)
  • 使用DBMS计算的代理键
  • 按照类似的方式进行操作
  • 做一些邪恶的巫术
  • 更新:似乎有一个普遍的共识,那就是尝试根本不值得;因此,大多数人只是使用存储过程来解决问题

    使用自然关键点 首先,请记住,EF跟踪的对象是DAL的一部分,而不是域模型(无论您是否使用POCO)。有些人不需要域模型,但请记住它,因为我们现在可以将这些对象视为表记录的表示形式,我们用域对象所不需要的方式来处理这些记录

    在这里,我们使用删除实体的记录,然后使用在单个事务中添加具有相同主键的新记录。请参见下面示例代码中的
    ChangeType
    方法

    从理论上讲,诚信是可以的,从理论上讲,EF可以检测到你在做什么并优化事情。实际上,它目前没有(我分析了SQL接口以验证这一点)。结果是它看起来很难看(
    DELETE
    +
    INSERT
    而不是
    UPDATE
    ),因此如果系统美观和性能是问题,那么它可能是不可能的。如果你能接受,那就相对简单了

    下面是我用来测试的一些示例代码(如果您想进行实验,只需创建一个新的控制台应用程序,添加对
    EntityFramework
    程序集的引用,然后粘贴代码)

    A
    是基类,
    X
    Y
    是子类。我们认为<代码> ID>代码>是一个自然键,所以我们可以将它复制到子类复制构造函数中(这里只针对<代码> y>代码>实现)。代码创建一个数据库,并使用
    X
    类型的记录对其进行种子设定。然后,它运行并将其类型更改为
    Y
    ,显然在这个过程中丢失了
    X
    特定的数据。复制构造函数是转换数据的地方,如果数据丢失不是业务流程的一部分,则可以对其进行归档。唯一“有趣”的代码是
    ChangeType
    方法,其余的是样板文件

    using System;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity;
    using System.Linq;
    
    namespace EntitySubTypeChange {
        abstract class A {
            [DatabaseGenerated(DatabaseGeneratedOption.None)]
            public int Id { get; set; }
            public string Foo { get; set; }
            public override string ToString() {
                return string.Format("Type:\t{0}{3}Id:\t{1}{3}Foo:\t{2}{3}",
                    this.GetType(), Id, Foo, Environment.NewLine);
            }
        }
    
        [Table("X")]
        class X : A {
            public string Bar { get; set; }
            public override string ToString() {
                return string.Format("{0}Bar:\t{1}{2}", base.ToString(), Bar, Environment.NewLine);
            }
        }
    
        [Table("Y")]
        class Y : A {
            public Y() {}
            public Y(A a) {
                this.Id = a.Id;
                this.Foo = a.Foo;
            }
    
            public string Baz { get; set; }
            public override string ToString() {
                return string.Format("{0}Baz:\t{1}{2}", base.ToString(), Baz, Environment.NewLine);
            }
        }
    
        class Program {
            static void Main(string[] args) {
                Display();
                ChangeType();
                Display();
            }
    
            static void Display() {
                using (var context = new Container())
                    Console.WriteLine(context.A.First());
                Console.ReadKey();
            }
    
            static void ChangeType()
            {
                using (var context = new Container()) {
                    context.A.Add(new Y(context.A.Remove(context.X.First())));
                    context.SaveChanges();
                }
            }
    
            class Container : DbContext {
                public IDbSet<A> A { get; set; }
                public IDbSet<X> X { get; set; }
                public IDbSet<Y> Y { get; set; }
            }
    
            static Program() {
                Database.SetInitializer<Container>(new ContainerInitializer());
            }
    
            class ContainerInitializer : DropCreateDatabaseAlways<Container> {
                protected override void Seed(Container context) {
                    context.A.Add(new X { Foo = "Base Value", Bar = "SubType X Value" });
                    context.SaveChanges();
                }
            }
        }
    }
    
    注意:如果您想要一个自动生成的自然密钥,您不能让EF要求DBMS计算它,否则EF将阻止您以您想要的方式操作它(见下文)。实际上,EF将所有具有计算值的键都视为代理键,尽管它仍然乐于泄漏它们(这两个世界都不好)

    注意:我用注释子类,因为您提到了TPT设置,但问题实际上与TPT无关

    使用代理键

    如果你认为代理键真的是内部的,那么只要你能以同样的方式访问你的数据(使用一个辅助索引),那就没关系了。 注意:在实践中,许多人到处泄漏代理密钥(域模型、服务接口等)。不要这样做

    如果使用上一个示例,只需删除子类型的复制构造函数中的
    DatabaseGenerated
    属性和
    Id
    的赋值

    注意:使用DBMS生成的值,
    Id
    属性被EF完全忽略,除了由模型生成器分析以在SQL模式中生成
    Id
    列之外,它没有任何实际用途。而且被糟糕的程序员泄露

    输出:

    Type:   EntitySubTypeChange.X
    Id:     0
    Foo:    Base Value
    Bar:    SubType X Value
    
    Type:   EntitySubTypeChange.Y
    Id:     0
    Foo:    Base Value
    Baz:
    
    Type:   EntitySubTypeChange.X
    Id:     1
    Foo:    Base Value
    Bar:    SubType X Value
    
    Type:   EntitySubTypeChange.Y
    Id:     2
    Foo:    Base Value
    Baz:
    
    使用状态模式(或类似模式)

    这个解决方案可能是大多数人所认为的“正确的解决方案”,因为你不能改变大多数面向对象语言中对象的内在类型。兼容语言(包括C#)就是这种情况

    问题在于,这种模式在域模型中得到了正确的使用,而不是像用EF实现的DAL那样。我并不是说这是不可能的,您可以使用复杂类型或TPH构造来解决问题,以避免创建中间表,但最有可能的情况是,您将一直游弋到放弃为止。希望有人能证明我错了

    注意:您可以决定希望您的关系模型看起来不同,在这种情况下,您可以完全绕过这个问题。但这并不能回答你的问题

    使用内部EF巫毒 我很快查阅了和的参考文档,但我无法立即找到任何方法来更改实体的类型。如果你比我幸运,你也许可以使用DTO并进行转换

    重要提示 在前两种解决方法中,您可能会遇到导航属性和外键方面的一系列问题(因为
    DELETE
    +
    INSERT
    操作)。这将是一个单独的问题

    结论 EF i