简体   繁体   English

EF Code First在现有数据库上,一对多映射

[英]EF Code First on existing database, mapping one to many

I have a database generated by application which I can't modify (I can add tables, views and such but I can't modify existing tables, add columns to them). 我有一个无法修改的应用程序生成的数据库(我可以添加表,视图等,但不能修改现有表,也不能向其中添加列)。 I work on a web application which uses BreezeJS to allow the client-side part of the web app query for the data via OData protocol. 我在使用BreezeJS的Web应用程序上工作,该Web应用程序允许Web应用程序的客户端部分通过OData协议查询数据。

Measurement table has following structure: Measurement表具有以下结构:

MeasurementId INT
DeviceId INT FOREIGN KEY REFERENCES Devices (DeviceId)
Name VARCHAR,
PRIMARY KEY (MeasurementId)

What I need is to add nullable ParentId self referencing foreign key and because I can't modify existing tables, I've created new one, Measurement_Parent : 我需要添加可为空的ParentId自引用外键,并且由于无法修改现有表,因此创建了一个新的Measurement_Parent

MeasurementId INT FOREIGN KEY REFERENCES Measurements (MeasurementId),
ParentId INT FOREIGN KEY REFERENCES Measurements (MeasurementId),
PRIMARY KEY (MeasurementId)

I have following entity: 我有以下实体:

public partial class Measurement
{
    public Measurement()
    {
        this.Children = new List<Measurement>();
    }

    public Int32 MeasurementId { get; set; }

    public virtual Measurement Parent { get; set; }

    public Int32 DeviceId { get; set; }

    public virtual Device Device { get; set; }

    public String Name { get; set; }

    public virtual ICollection<Measurement> Children { get; set; }
}

Now the tricky part. 现在是棘手的部分。 I've tried many different approaches to get this working but without success. 我尝试了许多不同的方法来使此工作正常进行,但没有成功。 Current EntityTypeConfiguration for my entity looks like this: 我实体的当前EntityTypeConfiguration看起来像这样:

// Primary Key
this.HasKey(m => m.MeasurementId);

// Table & Column Mappings

this.Property(t => t.MeasurementId)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

// Table & Column Mappings
this.ToTable("Measurement");
this.Property(m => m.MeasurementId);
this.Property(m => m.DeviceId);
this.Property(m => m.Name);

// Relationships

// Each measurement references device performing the measurement.
this.HasRequired(d => d.Device)
    .WithMany(m => m.Measurements)
    .HasForeignKey(d => d.DeviceId);

// Each measurement can have optional parent.
this.HasOptional(measurement => measurement.Parent)
    .WithMany() // .WithMany(measurement => measurement.Children) ??
    .Map(m =>
    {
        m.MapKey("ParentId");
        m.ToTable("Measurement_Parent");
    });

Unfortunately this gives me weird error while loading my app: 不幸的是,这在加载我的应用程序时给了我一个奇怪的错误:

Metadata query failed for: api/EDW/Metadata; The specified table 'Measurement_Parent' was not found in the model. Ensure that the table name has been correctly specified.

I have no idea why is this happening because the table is there. 我不知道为什么会这样,因为桌子那里。 I tried mapping these two tables onto one entity (table splitting), but because the ParentId can be NULL and EF generated INNER JOIN instead of LEFT OUTER JOIN for this mapping, it didn't work because some rows in Measurement table were ommited as they didn't have any corresponding rows in Measurement_Parent . 我尝试将这两个表映射到一个实体上(表拆分),但是由于ParentId可以为NULL并且EF生成了INNER JOIN而不是LEFT OUTER JOIN来进行此映射,所以该表不起作用,因为Measurement表中的某些行被省略了在Measurement_Parent没有任何对应的行。

Basically what I need is to have optional Parent property with reference to parent measurement and list of Children measurements. 基本上我需要的是有可选的Parent参考父测量和列表属性Children测量。

What you need is entity splitting - splitting a single entity among two or more tables. 您需要的是实体拆分-在两个或多个表之间拆分单个实体。 This implicitly involves shared primary key - in this case, the shared key in the relationship table will be the child entities' ID. 这隐式涉及共享主键-在这种情况下,关系表中的共享键将是子实体的ID。 You do this by calling multiple Map methods, each with a call to EntityMappingConfiguration.Properties to define which properties should be included in that mapping fragment and a call to ToTable to set the table name. 为此,您可以调用多个Map方法,每个方法都调用EntityMappingConfiguration.Properties来定义应在该映射片段中包括哪些属性,并调用ToTable来设置表名称。

modelBuilder.Entity<Measurement>()
    .HasKey( ke => ke.MeasurementId )
    .Map( emc =>
    {
        emc.Properties( pe => new { pe.MeasurementId, pe.Name, pe.DeviceId } );
        emc.ToTable( "Measurement" );
    } )
    .Map( emc =>
    {
        emc.Properties( pe => new { pe.MeasurementId, pe.ParentId } );
        // maybe name this MeasurementExtension?  This table could
        //  be used for any properties you wish to add to the Measurement entity
        emc.ToTable( "Measurement_Parent" );

        // for this example's clarity I've named the PK Child_MeasurementId
        //  but in real-world use I would name it MeasurementId
        emc.Property( pe => pe.MeasurementId ).HasColumnName( "Child_MeasurementId" );
        emc.Property( pe => pe.ParentId ).HasColumnName( "Parent_MeasurementId" );
    } );

modelBuilder.Entity<Measurement>()
    .HasOptional( npe => npe.Parent )
    .WithMany( npe => npe.Children )
    .HasForeignKey( fke => fke.ParentId );

Here's the result in the DB (note I did not set up a FK/nav prop for Device but you know how to do that): 这是数据库中的结果(请注意,我没有为Device设置FK / nav道具,但您知道该怎么做):

实体分割结果

Ideally, the Parent_MeasurementId field would be not null and the record would be deleted instead of setting that column to null if their is no parent, but that doesn't seem possible with entity splitting. 理想情况下, Parent_MeasurementId字段将not null并且如果记录不是父级,则记录将被删除,而不是将该列设置为null,但这对于实体拆分来说似乎是不可能的。 In any case, this does exactly what you're looking for - extending an entity without modifying the initial underlying table. 无论如何,这完全可以满足您的需求-扩展实体而无需修改初始基础表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM