Search code examples
c#oopgenericsopen-closed-principle

Call DAL Method Based on Type


I'm working on an app where I need to call one of two data methods based on the generic type of the calling class. For example, if T is of type Foo, I'll call data.GetFoo():

private static List<T> GetObjectList(DateTime mostRecentProcessedReadTime)
{
    using (MesReportingDal data = new MesReportingDal())
    {
        return data.GetFoo(mostRecentProcessedReadTime);  // Notice GetFoo()
    }
}

And if T is of type Bar, I'll call data.GetBar():

private static List<T> GetObjectList(DateTime mostRecentProcessedReadTime)
{
    using (MesReportingDal data = new MesReportingDal())
    {
        return data.GetBar(mostRecentProcessedReadTime);  // Notice GetBar()
    }
}

Before now, I only needed one DAL method because all types were retrieved the same way. I now need to call one of two methods, depending on the type of T.

I'm trying to avoid something like this:

private static List<T> GetObjectList(DateTime mostRecentProcessedReadTime)
{
    using (MesReportingDal data = new MesReportingDal())
    {
        if (T is Foo) { return data.GetFoo(mostRecentProcessedReadTime); }
        if (T is Bar) { return data.GetBar(mostRecentProcessedReadTime); }
    }
}

This violates OCP. Is there an elegant way to handle this, so I can get rid of my if statement?

Edit - This is what the types look like

public partial class Foo1 : IDataEntity { }
public partial class Foo2 : IDataEntity { }
public partial class Bar1 : IDataEntity { }
public partial class Bar2 : IDataEntity { }

These Foos and Bars are the DBML items used with Linq-to-SQL.


Solution

  • I would change GetFoo and GetBar to just be Get, and make MesReportingDal a generic too.

    So I think you would end up with something like this:

    private static List<T> GetObjectList(DateTime mostRecentProcessedReadTime)
    {
        using (var data = new MesReportingDal<T>())
        {
            return data.Get(mostRecentProcessedReadTime);        
        }
    }
    

    Incidentally, having the using statement also requires that the MesReportingDal implements IDisposable, otherwise you'll get the following compile error:

    'MesReportingDal': type used in a using statement must be implicitly convertible to 'System.IDisposable'

    UPDATE

    So after thinking about this some more and reading your feedback, one option you have is to extract a repository interface and push the creation back to a factory method. This will allow you to maintain the single data.Get(...) call, but with different implementations based on T

    public interface IRepository<T> : IDisposable
    {
        IList<T> Get(DateTime mostRecentRead);
    }
    
    public class FooRepo : IRepository<Foo>
    {
        public IList<Foo> Get(DateTime mostRecentRead)
        {
            // Foo Implementation
        }
    }
    
    public class BarRepo : IRepository<Bar>
    {
        public IList<Bar> Get(DateTime mostRecentRead)
        {
            // Bar Implemenation
        }
    }
    

    Your factory could then look something like this

    public class RepositoryFactory
    {
        public static IRepository<T> CreateRepository<T>()
        {
            IRepository<T> repo = null;
            Type forType = typeof(T);
    
            if (forType == typeof(Foo))
            {
                repo = new FooRepo() as IRepository<T>;
            }
            else if (forType == typeof(Bar))
            {
                repo = new BarRepo() as IRepository<T>;
            }
    
            return repo;
        }
    }
    

    And this would allow you to keep your initial code block clean

    private static IList<T> GetObjectList(DateTime mostRecentProcessedReadTime)
    {
        using (var data = RepositoryFactory.CreateRepository<T>())
        {
            return data.Get(mostRecentProcessedReadTime);
        }
    }
    

    Hope that helps.