0

I'm registering some concrete types using the Autofac Keyed options. This all appeared to be working fine until I added a generic parameter to the types and interface.

My registration looks like this:

builder.RegisterType<Customer.DataAccess.Database.StoreElasticConnectionManagerByCompany>();
builder.RegisterType<Customer.DataAccess.Database.StoreElasticConnectionManagerByStore>();
builder.RegisterType<Customer.DataAccess.Database.StoreElasticConnectionManager>();
builder.RegisterType<Customer.DataAccess.Database.DatabaseManagement>().As<IDatabaseManagement>();

builder.RegisterType<ConnectionManagerFactory<Customer.DataAccess.Database.StoreElasticConnectionManagerByStore>>()
    .Keyed<IConnectionManagerFactory<IConnectionManager>>(DatabaseConnectionShardingTypes.StoreDatabaseByStoreId);
builder.RegisterType<ConnectionManagerFactory<Customer.DataAccess.Database.StoreElasticConnectionManagerByCompany>>()
    .Keyed<IConnectionManagerFactory<IConnectionManager>>(DatabaseConnectionShardingTypes.StoreDatabaseByCompanyId);

builder.RegisterType<DatabaseManagement>().As<IDatabaseManagement>().SingleInstance();

The concrete types and interfaces are implemented like so:

/*The factories*/
public interface IConnectionManagerFactory<T>
        where T : IConnectionManager 
{}

public class ConnectionManagerFactory<T> : IConnectionManagerFactory<T>
        where T : IConnectionManager
{}
/*The Connection Manager*/
public interface IConnectionManager
{}
public abstract class StoreElasticConnectionManager : IConnectionManager
{}
public class StoreElasticConnectionManagerByStore : StoreElasticConnectionManager
{}

When I'm trying to run my code (when the registration is executing) I'm receiving the following error:

An unhandled exception of type 'System.ArgumentException' occurred in Autofac.dll

Additional information: The type 'Customer.DataAccess.Database.ConnectionManagerFactory1[Customer.DataAccess.Database.StoreElasticConnectionManagerByStore]' is not assignable to service 'StoreDatabaseByStoreId (Customer.DataAccess.Database.IConnectionManagerFactory1[[Customer.DataAccess.Database.IConnectionManager, Customer.DataAccess.Database, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]])'.

This is probably due to the fact I've added the generic parameter to the ConnectionManagerFactory, because it worked before I did this.

I've tried adding the following:

builder.RegisterGeneric(typeof(Customer.DataAccess.Database.ConnectionManagerFactory<>))
                                                    .As(typeof(IConnectionManagerFactory<>));

But this didn't appear to solve the issue.

Any advice on how to proceed?

edit

In response to nemesv's question, this is the code which works and where I'm not using generics

builder.RegisterType<Customer.DataAccess.Database.StoreElasticConnectionManagerByCompany>();
                builder.RegisterType<Customer.DataAccess.Database.StoreElasticConnectionManagerByStore>();
                builder.RegisterType<Customer.DataAccess.Database.StoreElasticConnectionManager>();

                builder.RegisterType<ConnectionManagerFactory<Customer.DataAccess.Database.StoreElasticConnectionManagerByStore>>()
                                .Keyed<IConnectionManagerFactory>(DatabaseConnectionShardingTypes.StoreDatabaseByStoreId);
                builder.RegisterType<ConnectionManagerFactory<Customer.DataAccess.Database.StoreElasticConnectionManagerByCompany>>()
                                .Keyed<IConnectionManagerFactory>(DatabaseConnectionShardingTypes.StoreDatabaseByCompanyId);

As you can see, the interface IConnectionManagerFactory isn't generic anymore.

The concrete ConnectionManagerFactory now looks like this:

public class ConnectionManagerFactory<T> : IConnectionManagerFactory
        where T : IConnectionManager

The DatabaseConnectionShardingTypes is a simple enum.

I've solved my issue now by changing the code a bit and not using generics anymore, but I'd still like to know what I'm doing wrong in the above example.

Jan_V
  • 4,244
  • 1
  • 40
  • 64
  • What is the type of `DatabaseConnectionShardingTypes.StoreDatabaseByStoreId`? How did the your working registration looked before adding the generic paramter? – nemesv Feb 26 '15 at 15:49

1 Answers1

1

You are trying to cast a ConnectionManagerFactory<StoreElasticConnectionManagerByStore> to a IConnectionManagerFactory<IConnectionManager>

You have to specify that your interface is covariant to allow such cast, you do it with the out modifier (see "out T" vs. "T" in Generics for more information)

public interface IConnectionManagerFactory<out T>
        where T : IConnectionManager
{ }

If you try the following line of code you will obtain a compilation error :

ConnectionManagerFactory<StoreElasticConnectionManagerByStore>  c = null;
IConnectionManagerFactory<IConnectionManager> i = (ConnectionManagerFactory<StoreElasticConnectionManagerByStore>)c; 

Cannot implicitly convert type 'ConnectionManagerFactory' to 'IConnectionManagerFactory'. An explicit conversion exists (are you missing a cast?)

Adding the out modifier implies that your interface can't accept a T within a method parameter :

public interface IConnectionManagerFactory<out T>
        where T : IConnectionManager
{
    void Do1(T t); // not valid 
    T Do2();       // valid
}

If your IConnectionManagerFactory<T> is not covariant, you will have to change your code by splitting your interface for example

Community
  • 1
  • 1
Cyril Durand
  • 15,834
  • 5
  • 54
  • 62