Search code examples
c#oopdesign-patternsdependency-injectionsimple-injector

Is it a good practice to cast from an interface to some concrete class when needed?


I'am developing a small system and i developed the classic generic repository. For now, i have the following architecture for my DAL.

public interface IRepositorio<T> where T : class
{
    T Get(long id);
    long Insert(T obj);
    bool Update(T obj);
    bool Delete(T obj);
}

public abstract class Repositorio<T> : IRepositorio<T> where T : class
{
    public IDbConnection Connection
    {
        get
        {
            return new SqlConnection(ConfigurationManager.ConnectionStrings["DBFila"].ConnectionString);
        }
    }

    public T Get(long id)
    {
        //...
    }

    public long Insert(T obj)
    {
        //...
    }

    public bool Update(T obj)
    {
        //...
    }

    public bool Delete(T obj)
    {
        //...
    }
}

My concrete repository looks like this:

public class FilaRepositorio : Repositorio<FilaRepositorio>
{
    public FilaRepositorio() 
    {
    }

    public void SomeCustomMethod()
    {
        // Some custom method
    }
}

I am also using Simple Injector to follow the IoC and DI patterns, for this reason, when i try to call "SomeCustomMethod()" i dont have access to it (obviously). Look:

public class Processador
{
    private IRepositorio<FilaModel> _repoFila;
    public Processador(IRepositorio<FilaModel> repoFila)
    {
        _repoFila = repoFila;
    }

    public void Processar()
    {
        _repoFila.SomeCustomMethod(); // <-- wrong

        ((FilaRepositorio)_repoFila).SomeCustomMethod();// <-- works
    }
}

Given this i have some questions:

  • Is a good or acceptable practice to make that cast (FilaRepositorio)?
  • If its not a good practice, how to write good code for this case?

Solution

  • There are a few options available. The main problem with making the cast is that it is an implementation concern.

    What would happen if the injected object was not a FilaRepositorio?

    By making the cast you are tightly coupling the class to an implementation concern that is not guaranteed to be the inject dependency. Thus the constructor is not being entirely truthful about what it needs to perform its function.

    This demonstrates the need to practice Explicit Dependencies Principle

    The Explicit Dependencies Principle states:

    Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.

    One way to avoid it would be to make a derived interface that explicitly exposes the desired functionality of its dependents.

    public interface IFilaRepositorio : IRepositorio<FilaModel> {
        void SomeCustomMethod();
    }
    
    public class FilaRepositorio : Repositorio<FilaModel>, IFilaRepositorio {
        public void SomeCustomMethod() {
            //...other code removed for brevity.
        }
    }
    

    and have the Processador depend on that more targeted abstraction.

    Now there is no need for the cast at all and the class explicitly expresses what it needs.

    public class Processador {
        private readonly IFilaRepositorio _repoFila;
    
        public Processador(IFilaRepositorio  repoFila) {
            _repoFila = repoFila;
        }
    
        public void Processar() {
            _repoFila.SomeCustomMethod(); // <-- works
        }
    }