3

I am having a weird problem using Unity as an IOC container and im out of ideas of what could cause it. I have a service dependency in my webapi controller but it randomly fails to resolve this dependency. Sometimes i have to start my application 3 or 4 times and then it suddenly works again.

The error I am getting is:

Resolution of the dependency failed, type = "Base.WebApi.Controllers.ApiUsersController", name = "(none)". Exception occurred while: while resolving. Exception is: InvalidOperationException - The type IApiUserService does not have an accessible constructor. ----------------------------------------------- At the time of the exception, the container was: Resolving Base.WebApi.Controllers.ApiUsersController,(none) Resolving parameter "apiUserService" of constructor Base.WebApi.Controllers.ApiUsersController(Base.BLL.Services.User.IApiUserService apiUserService) Resolving Base.BLL.Services.User.IApiUserService,(none)

For initializing and registering my types in unity i use the following:

public static void RegisterTypes(IUnityContainer container)
{
    var myAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("Base") && !a.FullName.StartsWith("Base.WebApi")).ToArray();

    container.RegisterType(typeof(Startup));

    container.RegisterTypes(
            UnityHelpers.GetTypesWithCustomAttribute<UnityIoCSingletonLifetimedAttribute>(myAssemblies),
            WithMappings.FromMatchingInterface,
            WithName.Default,
            WithLifetime.ContainerControlled,
            null
        ).RegisterTypes(
                UnityHelpers.GetTypesWithCustomAttribute<UnityIoCTransientLifetimedAttribute>(myAssemblies),
                WithMappings.FromMatchingInterface,
                WithName.Default,
                WithLifetime.Transient);

}

As you can see i am using singletone and transient named attributes to define the way my dependencies should be resolved.

My controller looks like this:

public class ApiUsersController : ODataController
{

    private readonly IApiUserService _apiUserService;

    public ApiUsersController(IApiUserService apiUserService)
    {
        _apiUserService = apiUserService;
    }

    public IQueryable<ApiUserEntity> Get()
    {
        return this._apiUserService.GetUsers();
    }
}

as you can see it has a dependency on user service which looks like this:

[UnityIoCTransientLifetimed]
public class ApiUserService : BaseService, IApiUserService
{
    private readonly IUserRepository _userRepository; 

    public ApiUserService(IUserRepository userRepository, IUnitOfWork uow) : base(uow)
    {
        _userRepository = userRepository;
    }
 }

The api user repository looks like this:

[UnityIoCTransientLifetimed]
public class UserRepository :  GenericRepository<ApiUserEntity>, IUserRepository
{
    public UserRepository(IUnitOfWork unitOfWork, IDomainContext context) : base(unitOfWork, context)
    {

    }

Extending the following GenericRepository:

public class GenericRepository<T> : IGenericRepository<T> where T : class
{
    protected readonly BaseContext Context;

    public GenericRepository(IUnitOfWork unitOfWork, IBaseContext context)
    {
        // register this repository with the unit of work.
        unitOfWork.Register(this);

        Context = (BaseContext)context;
    }

With my unit of work that looks like this:

[UnityIoCSingletonLifetimed]
public class UnitOfWork : IUnitOfWork
{
    private readonly Dictionary<string, IRepository> _repositories;

    // unit of work class is responsible for creating the repository and then dispossing it when no longer needed.
    public UnitOfWork()
    {
        _repositories = new Dictionary<string, IRepository>();
    }
 }

However it sometimes works and sometimes it doesnt and i cant figure out why or where to look.

Jurgen Welschen
  • 871
  • 1
  • 13
  • 21
  • Does it work if you register the types explicitly instead of using registration by convention? – Thomas Levesque Aug 19 '15 at 18:38
  • Possibly the `IApiUserService` isn't registered before the `ApiUsersController`, which means that it doesn't have a type to inject into that constructor. – Ron Beyer Aug 19 '15 at 18:40
  • btw, are you able to reproduce the issue in debug? If you are, can you check what is returned by `AppDomain.CurrentDomain.GetAssemblies()`? I suspect the explanation might be that the assembly containing `ApiUserService` is not loaded yet. – Thomas Levesque Aug 19 '15 at 18:40
  • @RonBeyer, the registration order doesn't matter, as long as everything is registered before you try to resolve – Thomas Levesque Aug 19 '15 at 18:41
  • I just tried to register them explicitely and it has not crashed once since. restarted about 5/6 times. It seems to be in the GetAssemblies part indeed. – Jurgen Welschen Aug 19 '15 at 18:49
  • Ok guys it is indeed in the getassemblies. I used registration by convention again and sometimes it does not load the Base.BLL assembly (the one where apiuserservice is located) and sometimes it does. How can that be? – Jurgen Welschen Aug 19 '15 at 18:52
  • So sometimes this: http://i.imgur.com/AaRbdo3.png (all good) and sometimes this: http://i.imgur.com/W9VLw7l.png (missing base.BLL ) – Jurgen Welschen Aug 19 '15 at 18:54
  • Just came accross this SO post: http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembli which tells me getassemblies only loads all assemblies when its needed. – Jurgen Welschen Aug 19 '15 at 18:58

1 Answers1

1

Finally solved it thanks to some suggestions. Looking at the documentation for

AppDomain.CurrentDomain.GetAssemblies()

it says the following: Gets the assemblies that have been loaded into the execution context of this application domain.

Which basically means that it only loads the assemblies when they are actually needed.

The way i solved it was by using the more reliable GetReferencedAssemblies which loads all assemblies even if they are not being used.

var allAssemblies = new ReadOnlyCollection<Assembly>(
BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList());

Restarted tons of times and not one resolve crash :) Thanks everyone! For everyone looking for more information check out this SO answer: Difference between AppDomain.GetAssemblies and BuildManager.GetReferencedAssemblies

Community
  • 1
  • 1
Jurgen Welschen
  • 871
  • 1
  • 13
  • 21