views:

2374

answers:

3

I am writing an application where we will need to extend a basic entity into a number of different things (eg employee, vehicle etc). The design is as such that there is a Entity table and a second table with type specific values eg an employee will have an ID Number but a vehicle will have a registration number.

I have inherited from the class entity generated in the Data Context but am having trouble with the casting in my repository. What is the correct way of doing this?

public class cAccountEmployee : cAccountEntity
{
    public string id_number
    {
        get
        {
            try
            {
                return this.cAccountEntityValues.Single(e => e.type == 1).value;
            }
            catch (Exception)
            {
                return "";
            }
        }

        set
        {
            try
            {
                this.cAccountEntityValues.Single(e => e.type == 1).value = value;
            }
            catch (Exception)
            {
                this.cAccountEntityValues.Add(new cAccountEntityValue()
                                            {
                                                accountentity_id = this.id,
                                                cAccountEntity = this,
                                                type = 1,
                                                value = value
                                            });
            }
        }
    }

}

Then in my repository (does not inherit anything)

public IEnumerable<cAccountEmployee> All(int accountholder_id)
    {
        return db.cAccountEntities.Where(e => e.accountholder_id == accountholder_id).OrderBy(a => a.name).Cast<cAccountEmployee>();
    }

    public cAccountEmployee Single(int id)
    {
        return db.cAccountEntities.Single(a => a.id == id) as cAccountEmployee;
    }

The cast fails in the single method and hence I get back null. It is my understanding you cannot define explicit or implicit operators from or to a base class? How can I get the base class Linq result to cast up to the inherited Employee class, while still maintaining its db state so I can submit changes?

+1  A: 

With LINQ-to-SQL, there are two ways inheritance can work:

  • discriminator over a single table (not suitable since your data is not homogeneous)
  • base-class / multi-table (not that this isn't supported in the dbml - only if you write the classes manually)

LINQ-to-SQL does not support multi-table inheritance (i.e. a single object with data from multiple tables). Entity Framework does, but is more complex; you use .Cast<T> and .OfType<T> in EF to cast/filter based on sub-types.

You might want to look at:

What is the purpose of the base class here? If it adds behaviour, then you can edit the dbml to specify a common base-class for all your entities. If it has data properties then it gets trickier.

Personally, I simply wouldn't do it this way... I would keep separate classes for the different types, and use the data-context correctly, using the separate tables per type:

public IEnumerable<Employee> All(int accountholder_id)
{
    return db.Employees.Where(e => e.accountholder_id == accountholder_id)
        .OrderBy(a => a.name);
}

public Employee Single(int id)
{
    return db.Employees.Single(a => a.id == id);
}

So - can you clarify what the cAccountEntity does here?

Marc Gravell
A: 

Thanks for the input, will look over some of the suggestions...

The idea behind account entity was that as of now the site only needs to handle employees but in the future they may want to add vehicles etc to the system, the system is used to assign costs to an entity, so for this purpose an employee is handled the same as a vehicle.

The idea being that an employee and a vehicle need to he handled the same for referencing in the db etc but will need slightly different info about them. It is a complex design only because they want to add extra types later but without needing to upgrade the db...

In my code however I want to talk about an employee not about a generic entity type (make controller, view etc much easier in an mvc app). As you cannot supply a user-defined casting from base to derived class I have skipped inheritting it and used the following solution instead. Bit more cumbersome but does work... Let me know if someone can see a better way of doing this.

public class cAccountEmployee
{
    private cAccountEntity entity;

    public int id
    {
        get
        {
            return this.entity.id;
        }
        set
        {
            this.entity.id = value;
        }
    }

    public string name
    {
        get
        {
            return this.entity.name;
        }
        set
        {
            this.entity.name = value;
        }
    }

    public int accountholder_id
    {
        get
        {
            return this.entity.accountholder_id;
        }
        set
        {
            this.entity.accountholder_id = value;
        }
    }

    public System.Data.Linq.EntitySet<cAccountEntityValue> cAccountEntityValues
    {
        get
        {
            return this.entity.cAccountEntityValues;
        }
    }

    public cAccountHolder cAccountHolder
    {
        get
        {
            return this.entity.cAccountHolder;
        }
    }

    public cAccountEmployee()
    {
        this.entity = new cAccountEntity();
    }

    public cAccountEmployee(cAccountEntity entity)
    {
        this.entity = entity;
    }

    public string id_number
    {
        get
        {
            try
            {
                return this.entity.cAccountEntityValues.Single(e => e.type == 1).value;
            }
            catch (Exception)
            {
                return "";
            }
        }

        set
        {
            try
            {
                this.entity.cAccountEntityValues.Single(e => e.type == 1).value = value;
            }
            catch (Exception)
            {
                this.entity.cAccountEntityValues.Add(new cAccountEntityValue()
                                            {
                                                accountentity_id = this.id,
                                                cAccountEntity = this.entity,
                                                type = 1,
                                                value = value
                                            });
            }
        }
    }
}

//In the repository
public cAccountEmployee Single(int id)
    {
        return new cAccountEmployee(db.cAccountEntities.Single(a => a.id == id));
    }
Matthew Hood
A: 

How can I get the base class Linq result to cast up to the inherited Employee class

It's not an upcast, it's a downcast.

I think you don't understand casting or possibly - instance type vs reference type.

public class Animal { }
public class Zebra : Animal { }

public class Zoo
{
    public void ShowZebraCast()
    {
        Animal a = new Animal();
        Zebra z = (Zebra)a;
    }
}

System.InvalidCastException: Unable to cast object of type 'Animal' to type 'Zebra'.

In the same way, you have an instance of Entity that you can't downcast to use an Employee reference against it.

You could convert the types, but then you have to supply a conversion method.

public partial class Animal { }
public class Zebra : Animal { }
//in another file
public partial class Animal{
  public Zebra ToZebra(){
    return new Zebra() { //set Zebra properties here.
    };
  }
}


public class Zoo
{
    public void ShowZebraConvert()
    {
        Animal a = new Animal();
        Zebra z = a.ToZebra();
    }
}
David B