Getting null argument when implementing N-tiers architecture with Autofac

0

I created this simple WebAPI project containing the following library class

  • Coordination (ServiceLayer)
  • Domain (BusinessLayer)
  • Data (DataLayer)
  • Dtos (Dtos for webapi)

Basically WebAPI project calls(reference) Dtos, and Coordination. Coordination Calls Domain and Domain call Data.

This is what my structure look like.

enter image description here

My issues is implementing Dependency Injection using Autofac. I can call the Coordination layer, when i try to call the domain layer this is where it get confused.

This is how I defined my registertype

public class AutofacWebapiConfig
    {

        public static IContainer Container;

        public static void Initialize(HttpConfiguration config)
        {
            Initialize(config, RegisterServices(new ContainerBuilder()));
        }

        public static void Initialize(HttpConfiguration config, IContainer container)
        {
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        }

        private static IContainer RegisterServices(ContainerBuilder builder)
        {
            //Register your Web API controllers.  
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            builder.RegisterAssemblyTypes(Assembly.Load(nameof(Coordination)))
              .Where(t => t.Namespace.Contains("Services"))
              .As(t => t.GetInterfaces().FirstOrDefault(i => i.Name == "I" + t.Name));

            builder.RegisterAssemblyTypes(Assembly.Load(nameof(Domain)))
              .Where(j => j.Namespace.Contains("Domain"))
              .As(j => j.GetInterfaces().FirstOrDefault(i => i.Name == "I" + j.Name));

            //Set the dependency resolver to be Autofac.  
            Container = builder.Build();

            return Container;
        }

    }
  1. A few issues occurs. First it doesn't know how to find Domain, because in theory WebAPI does not talk to the Domain layer.
  2. I did add it as a reference but now i get a null argument error. ServiceType cannot be null.

Nothing that jumps out of me from the service implementation

 public class StudentService : IStudentService
    {
        private readonly IStudentDomain studentDomain;
        public StudentService(IStudentDomain _studentDomain)
        {
            this.studentDomain = _studentDomain;
        }

        public async Task<StudentDto> GetStudentByID(string id)
        {
            var test = this.studentDomain.getStudentByID(id);
        }
}

And here is my domain implementation

    public class StudentDomain : IStudentDomain
    {

        public StudentDomain()
        {

        }

        /// <summary>
        /// Return student
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        StudentEntity IStudentDomain.getStudentByID(string id)
        {
            StudentEntity student = new StudentEntity("dd", "aa", "ddd");
            return student;
        }
    }

This is the error I keep getting enter image description here

I am sorry my os is french, but its just mean the value is null. Full stack error

System.ArgumentNullException
  HResult=0x80004003
  Message=La valeur ne peut pas être null.
Nom du paramètre : serviceType
  Source=Autofac
  StackTrace:
   at Autofac.Core.TypedService..ctor(Type serviceType)
   at Autofac.RegistrationExtensions.<>c__DisplayClass14_0`3.<As>b__0(Type t)
   at Autofac.RegistrationExtensions.<>c__DisplayClass13_0`3.<As>b__0(Type t)
   at Autofac.Features.Scanning.ScanningRegistrationExtensions.<>c__DisplayClass8_0`3.<As>b__0(Type t, IRegistrationBuilder`3 rb)
   at Autofac.Features.Scanning.ScanningRegistrationExtensions.ScanTypes(IEnumerable`1 types, IComponentRegistryBuilder cr, IRegistrationBuilder`3 rb)
   at Autofac.Features.Scanning.ScanningRegistrationExtensions.ScanAssemblies(IEnumerable`1 assemblies, IComponentRegistryBuilder cr, IRegistrationBuilder`3 rb)
   at Autofac.Features.Scanning.ScanningRegistrationExtensions.<>c__DisplayClass0_0.<RegisterAssemblyTypes>b__0(IComponentRegistryBuilder cr)
   at Autofac.ContainerBuilder.Build(IComponentRegistryBuilder componentRegistry, Boolean excludeDefaultModules)
   at Autofac.ContainerBuilder.Build(ContainerBuildOptions options)
   at CB.WebAPI.App_Start.AutofacWebapiConfig.RegisterServices(ContainerBuilder builder) in C:\source\repos\CB.WebAPI\CB.WebAPI\App_Start\AutofacWebapiConfig.cs:line 43
   at CB.WebAPI.App_Start.AutofacWebapiConfig.Initialize(HttpConfiguration config) in C:\source\repos\CB.WebAPI\CB.WebAPI\App_Start\AutofacWebapiConfig.cs:line 21
   at CB.WebAPI.App_Start.Bootstrapper.Run() in C:\source\repos\CB.WebAPI\CB.WebAPI\App_Start\Bootstrapper.cs:line 14
   at CB.WebAPI.WebApiApplication.Application_Start() in C:\source\repos\CB.WebAPI\CB.WebAPI\Global.asax.cs:line 18

Here I am adding the global.asax.cs file. Line 18 is my bootstrapper.run();

    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            Bootstrapper.Run();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
c#
asp.net-web-api
dependency-injection
autofac
asked on Stack Overflow May 28, 2020 by Jseb • edited May 28, 2020 by Jseb

1 Answer

2

It seems that there is an issue with registering Domain because StudentEntity does not have corresponding interface and .As(j => j.GetInterfaces().FirstOrDefault(i => i.Name == "I" + j.Name)) will fail to find one for it.

If you don't want to manually add all types with interfaces try this:

builder.RegisterAssemblyTypes(typeof(StudentDomain).Assembly)
          .Where(j => j.Namespace.Contains("Domain"))
          .AsImplementedInterfaces()

Possibly you will want/need to distinguish between types with interfaces and without them, you can try next approach:

builder.RegisterAssemblyTypes(typeof(StudentDomain).Assembly)
          .Where(j => j.Namespace.Contains("Domain") && j.GetInterfaces().Any())
          .AsImplementedInterfaces()

builder.RegisterAssemblyTypes(typeof(StudentDomain).Assembly)
          .Where(j => j.Namespace.Contains("Domain") && !j.GetInterfaces().Any())
          .AsSelf();

P.S.

Also I would recommend to change all Assembly.Load(nameof(SOME_NAME)) to typeof(TYPE_NAME).Assembly, I think it is more readable and obvious.

answered on Stack Overflow May 28, 2020 by Guru Stron • edited May 28, 2020 by Guru Stron

User contributions licensed under CC BY-SA 3.0