views:

100

answers:

6

I am going to have multiple "types" of an object and I am really not sure how best to retrieve/save those multiple types without having a separate save/retrieve for each type.

My classes:

public class Evaluation {
  public int Id
  public string Comment
}

public class EvaluationType_1 : Evaluation {
  public string field
}

public class EvaluationType_1 : Evaluation {
  public string field
}

What I would like to do in my repository:

public interface IEvaluationRepository {
  public Evaluation getEvaluation(int id);
  public SaveEvaluation(Evaluation);
}

Inside the get/save methods:

// Save/get common fields
Id
Comments

// Get child type, perform switch
Type childType = ???
switch(childType) {
  // Set child-specific fields
}

I hope this makes sense. I'd rather not add a "type" column as I have this in another part of my database and I am not really liking it too much

UPDATE

Great comments, everyone - many thanks. Here's more info/questions for clarification, if necessary.

I love the idea of using interfaces and generics, I'm really at a loss for how to incorporate them into my repository pattern.

When I call getEvaluation, I want it to return an abstract Evaluation, but I'm struggling with this code. Same with Saving - any insight on this would be excellent - thanks again!

UPDATE 2

I hate to keep refining this, but Daniel is helping me hone in on what exactly I am trying to ask :P.

Database: Evaluations Id (PK) Comment

EvaluationType1
  Id (FK to Evaluations.Id)
  Field

EvaluationType1
  Id (FK to Evaluations.Id)
  Field

So, in getEvaluation(int id), I need to figure out what type of Evaluation they want. Does this mean I should pass in a type? Same is true in saveEvaluation, But I can do a switch/function map to see what Type it is.

+1  A: 

Your question is unclear, still from what I understood, you're looking for the type of the object. Here's how you do it.

EvaluationType_1 objOfEvalType1 = new EvaluationType_1();
Type childType = objOfEvalType1.GetType();

If you need to the type of child-class in the base/parent class, update your Evaluation class as below.

public class Evaluation {
  public int Id;
  public string Comment;

  //call this.GetType() anywhere you wish to get the type of the object.
  public Type MyType = this.GetType();
}
this. __curious_geek
I wasn't exactly sure what I was asking, really (if that makes sense). Please see my update; I hope it is more clear :)
Dan
A: 

Object.GetType returns the exact runtime type of the current instance - it does not consider the declaring type of the associated variable:

Type type = evaulation.GetType();

// Note that you can't switch on types

if (type == typeof(DerivedEvaluation1)) {
    // Perform custom operations        
}
else if (type == typeof(DerivedEvaluation2)) {
    // Perform custom operations
}

// Etc.
Jeff Sternal
This dispatching logic is exactly what virtual method calls do - I would rather suggest to use virtual methods instead of emulating a virtual dispatch.
Daniel Brückner
@Daniel - if he were using the active record pattern, I'd agree. But that doesn't seem to be the case here: the persistence logic lives in repository classes distinct from the `Evaluation` inheritance hierarchy. Of course, the repositories could have their own hierarchy ... but that requires moving the dispatch logic into a factory or making the domain objects factories. I think that's beyond the scope of the question.
Jeff Sternal
A: 

I interpret your question as inquiring about a convenient way of dispatching custom save logic without doing "messy" switching on the type of the child object. If all you want is an easy way to gt the type a parameter you can use the is keyword.

There are a couple of ways of dispatching logic based on runtime type.

One is that you can always create a dispatch dictionary of Save functions for each specific type:

private static readonly Dictionary<Type,Action<Evaluation>> s_SaveFunctions =
    new Dictionary<Type,Action<Evaluation>>();

s_SaveFunctions[typeof(ChildA)] = SaveChildA;
s_SaveFunctions[typeof(ChildB)] = SaveChildB;
// .. and so on.

public SaveEvaluation( Evaluation eval )
{
   // .. common save code ...

   // cal the appropriately typed save logic...
   s_SaveFunctions[eval.GetType()]( eval );
}

private static void SaveChildA( Evaluation eval ) { ... }

private static void SaveChildB( Evaluation eval ) { ... }

In .NET 4, you could use dynamic to achieve a cleaner version of the same:

public SaveEvaluation( Evaluation eval )
{
   // .. common save logic ..

   dynamic evalDyn = eval;

   SaveChild( evalDyn );
}

private void SaveChild( ChildA eval ) { ... }

private void SaveChild( ChildB eval ) { ... }

Note how the SaveChild methods all have the same name, but are just overloaded by the type of their argument. The dynamic parameter used in the SaveEvaluation method will be evaluated at runtime, and dispatched to the appropriate overload.

LBushkin
+4  A: 

Try this

public interface ISaveable {
   void SaveFields();
}

public abstract class Evaluation : ISaveable {
  public int Id
  public string Comment

  public virtual void SaveFields() {
     //Save ID and Comments
  }
}

public class EvaluationType_1 : Evaluation {
    public string field1

  public override void SaveFields() {
     //Save field1
     base.SaveFields();
  }

}

public class EvaluationType_2 : Evaluation {
   public string field2

  public override void SaveFields() {
     //Save field2
     base.SaveFields();
  }

}

Then you can have a collection of ISaveable such as List<ISaveable> and call SaveFields on each one, regardless of their type. You are now programming against the interface rather than against concrete types. The first step towards decoupling of code.

Edited: In response to your comment In your repository, you would no longer program against the Evaluation class. Instead you would program agains t the methods in the interface that it implements:

Instead of:

public interface IEvaluationRepository {
  public Evaluation getEvaluation(int id);
  public SaveEvaluation(Evaluation);
}

You might have:

 public interface ISaveableRepository {
   public ISaveable getSavable(int id);
   public Save(ISaveable saveable);
 }

And an implementation of the repository might be like:

 public class SaveableEvaluationRepository : ISaveableRepository {
   public ISaveable getSavable(int id) {
       //Add your logic here to retrieve your evaluations, although I think that 
       //this logic would belong elsewhere, rather than the saveable interface.
   }

   public Save(ISaveable saveable) {
       saveable.SaveFields();
   }
 }
Daniel Dyson
This looks great, Daniel. My only concern is how I'd use `saveFields` in conjunction with a `Save` method inside my repository - see my updates +1
Dan
Hey Dan. I have edited my post. I am not 100% happy with it, but it should get you thinking about how to program against interfaces rather than concrete types.
Daniel Dyson
One other thing that you might want to think about is using a manager class to handle the loading and saving of your entities rather than embedding the logic into the entities themselves.
Daniel Dyson
Good stuff - Thanks for the edit. I updated again to see if you can give me an elegant way of getting an evaluation
Dan
A: 

You could just make your methods virtual - the call will be dispatched to the correct method based on the actual runtime type.

public class Evaluation
{
    public Int32 Id { get; set; }
    public String Comment { get; set; }

    public virtual void Save()
    {
        // Save the common information.
        this.SaveToDatabase(this.Id);
        this.SaveToDatabase(this.Comment);
    }

    private void SaveToDatabase(Object value)
    {
        // Left as an exercise for the reader... :D
    }
}

public class EvaluationType1 : Evaluation
{
    public String Foo { get; set; }

    public override void Save()
    {
        // Save the common information.
        base.Save();

        // Save the specific information here.
        this.SaveToDatabase(this.Foo);
    }
}


public class EvaluationType2 : Evaluation
{
    public String Bar { get; set; }

    public override void Save()
    {
        // Save the common information.
        base.Save();

        // Save the specific information here.
        this.SaveToDatabase(this.Bar);
    }
}

Maybe you can also make the base class abstract. Further you should usually avoid making fields public accessible - this may turn maintaining the code into a nightmare and therefore I use properties in my example.

Daniel Brückner
+1  A: 

This sounds like an very good candidate for generics, and a lot of repository and ORM frameworks use them.

public interface IEvaluationRepository<TEvaluation> 
{ 
  public TEvaluation getEvaluation(int id); 
  public SaveEvaluation(TEvaluation evaluation); 
} 

You might also want an EvaluationBase class to handle common functions, and constraint your interface to take only EvaluationBase classes:

public interface IEvaluationRepository<TEvaluation> where TEvaluation : EvaluationBase
...
public class SomeEvaluation : EvaluationBase
{
}

It would save most or all of the problems of recognizing and tracking object types.

Cylon Cat