Search code examples
c#dependency-injectionmaui

How to read dll file in Maui project


I have dll library let's name it "LibraryA". I've attached it to Application/Resources/Raw/LibraryA.dll. I just want to register all the services and interfaces from that library like:

    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiCommunityToolkit()
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                    fonts.AddFont("Roboto-Regular.ttf", "RobotoRegular");
                    fonts.AddFont("Roboto-Medium.ttf", "RobotoMedium");
                    fonts.AddFont("Roboto-Light.ttf", "RobotoLight");
                })
                ;
            
            RegisterServicesAsync(builder.Services);
            builder.Services.AddSingleton<App>();

            var app = builder.Build();
            return app;
        }


        private static void RegisterServices(IServiceCollection services)
        {
            string dllFileName = "LibraryA.dll";
            var dllPath = Path.Combine(FileSystem.AppDataDirectory, dllFileName);

            var assembly = Assembly.LoadFrom(dllPath);

            var serviceTypes = assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any());

            foreach (var implementationType in serviceTypes)
            {
                var interfaceType = implementationType.GetInterfaces().FirstOrDefault();
                if (interfaceType != null)
                {
                    var descriptor = services.FirstOrDefault(d => d.ServiceType == interfaceType);
                    if (descriptor != null)
                    {
                        services.Remove(descriptor);
                    }

                    services.AddSingleton(interfaceType, implementationType);
                }
            }
        }
    }

And there is an exception thrown because there is no LibraryA.dll file in path: '/data/user/0/com.application/files/LibraryA.dll'. When I copy LibraryA.dll to that path everything works. But I can't achieve that with

using var stream = await FileSystem.OpenAppPackageFileAsync(dllFileName);
using var fileStream = File.Create(dllPath);
await stream.CopyToAsync(fileStream);

, because that code is asynchronous and there is a System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.' because services didn't register. There is the code with copy file to device:

private static async Task RegisterServicesAsync(IServiceCollection services)
{
    string dllFileName = "LibraryA.dll";
    var dllPath = Path.Combine(FileSystem.AppDataDirectory, dllFileName);

    ****added code below****
    using var stream = await FileSystem.OpenAppPackageFileAsync(dllFileName);
    using var fileStream = File.Create(dllPath);
    await stream.CopyToAsync(fileStream);
    **********************

    var assembly = Assembly.LoadFrom(dllPath);

    var serviceTypes = assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any());

    foreach (var implementationType in serviceTypes)
    {
        var interfaceType = implementationType.GetInterfaces().FirstOrDefault();
        if (interfaceType != null)
        {
            var descriptor = services.FirstOrDefault(d => d.ServiceType == interfaceType);
            if (descriptor != null)
            {
                services.Remove(descriptor);
            }

            services.AddSingleton(interfaceType, implementationType);
        }
    }
}

Should I copy LibraryA.dll to android device by hand? How can I handle that?


Solution

  • Try to set up Build Action for your LibraryA.dll in .csproj file like this:

    <ItemGroup>
        <EmbeddedResource Include="LibraryA.dll" />
    </ItemGroup>
    <ItemGroup>
        <None Remove="LibraryA.dll" />
    </ItemGroup>
    

    It worked for me to move my file into apk bundle of application. And then in code to EXTRACT FILE FROM APK BUNDLE and MOVE FILE INTO APK FOLDER I do:

    var assembly = System.Reflection.Assembly.GetExecutingAssembly();
    using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
    {
        using (var fileStream = new FileStream(destinationPath, FileMode.OpenOrCreate, FileAccess.Write))
        {
          resourceStream.CopyTo(fileStream);
        }
    }
    

    where resourceName = $"{nameof(SolutionName)}.LibraryA.dll" and destinationPath where to move this file = Path.Combine(appDataDirectory, "LibraryA.dll"). appDataDirectory in Xamarin.Forms is FileSystem.AppDataDirectory I don't which is for MAUI but I think the same.

    Hope this hepls. If you have any questions please leave a comment.