tags:

views:

128

answers:

3

I have throughout my application many methods where I load collections. They all (actually most with a couple of differentials) follow the following pattern:

public BaseCollection<ObjectType1> LoadObjectType1(EventHandler handleEvent)
{
    var myQuery = ObjectType1.Load(MyServiceContext);
    return new DataManager<ObjectType1>().GetData(myQuery , handleEvent, MyServiceContextt);
}

public BaseCollection<ObjectType2> LoadObjectType2(EventHandler handleEvent)
{
   var myQuery = ObjectType2.Load(MyServiceContext);
   return new DataManager<ObjectType2>().GetData(myQuery , handleEvent, MyServiceContextt);
}

public BaseCollection<ObjectType3> LoadObjectType3(EventHandler handleEvent)
{
   var query = ObjectType3.Load(MyServiceContext);
   return new DataManager<ObjectType3>().GetData(query, handleEvent, MyServiceContextt);
}

Where ObjectType# are my business objects, e.g. Employee, Department, etc.

I would like to convert these to harness Generics.

Any advice will be greatly appreciated.

+4  A: 

You can always create version of these functions that take a generic argument themselves. However, since there is no argument that can allow the compiler to infer the type of the generic argument, you will always have to supply it in the call.

The main issue, is that you're using a static Load method that is implemented on each object type. You would have to pass this in to the call as a delegate:

public BaseCollection<T> Load<T>(EventHandler handleEvent, Func<QueryType> querySelector) 
{ 
    var myQuery = querySelector(MyServiceContext); 
    return new DataManager<T>().GetData(myQuery , handleEvent, MyServiceContext); 
}

When calling these version, you would have to specify the type T, as well as pass in a delegate that will return the query object you use to load your data (since it's type specific):

LoadDepositionSampleTypeItemSource<Department>( handler, Department.Load );

EDIT: So, now that you've updated your question I think I understand it a bit better. There's no reason you can't collapse the different methods down to a single overload. If you can refactor the implementation so that the query is not retrieved from the object type, you may be able to improve and consolidate things further. A factory pattern may make things cleaner and more maintainable:

public BaseCollection<T> Load<T>(EventHandler handleEvent) 
{ 
    var myQuery = QueryManager.GetQuery<T>(MyServiceContext); 
    return new DataManager<T>().GetData(myQuery , handleEvent, MyServiceContext); 
}
LBushkin
Since you’re already making it generic, I don’t understand your purpose of having three identical methods with different names.
Timwi
Thanks everyone - I know I am already using some refactoring, but would like to take it further.Something like, Load<ObjectType1> and it will do all of the underlying logic like I pasted.I just feel that there is too much replication of code.
@Timwi: You're absolutely right. I didn't catch on initially that each of these methods was an example of one of the duplicative methods. Fore some reason I understood it to mean that the set of three methods *together* were getting replicated.
LBushkin
+3  A: 

Do you mean you want the methods to be generic? Something like this?

public BaseCollection<T> LoadObject<T>(EventHandler handleEvent)
{
    var myQuery = BusinessUtil.Load<T>(MyServiceContext);
    return new DataManager<T>().GetData(myQuery, handleEvent, MyServiceContext);
}

Of course, the problem with this is that you can’t easily call the separate static .Load() methods you already have. You will have to declare a single static generic .Load() method, which can return objects of any of your business types. Something like this maybe:

public static class BusinessUtil
{
    public static T Load<T>(ServiceContext context)
    {
        if (typeof(T) == typeof(Object1))
            return (T) Object1.Load(context);
        if (typeof(T) == typeof(Object2))
            return (T) Object2.Load(context);
        // ... etc.
    }
}

Alternatively, you can require an extra parameter on LoadObject<T> that specifies how to create such an object:

public BaseCollection<T> LoadObject<T>(EventHandler handleEvent,
    Func<ServiceContext, T> generator)
{
    var myQuery = generator(MyServiceContext);
    return new DataManager<T>().GetData(myQuery, handleEvent, MyServiceContext);
}

// ...

var obj = LoadObject(handleEvent, Object1.Load);

This is assuming that myQuery needs to be of type T, which unfortunately the code in your question doesn’t reveal. If it needs to be a different type, maybe some sort of Query<T>?, then you will need to change the T inside the Func<> (and the return type of BusinessUtil.Load) to that too.

Timwi
Thanks, this looks promising and will further investigate.I added this comment above:Thanks everyone - I know I am already using some refactoring, but would like to take it further.Something like, Load<ObjectType1> and it will do all of the underlying logic like I pasted.I just feel that there is too much replication of code.
A: 

You could use Reflection:

public BaseCollection<T> LoadGeneric<T>(EventHandler handleEvent)
{
    var myQuery = (YourQueryType)typeof(T)
        .GetMethod("Load")
        .Invoke(null, new object[] { MyServiceContext });
    return new DataManager<T>().GetData(myQuery , handleEvent, MyServiceContextt);
}

But I think refactoring the code (maybe using single static method as Timwi suggested) would be a better choice.

svick