Home > Software engineering >  Autofac maui, Autofac.Core.Activators.Reflection.NoConstructorsFoundException: 'No accessible c
Autofac maui, Autofac.Core.Activators.Reflection.NoConstructorsFoundException: 'No accessible c

Time:05-26

so I'm following a course for xamarin forms but I'm adapting it for use with .net Maui. It's a basic app so far with only the flyout shell built.

The author is using autofac which I've never before heard of nor used. I am following the example and get a runtime exception:

Autofac.Core.Activators.Reflection.NoConstructorsFoundException: 'No accessible constructors were found for the type 'MauiApp4.Resource Xml'.'

I have no idea what this means and no idea how to proceed :sigh

(I think it is trying to say it can't access something? I also suspect something to do with IContainer ?)

Oh it

This occurs when I try to build and run the app on an Android Pixel 5.0 API 30 Android emulator from Visual Studio 2022 community edition.

It seems to crash at the line Container = builder.Build();

Autofac is loaded using nuget and is version 6.3.0

I have no idea what version of .net or maui I'm on but it is the latest (How do I get this info?)

Here is the current (applicable?) code: (it's also on my github repo https://github.com/gfmoore/MauiApp4B.App/tree/master )

App.xaml.cs

using System.Reflection;
using Autofac;
using IContainer = Autofac.IContainer;

namespace MauiApp4;

public partial class App : Application
{
    public static IContainer Container;

    public App()
    {
      InitializeComponent();
      //class used for registration
      var builder = new ContainerBuilder();

      //scan and register all classes in the assembly
      var dataAccess = Assembly.GetExecutingAssembly();
      builder.RegisterAssemblyTypes(dataAccess)
             .AsImplementedInterfaces()
             .AsSelf();

      //TODO - register repositories if you use them
      //builder.RegisterType<Repository<User>>().As<IRepository<User>>();

      //get container
      Container = builder.Build();

      //set first page
      MainPage = new AppShell();
    }
}

AppShell.xaml.cs

using Autofac;

namespace MauiApp4;

public partial class AppShell : Shell
{
  public AppShell()
  {
    InitializeComponent();
    Application.Current.UserAppTheme = AppTheme.Dark;
    BindingContext = App.Container.Resolve<AppShellViewModel>();
  }
}

AppShellViewModel.cs

using System;
using System.Windows.Input;

namespace MauiApp4
{
  public class AppShellViewModel
  {
    public ICommand SignOutCommand { get => new Command(async () => await SignOut());  }

    private async Task SignOut()
    {
      await Shell.Current.DisplayAlert("Todo sign out code", "You have been logged out.", "Ok");
    }
  }
}

Any help gratefully received.

G

CodePudding user response:

Welcome to the wonderful world of dependency injection and native code. It's kind of a deep end of the pool to be jumping into, so you'll want to be patient with yourselves, crack your knuckles, and become best friends with Google/Bing/whatever.

I'm not a MAUI user, but I am one of the Autofac owners. Autofac is a dependency injection / inversion of control framework, which means it's going to find all of your dependencies and wire things up. Instead of calling new X(y, z) to create something, you'll get things out of the inversion of control container. The container will figure out the parameters for your constructors and resolve them all.

There is a lot of Autofac documentation here. A lot. DI can be a complex topic involving figuring out how long objects should live (are they singletons or do you want a new one every time you ask for it?) and how to wire things up. This doc will be a good friend, just like your favorite search engine.

Bringing this together, I see this in your code:

builder.RegisterAssemblyTypes(dataAccess)
       .AsImplementedInterfaces()
       .AsSelf();

This is doing assembly scanning to register literally every type in that data access assembly into the container. By doing this, you're telling Autofac you also intend to resolve literally every time in that data access assembly, so when you ask for it, it'll try to locate the constructors and start new-ing things up.

Your error says:

Autofac.Core.Activators.Reflection.NoConstructorsFoundException: 'No accessible constructors were found for the type 'MauiApp4.Resource Xml'.'

...but you didn't provide a stack trace. I don't recall that Autofac does any scanning for constructors on container build, and that exception is from the activator (meaning it's in the process of trying to resolve the thing), so I'm guessing the actual line blowing up is this:

BindingContext = App.Container.Resolve<AppShellViewModel>();

That is, the line where something tries to get resolved. I don't know this for sure because it may well be something internal to MAUI! Like I said, I'm not a MAUI user so I'm guessing, and you didn't include the stack trace so all I can do is guess more.

Putting that all together, though, it appears something is trying to resolve an instance of MauiApp4.Resource Xml (a class called Xml that's nested inside the class MauiApp4.Resource?) and there are no public constructors for it, so Autofac can't "new it up" for you. Likely this has to do with the fact that literally every type in the assembly was registered.

I would strongly recommend only registering exactly the types you need and only with the interfaces you use. Doing a big bulk registration with AsImplementedInterfaces and AsSelf is easy but it can also make troubleshooting harder since it could be that this is happening way down in some resolve chain. I can't promise that changing the assembly scanning is going to fix this issue, it'll just potentially make it easier to figure out.

The first step is to figure out what's using MauiApp4.Resource Xml and why that thing is getting created. Like, find a class that has a constructor parameter with it. It may be nested deep in a resolve chain! It may be compiler-generated! The full exception - with inner exception messages and stack trace - may help you!

// Some class may need a Resource...
public class SomeClassInTheApp
{
  public SomeClassInTheApp(Maui.Resource resource) { ... }
}
// And the resource may need the internal class?
public class Resource
{
  public Resource(Xml xml) {...}

  // I imagine Xml is nested inside Resource?
  public Xml
  {
    // But it might not have a public constructor
    internal Xml(){}
  }
}

There's a lot I can only guess at, but hopefully that gives you a place to look.

I will say, be sure to read the whole exception, including inner exception messages, and stack traces. They can be very long, and can be confusing, but Autofac has done its best to try to provide enough information in there to point you to where the problem is. Downside is that since the problem can be way deep in the stack somewhere, the exception may take some reading to get through.

There's a whole debugging and troubleshooting tips page in the Autofac docs that might help, too.

Sorry I can't tell you exactly what to fix but hopefully this unblocks you and gets you looking in the right spot.

CodePudding user response:

Happily the instructor of the course responded even though it's a Xamarin forms course and not a Maui course. His solution seems to be in line with the very detailed and wonderful answer above.

Remove the code:

builder.RegisterAssemblyTypes(dataAccess)
       .AsImplementedInterfaces()
       .AsSelf();

and add your registrations manually? So you add a line:

builder.RegisterType<AppShellViewModel>();

So that has got the app now working.

Many thanks again to Travis Illig. I think it's wonderful when ones spend so much time helping others and in such a detailed way. :)

  • Related