Search code examples
c#dry

c# apply DRY standards to multiple classes


I am working on a project at the moment with a lot of classes that inherit from a base class. This is in a test project. The base class looks like this:

public class DatabaseContextContext
{
    public DatabaseContext DatabaseContext;
    protected DatabaseContextContext()
    {
        DatabaseContext = Substitute.For<DatabaseContext>();
    }

    public static DatabaseContextContext GivenServices() => new DatabaseContextContext();

    public void WhenList<T>(List<T> response) where T: class
    {
        DatabaseContext.Set<T>().Returns(response.AsQueryable());
    }
}

And then there is another class that looks like this:

public class VenuesContext: DatabaseContextContext
{
    public IMediator Mediator;
    protected VenuesContext()
    {
        Mediator = Substitute.For<IMediator>();
    }

    public VenuesContext WhenListSucceeds(List<Venue> venues)
    {
        WhenList(venues);
        return this;
    }
}

And another like this:

public class TheatresContext: DatabaseContextContext
{
    public IMediator Mediator;
    protected TheatresContext()
    {
        Mediator = Substitute.For<IMediator>();
    }

    public TheatresContext WhenListSucceeds(List<Theatre> theatres)
    {
        WhenList(theatres);
        return this;
    }
}

As you may have guessed, this is a simple version, there are hundreds of these classes and some are much more complicated, but the idea is the same. Anyway, I want to try to remove some of the repeated code, namely:

public ListVenuesContext WhenListSucceeds(List<Venue> venues)
{
    WhenList(venues);
    return this;
}

Currently you can chain the methods together like this:

var services = VenuesContext.GivenServices().WhenListSucceeds(new List<Venue>());

and I would like to keep that functionality. I tried to do something like this:

public TReturn WhenList<T, TReturn>(List<T> response) where T: class where TReturn: class, new()
{
    DatabaseContext.Set<T>().Returns(response.AsQueryable());

    return new TReturn();
}

But I am not sure this is right because I have to call new TReturn() instead of this. Also, the chain changes to this:

var services = VenuesContext.GivenServices().WhenList<Venue, ListVenuesContext>(new List<Venue>());

Which isn't terrible, but it would be nice to not have to add <Venue, ListVenuesContext> to each call.

Can anyone think of a more elegant solution?


Update

I may have found a solution by using http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

If I change the DatabaseContextContext to this:

public class DatabaseContextContext<T> where T: DatabaseContextContext<T>
{
    public DatabaseContext DatabaseContext;
    protected DatabaseContextContext()
    {
        DatabaseContext = Substitute.For<DatabaseContext>();
    }

    public T WhenList<TM>(List<TM> response) where TM : class
    {
        DatabaseContext.Set<TM>().Returns(response.AsQueryable());

        return (T)this;
    }
}

Then I can do something like this:

public class ListVenuesContext : DatabaseContextContext<ListVenuesContext>
{
    public static ListVenuesContext GivenServices() => new ListVenuesContext();

    public ListVenuesHandler WhenCreateHandler() => new ListVenuesHandler(DatabaseContext);
}

which will allow me to invoke like this:

var services = ListVenuesContext.GivenServices().WhenList(new List<Venue>());

The only problem I can see with this, is that I can't inherit multiple times. For example:

public class ListVenuesContext : VenuesContext
{
    public static ListVenuesContext GivenServices() => new ListVenuesContext();

    public ListVenuesHandler WhenCreateHandler() => new ListVenuesHandler(DatabaseContext);
}

public class VenuesContext: DatabaseContextContext<VenuesContext>
{
    public IMediator Mediator;
    protected VenuesContext()
    {
        Mediator = Substitute.For<IMediator>();
    }

    public void WhenGet(Venue venue)
    {
        Mediator.Send(Arg.Any<GetVenue>()).Returns(Attempt<Venue>.Succeed(venue));
    }
}

Solution

  • Sorted it. I did use the solution above, but I did it like this:

    public class DatabaseContextContext<T> where T: DatabaseContextContext<T>
    {
        public DatabaseContext DatabaseContext;
        protected DatabaseContextContext()
        {
            DatabaseContext = Substitute.For<DatabaseContext>();
        }
    
        public T WhenListSucceeds<TM>(List<TM> response) where TM : class
        {
            DatabaseContext.Set<TM>().Returns(response.AsQueryable());
    
            return (T)this;
        }
    
        public T WhenGetSucceeds<TM>(TM response) where TM : class
        {
            DatabaseContext.Set<TM>().Returns(new List<TM> {response}.AsQueryable());
    
            return (T)this;
        }
    }
    

    If there was a class that others inherited from that also inherited from the DatabaseContextContext, I could do it like this:

    public class MediatorContext<T>: DatabaseContextContext<T> where T: MediatorContext<T>
    {
        public IMediator Mediator;
        protected MediatorContext()
        {
            Mediator = Substitute.For<IMediator>();
        }
    }
    

    And then the child class could inherit that, like this:

    public class DeleteVenueContext : MediatorContext<DeleteVenueContext>
    {
        public static DeleteVenueContext GivenServices() => new DeleteVenueContext();
    
        public DeleteVenueHandler WhenCreateHandler() => new DeleteVenueHandler(DatabaseContext, Mediator);
    }
    

    Which meant I could still use my chaining like this:

    var services = DeleteVenueContext.GivenServices().WhenGetSucceeds(new Venue { Id = id });