views:

153

answers:

1

I’m fairly new to IoC and perhaps my understanding of generics and inheritance is not strong enough for what I’m trying to do. You might find this to be a mess. I have a generic Repository<TEntity> base class:

public class Repository<TEntity> where TEntity : class, IEntity
   {
      private Table<TEntity> EntityTable;
      private string _connectionString;
      private string _userName;
      public string UserName
      {
         get { return _userName; }
         set { _userName = value; }
      }

      public Repository() {}

      public Repository(string connectionString)
      {
         _connectionString = connectionString;
         EntityTable = (new DataContext(connectionString)).GetTable<TEntity>();
      }

      public Repository(string connectionString, string userName)
      {
         _connectionString = connectionString;
         _userName = userName;
         EntityTable = (new DataContext(connectionString)).GetTable<TEntity>();
      }   
// Data access methods ...
 ... }

and a SqlClientRepository that inherits Repository:

    public class SqlClientRepository : Repository<Client> 
    {     
private Table<Client> ClientTable;
          private string _connectionString;
          private string _userName;

          public SqlClientRepository() {}

          public SqlClientRepository(string connectionString) : base(connectionString)
          {
             _connectionString = connectionString;
             ClientTable = (new DataContext(connectionString)).GetTable<Client>();
          }

          public SqlClientRepository(string connectionString, string userName)
             : base(connectionString, userName)
          {
             _connectionString = connectionString;
             _userName = userName;
             ClientTable = (new DataContext(connectionString)).GetTable<Client>();
          }
    // data access methods unique to Client repository
     ... }

The Repository class provides some generics methods like Save<TEntity>, Delete<TEntity>, etc, that I want all my repository derived classes to share.

The TEntity parameter is constrained to the IEntity interface:

   public interface IEntity
   {
      int Id { get; set; }
      NameValueCollection GetSaveRuleViolations();
      NameValueCollection GetDeleteRuleViolations();
   }

This allows the Repository class to reference these methods within its Save and Delete methods. Unit tests work fine on mock SqlClientRepository instances as well as live unit tests on the real database. However, in the MVC context:

   public class ClientController : Controller
   {
      private SqlClientRepository _clientRepository;

      public ClientController(SqlClientRepository clientRepository)
      {
         this._clientRepository = clientRepository;
      }
      public ClientController() { }
// ViewResult methods ...
... }

... _clientRepository is always null. I’m using Windor Castle as an IoC container. Here is the configuration:

<component id="ClientRepository" service="DomainModel.Concrete.Repository`1[[DomainModel.Entities.Client, DomainModel]], DomainModel"
       type="DomainModel.Concrete.SqlClientRepository, DomainModel" lifestyle="PerWebRequest">
      <parameters>
        <connectionString>#{myConnStr}</connectionString>
      </parameters>
    </component>

I’ve tried many variations in the Windsor configuration file. I suspect it’s more of a design flaw in the above code. As I'm looking over my code, it occurs to me that when registering components with an IoC container, perhaps service must always be an interface. Could this be it? Does anybody have a suggestion? Thanks in advance.

---- AMENDMENT ----

In response to Answer 1, I’ve appended a new code sample, since it won’t format properly in the comment sections below.

I can get this to work:

public class ClientController : Controller
   {
      private IClientRepository _clientRepository;
      public ClientController(IClientRepository clientRepository) { ... }
   }

public interface IClientRepository : IRepository<Client>   { ... } 

public class SqlClientRepository : IClientRepository    { ... }

... but now I’m required to duplicate my Save and Delete methods inside of SqlClientRepository and the benefits of my generic Repository class are lost. Once I try and have SqlClientRepository inherit from Repository<Client> again, like this:

public class SqlClientRepository : Repository<Client>, IClientRepository   { ... }

... my null value for _clientRepository in the controller returns. Is there a way I can do this or is it not possible? I feel like I’ve tried many variations and can’t get it right.

Thanks again for your help.

A: 

No, service does not have to be an interface, although usually it should. Service is what a component exposes to an outside world (see the doco, let me know if it makes sense).

So, constructor of your ClientController says "I depend on SqlClientRepository service" That's bad (because services should be abstract (preferably interfaces)), but that's besides the point.

The component you're registering ( named ClientRepository), you're registering as Repository<Client> service.

Hopefully by now you're seeing what the problem is. ClientComponent expects SqlClientRepository, but the component you have registered does not expose itself as SqlClientRepository, but as Repository<Client>, hence Windsor thinks: "Ok, since I have no service for SqlClientRepository I'm gonna use the other constructor, to create ClientController" (see the doco for explanation on how Windsor picks which constructor to use)

Krzysztof Koźmic
Ok, I’ve returned to what worked before:class ClientController : Controller { private IClientRepository _clientRepository; public ClientController(IClientRepository clientRepository) {...} }interface IClientRepository : IRepository<Client> {...} class SqlClientRepository : IClientRepository {...}... but now I’m required to duplicate my Save, Delete methods in SqlClientRepository. The benefits of my generic Repository class are lost. This still gives _clientRepository a null value:class SqlClientRepository : Repository<Client>, IClientRepository {...}Is it possible? Thanks.
Robin
can you update the question? The code in the comment is unreadable
Krzysztof Koźmic
Ok, I've updated my original post above.
Robin