3

I have a project where I've defined in EF an Employer as a derived class of User. In my process I create a user without knowing whether it will eventually be an employer (or other kinds of users) and later I need to convert it. At first I tried (Intellisense indicated an explicit conversion exists):

Employer e = (Employer) GetUser();

but at runtime I got:

Unable to cast object of type 'System.Data.Entity.DynamicProxies.User_7B...0D' to type 'Employer'.

so I tried to write a converter:

public partial class User
{
    public static explicit operator Employer(User u)
    {

but I get the error:

Error   21  'User.explicit operator Employer(User)': user-defined
conversions to or from a derived class are not allowed
C:\Users\..\Documents\Visual Studio 2010\Projects\..\Website\Models\EF.Custom.cs

fine. I then overloaded the constructor for Employer like this:

public partial class Employer
{
    public Employer(User u)
    {
        this.Id = u.Id;
        this.Claims = u.Claims;
        // etc.
    }
}

and figured I could then just do:

Employer e = new Employer(GetUser());

but when I run it I get the error:

System.InvalidOperationException was unhandled by user code
  Message=Conflicting changes to the role 'User' of the
  relationship 'EF.ClaimUser' have been detected.
  Source=System.Data.Entity
  StackTrace:
       [...]
       at Controllers.AuthController.Register(String Company, String GivenName, 
       String Surname, String Title, String Department) in C:\Users\..\Documents\
       Visual Studio 2010\Projects\..\Website\Controllers\AuthController.cs:line

as a last resort I tried writing this:

        Employer e = Auth.Claims("id")
            .Where(x => x.Value == Auth.NameIdentifier())
            .Select(x => x.User)
            .Cast<Employer>()
            .Single();

... GetUser() returns an object of type User which does not offer the .Cast<> so I used a direct query to get there... but I still get the casting of dynamic proxy objects exception.

so my question is: how can I downcast when the object has persistence through EF?

ekkis
  • 9,804
  • 13
  • 55
  • 105
  • is `Employer` part of your EF model? if you have correctly mapped inheritance as a `TPH` or `TPC` then EF will instantiate correct sub class proxy – Eranga Sep 01 '11 at 06:56
  • @Eranga, yes `Employer` is part of the EF model. And I'm using TPT so I have a table called `Users` and one called `Users_Employer` – ekkis Sep 01 '11 at 07:14

2 Answers2

7

It is not possible. You must always use final type. Once you create it as a User, EF will never allow you changing it to a derived entity type.

Btw. it is also not possible with object oriented approach as well. You cannot cast instance of parent class to the instance of derived class (unless it really is instance of derived class) - it will throw exception at runtime. Very simple example to reproduce the issue:

class X { } 

class Y : X { }

class Program 
{
    static void Main(string[] args) 
    {
        X x1 = new Y();
        Y y1 = (Y)x1;   // Works

        X x2 = new X();
        Y y2 = (Y)x2;   // InvalidCastException
    }
}

The only way to do that is overriding conversion operator which will internally create a new instance of derived class and copy all fields from the old parent instance to that new derived one.

Exactly the same approach is needed with entity framework. If you started with User entity and now you want to promote it to Employer entity you must delete old user and create new Employer.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • but I did (try) to overwrite the conversion operator (see errors above); failing that I tried just creating a new object with the copy you mentioned but ran into problems as well (as described above). Are you suggesting that I need to do something like: `User u = db.Users.Find(...); Employer e = new Employer(u); db.Users.Remove(u); db.Employers.Add(e); db.SaveChanges()` ? – ekkis Sep 01 '11 at 08:00
  • thank you Ladislav. it seems excessively punitive an approach if you consider the need for a deep copy on an object that may have all kinds of foreign references... having to grab everything from the database, delete everything and recreate it, when really, all I need is to make a couple of entries to my Users_Employer table, but your solution works. of course, now I'm having a different issue... see: http://stackoverflow.com/questions/7276507/serializable-classes-and-dynamic-proxies-in-ef-how – ekkis Sep 01 '11 at 21:03
  • after much struggle I got the deep copy to work; sadly, only to find myself in the same place again where I can't add the object because EF thinks I've fiddled something under its nose – ekkis Sep 01 '11 at 22:51
  • I have found this: http://stackoverflow.com/questions/5381690/options-for-class-design-using-safe-downcasting which is for C++ but I'm going to test in C#... it's hard for me to believe this very common use-case is so difficult to implement! – ekkis Sep 06 '11 at 23:12
0

Supose that your Employer entity has only nullable properties then it is possible to go to the table at the database and change the Discriminator from User to Employer. All relationships will be kept. And also it is possible to do the opposite.

Adriano
  • 1
  • 1