Entity Framework 5 table-per-type update, change sub type but keep same base type -


i have simple hierarchy

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; } } 

i plan use table-per-type hierarchy db. 3 tables created, 1 base , 2 child using same pk base.

my problem want able update communicationsupport changing it's type. let's create telecomsupport, save , change it's type postalsupport , save again (update). result expect ef keep same base record (communicationsupport id) delete record in telecomsupport table , create new 1 in postalsupport. telecomsupport , postalsupport exclusive , cannot share same base communicationsupport.

how can using entityframework 5?

thanks help!

i don't have answer, can think of 4 "solutions" workarounds:

  1. don't use dbms-computed values primary keys (if use natural keys, it's fine).
  2. use dbms-computed surrogate keys.
  3. follow state pattern.
  4. do evil voodoo object state manager.

update: there seems popular consensus trying isn't worth it; people use stored procedures instead work around problem.

using natural keys

first, remember objects tracked ef part of dal, not domain model (regardless of whether use pocos or not). people don't need domain model, keep in mind, can think of these objects representations of table records manipulate in ways wouldn't domain objects.

here, use idbset.remove delete records of entity, add new ones same primary key using idbset.add, in single transaction. see changetype method in sample code below.

in theory, integrity ok, , in theory, ef detect you're trying , optimize things. in practice, doesn't (i profiled sql interface verify this). result looks ugly (delete+insert instead of update), if system beauty , performance issues, it's no-go. if can take it, it's relatively straightforward.

here sample code used test (if want experiment, create new console application, add reference entityframework assembly, , paste code).

a base class, x , y subclasses. consider id natural key, can copy in subclasses copy constructors (here implemented y). code creates database , seeds record of type x. then, runs , changes type y, losing x-specific data in process. copy constructor transform data, or archive if data loss not part of business process. piece of "interesting" code changetype method, rest boilerplate.

using system; using system.componentmodel.dataannotations.schema; using system.data.entity; using system.linq;  namespace entitysubtypechange {     abstract class {         [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 : {         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 : {         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> { 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();             }         }     } } 

output:

type:   entitysubtypechange.x id:     0 foo:    base value bar:    subtype x value  type:   entitysubtypechange.y id:     0 foo:    base value baz: 

note: if want auto-generated natural key, can't let ef ask dbms compute it, or ef prevent manipulating way want (see below). in effect, ef treats keys computed values surrogate keys, though still happily leaks them (the bad of both worlds).

note: annotate subclasses table because mentioned tpt setup, problem not related tpt.

using surrogate keys

if consider surrogate key internal, doesn't matter if changes under nose long can still access data same way (using secondary index example).

note: in practice, many people leak surrogate keys around (domain model, service interface, ...). don't it.

if take previous sample, remove databasegenerated attribute , assignment of id in copy constructor of subtypes.

note: value generated dbms, id property ignored ef , doesn't serve real purpose other being analyzed model builder generate id column in sql schema. , being leaked bad programmers.

output:

type:   entitysubtypechange.x id:     1 foo:    base value bar:    subtype x value  type:   entitysubtypechange.y id:     2 foo:    base value baz: 

using state pattern (or similar)

this solution people consider "proper solution", since can't change intrinsic type of object in object-oriented languages. case cts-compliant languages, includes c#.

the problem pattern used in domain model, not in dal 1 implemented ef. i'm not saying it's impossible, may able hack things complex types or tph constructs avoid creation of intermediary table, you'll swimming river until give up. can prove me wrong though.

note: can decide want relational model different, in case may bypass problem altogether. wouldn't answer question though.

using internal ef voodoo

i've rather looked around reference documentation dbcontext, objectcontext , objectstatemanager, , can't find way change type of entity. if have better luck me, may able use dtos , dbpropertyvalues conversion.

important note

with first 2 workarounds, you'll hit bunch of problems navigational properties , foreign keys (because of delete+insert operation). separate question.

conclusion

ef not flexible when non-trivial, keeps improving. answer won't relevant in future. it's possible i'm not aware of existing killer-feature make want possible, don't make decisions based on answer.


Comments

Popular posts from this blog

image - ClassNotFoundException when add a prebuilt apk into system.img in android -

I need to import mysql 5.1 to 5.5? -

Java, Hibernate, MySQL - store UTC date-time -