简体   繁体   中英

C# Entity Framework Adding New Column to Entity that's from external library

On a project I am working on I am having some troubles trying to add properties to an entity from an external NuGet package.

The external team originally used the EntityFramework to create their database, and awhile back my team used it initially in order to create ours, now having two separate databases but initial creation used the common NuGet package.

On the external team's side, they haven't changed the table at at all, but on our side we've added new columns and properties to our database and now we need it within our DBContext. How do I map these new fields to an Entity so that I can access and set the properties. I hoped it was protected but since it is public I can't just overwrite the DbSet<Profile> Profile call.

External Package:

  • DataContext (Class that extends DBContext and has a public DbSet<Profile> Profile {get;set;} )
  • Profile (Entity that is mapped to the "Profile" table in the database)

Since I can't modify the Profile class, how do I go about adding new columns that are there in the table?

My initial approach was to create:

  • DataContextExt (class that extends DataContext and added public DbSet<ProfileExt> ProfileExt {get;set;}
  • ProfileExt (Entity that extends Profile and has the additional fields that aren't part of the original

This seems to get me the furthest, but since ProfileExt extends Profile, I get an error when using it due to the "Discriminator" column since they are both the same entity technically.

I then tried to remove Profile by overriding the OnModelCreating() and map my ProfileExt to Profile but that failed as well, it didn't seem to change the model builder at all.

public class DataContextExt : DataContext
{

    public DbSet<ProfileExt> ProfileExt { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Ignore<Profile>();
        modelBuilder.Entity<ProfileExt>().ToTable("Profile");
        Database.SetInitializer<ApplicationDbContext>(null);
    }
}

Does anyone have any suggestions on what I should try next going forward?

EDIT: Currently the project is design to access the information VIA a Stored Procedure and then I mapped that to my ProfileExt, but when it comes to saving it is designed to use

Entity = await DB.Set<TModel>().FindAsync(Key.Compile()(Model)).ConfigureAwait(false);

Model is instance of ProfileExt when it reaches this point

If I try to pass a ProfileExt through(without its own DbSet) as Profile it fails saying ProfileExt is not in the context, and if I do register it (with its own DbSet) it throws the Discriminator error since once is an instance of another.

From the sounds of things you are using a library and initial schema provided by some third party. You don't share code modifications with that team but you've gone and changed part of their schema in your copy of this Profile table.

Why not keep the two DbContexts completely separate rather than trying to inherit to override?

One option would be not to add columns to a table/entity that you do not have ownership of to extend. Move your custom columns to something like a MasterProfile table which shares a ProfileId as it's PK. From there you can declare a MasterProfile entity with a one-to-one relationship with Profile.

public class MasterProfile
{
    [Key]
    public int ProfileId { get; set; }
    // add custom columns here...

    public virtual Profile Profile { get; set; }
}

then configure relationship:

modelBuilder.Entity<MasterProfile>()
    .HasRquired(x => x.Profile)
    .WithOptional();

This way you can read your custom object in your DbContext along with Profile without a breaking change to the schema.

Another option you could explore is to define your own Profile entity definition for your DbContext while reusing the other entity declarations from the 3rd party.

For example: Given a 3rd party library which defines the following classes:

  • 3rdParty.User
  • 3rdParty.Profile
  • 3rdParty.TableA
  • 3rdParty.TableB
  • 3rdParty.TableC

and they are accessed by a 3rdParty.DataContext

I can define a MyApp.DataContext that does not need to extend 3rdParty.DataContext. That DbContext can reference a Profiles collection that is declared as:

  • MyApp.Profile

which contains the full set properties from our Profile table. Provided you don't have to worry about references to 3rdParty.Profile you don't need to create custom entities for every table, you can reference 3rdParty.TableA etc. in your MyApp.DataContext.

Ie

public class DataContext : DbContext
{
    public DbSet<Profile> Profiles { get; set; }
    public DbSet<3rdParty.TableA> TableAs { get; set; }
    public DbSet<3rdParty.TableB> TableBs { get; set; }
    public DbSet<3rdParty.TableC> TableCs { get; set; }
}

The catch would be that this will only work if the class you define is not referenced by many other entities. Every 3rdParty entity definition we include in our DbContext can no longer reference a 3rdParty.Profile since our DbContext cannot have two entities mapped to the same table.

For instance, if Profile references a User, that isn't a problem since MyApp.Profile can reference 3rdParty.User, however if 3rdParty.User has a reference back like:

public virtual ICollection<Profiles> { get; set; } 

which will be pointing back to the Profile in the 3rdParty assembly, this is a deal breaker. We will need to recreate a MyApp.User as well. This could cascade if something like User needs to be re-declared and that class is referenced by the majority of other entities. (Ie public virtual User CreatedBy { get; set; } )

It may be an option worth exploring.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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