Ivan Montilla
Ivan Montilla

Reputation: 392

Extensible ASP.NET Core 2 application

I need to create an ASP.NET Core 2 application that can be extensible.

An extension, is a project that reference the main assembly and can extends it adding new controllers, models (with EF migrations), views, razor pages, etc.

Because the extensions need use the main application base classes like base controller, base model or view/page layout, the main application cannot reference the module project (avoid circular references).

I'm not sure how can I achieve this, but the idea is an installation of the main project, can add new functionality simple putting the modules DLL (or by online market in the main application, that download the DLL).

In my research, I found Applications parts, but my problem here is I need specify the part assembly in Startup class, and I need in installed the capacity of install modules without doing any changes in the code.

Some modules, need be extensible too, for example, an accounting module, need to connect with bank, and it have an interface that defines the methods of working with the bank, for example:

public interface IBankingOperation
{
    public async void PayInvoiceAsync(Invoice invoice);
    // More methods...
}

Then, differents projects can reference the banking assembly and provide implementation for differents banks.

In the main application, this modules can be installed like other modules, but checking the base module is intalled, for example, I can install the Santander module like other module, but only if banking module is installed (module dependency).

In conclusion, I need to create a modular ASP.NET Core 2 application, but the main assembly cannot reference the modules, the modules must reference the main assembly. The modules can contain Controllers, Models, Views, Pages, Etc.

Upvotes: 0

Views: 547

Answers (1)

malballah
malballah

Reputation: 701

In the main web app you would call a class which loads the extensions in the memory

ModuleManager.LoadModules(Path.Combine(_env.ContentRootPath, "Modules"));

this is the load modules function

public static void LoadModules(string modulesFolder)
    {
        var appsFolders = new DirectoryInfo(modulesFolder).GetDirectories();

        foreach (var appFolder in appsFolders)
        {
            var binFolder = new DirectoryInfo(Path.Combine(appFolder.FullName, "bin"));
            if (!binFolder.Exists)
            {
                continue;
            }
            var assemblies = AssemblyProvider.GetAssemblies(binFolder.FullName);
            foreach (var assembly in assemblies)
            {
                var iModuleClass = assembly.GetTypes().FirstOrDefault(type => type.GetInterfaces().Contains(typeof(IModule))
                                    && type.GetConstructor(Type.EmptyTypes) != null);
                if (iModuleClass != null)
                {
                    var module = Activator.CreateInstance(iModuleClass) as IModule;
                    module.ModuleFolder = appFolder;
                    Modules.Add(module); 
                    Assemblies.Add(assembly);
                    break;
                }
            }



        }

    }

then you should have an interface which should be implemented by each module the class which implement this interface should do the work of registering services and database models and all staff needed and you will load them as follows

public static IEnumerable<IExtensionRegister> GetModuleRegistrars()
    {
        lock (Modules)
        {
            return Modules.Select(item => item.Registrar).Where(item=>item!=null).ToList();
        }
    }

Upvotes: 1

Related Questions