Load external Assembly (RCL) to Blazor WebAssembly app - .net-assembly

It is possible to load a RCL (Razor Component Library) to Blazor WebAssembly dynamically?
I found this Loading an external .NET Standard 2.0 assembly with blazor to load a standard classes
What I want is to develop a pluggable/extensible visual framework, where putting a dll in a ASP.NET Core Server folder where enought to access to that blazor component
Solution config:
ASP.NET Core WebAPI project
Blazor WebAssembly project
RCL project 1 with some components
RCL project 2 with another components
Steps:
Open a Blazor Page and OnInitializedAsync() retreive some dll from WebAPI as binary
Load binary to Assembly
Reference the assembly and use it dynamically in the page

Sorry for the late answer, but yes, you can do that.
To use dynamic components, follow these steps:
Load the assemblies using the Assembly.LoadfFrom(assemblyFilename)
In a .razor file, use the blazor render tree builder to render your component dynamically, like this:
RenderFragment EditContent = (__builder) =>
{
__builder.OpenComponent(0, TypeOfYourComponent);
__builder.AddAttribute(1, "attr1", attrValue);
...
__builder.AddAttribute(n, "attrn", attrNValue);
__builder.CloseComponent();
};
#EditContent
Implement a method that returns the type you want from the currently loaded assemblies. The following will provide you a list with all types exported by dynamically loaded assemblies:
var exportedTypes = new List<Type>();
var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
foreach (var assembly in assemblies)
{
exportedTypes.AddRange(assembly.GetExportedTypes().ToList());
}
In my case, I created a singleton that loads the assemblies from a folder when it is initialized, and created a method in this singleton to return the type from the list defined on step 3. So, in this way, you can call this service whenever you like by registering it on the Startup.cs and injecting it on your razor components:
public class CustomComponentService : ICustomComponentService
{
public CustomComponentService(...)
{
// load the assemblies here
}
public Type GetCustomComponent(...)
{
//search in the loaded assemblies by any criteria you like
}
}
In the Startup.cs ConfigureServices method:
_ = services.AddSingleton<ICustomComponentService, CustomComponentService>();
In the razor file:
#inject ICustomComponentService CustomComponentService
...
__builder.OpenComponent(0, CustomComponentService.GetCustomComponent(...));
...

I finally develop a solution I want to share with you. A Module Manager witch allows you to load any outside component dynamically
Check it out here:
https://github.com/elgransan/BlazorPluginComponents
Some example code
var componentPackage = "RazorClassLibrary2";
var component = "Component2";
var stream = await Http.GetStreamAsync($"{MyNavigationManager.BaseUri}/{componentPackage}/{componentPackage}.dll");
var assembly = AssemblyLoadContext.Default.LoadFromStream(stream);
componentType = assembly.GetType(componentPackage + "." + component);
await DOMinterop.IncludeLink(componentPackage, $"/{componentPackage}/{componentPackage}.styles.css");
Where the files are in the server

Related

Owin startup bug with Umbraco Cloud - netStandard reference missing

In our current Umbraco Cloud project, we are using the Hangfire library (1.6.17) - the lib has a OWIN dependency (1.0.0).
Here is the code to call the hangfire launch:
In our current project, we are using the Hangfire library (1.6.17) - the lib has a OWIN dependency (1.0.0).
Here is the code to call the hangfire launch:
using Microsoft.Owin;
using Owin;
using Umbraco.Web;
using Hangfire;
using Hangfire.Dashboard;
using Hangfire.Annotations;
using Umbraco.Core.Models;
using Umbraco.Core;
using System.Web;
[assembly: OwinStartup(typeof(XX.Web.Core.Startup))]
namespace XX.Web.Core
{
public class Startup : UmbracoDefaultOwinStartup
{
public override void Configuration(IAppBuilder app)
{
//ensure the default options are configured
base.Configuration(app);
var cs = Umbraco.Core.ApplicationContext.Current.DatabaseContext.ConnectionString;
GlobalConfiguration.Configuration.UseSqlServerStorage(cs);
app.UseHangfireDashboard("/umbraco/backoffice/hangfire", new DashboardOptions
{
Authorization = new[] { new UmbracoUserAuthorisedFilter() },
AppPath = "/Umbraco"
});
app.UseHangfireServer();
}
}
public class UmbracoUserAuthorisedFilter : IDashboardAuthorizationFilter
{
public bool Authorize([NotNull] DashboardContext context)
{
// In case you need an OWIN context, use the next line,
// `OwinContext` class is the part of the `Microsoft.Owin` package.
//var context = new OwinContext(owinEnvironment);
// Allow all authenticated users to see the Dashboard (potentially dangerous).
//return context.Authentication.User.Identity.IsAuthenticated;
//this if you want to lock down to admins only
var userService = ApplicationContext.Current.Services.UserService;
var user = userService.GetByUsername(HttpContext.Current.User.Identity.Name);
return user.IsAdmin();
//this if you just want to make sure user is logged into backoffice
//return UmbracoContext.Current.Security.CurrentUser != null;
}
}
}
This is the default hangfire startup code to be able to use the library. The code has been working fine on 2 local machines, one Azure Web App instance but when I push this code to the Umbraco Cloud branch I get the following error:
Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.
The issue is: we are not using .net standard, both projects (web and core) are using .net framework 4.6.2
Is there any workaround for that issue ?

Why doesn't Azure load properly the Interop?

I have a WebApp on Azure that uses a dll. This library needs Interop libraries x86 and x64.
Sometimes, at the restart of the App (I suppose), the App fails due to an exception:
System.EntryPointNotFoundException: Unable to find an entry point named 'sqlite3_config' in DLL 'SQLite.Interop.dll'. at System.Data.SQLite.UnsafeNativeMethods.sqlite3_config_none(SQLiteConfigOpsEnum op) at System.Data.SQLite.SQLite3.StaticIsInitialized() at System.Data.SQLite.SQLiteLog.Initialize() at System.Data.SQLite.SQLiteConnection..ctor(String connectionString, Boolean parseViaFramework) at T_Dox.WebService.SQLiteDb.CreateConnection() at WebService.CeDb.Connect()
The SQLite used is the SQLCipher's one.
What am I missing here? I don't understand why the app stops working suddenly even if I don't make any changes.
The App is a Web Service (.asmx file) that uses a data access layer to perform some business logic.
It was under a web site project, then we moved it into another project, a webapi\mvc project.
The routing bypasses this extension, so it works as before, a simple web service call.
The called web method initializes a business class loaded from another .net library (a VB.Net library).
Inside, this class uses a wrapper to a sqlConnection, in this case the SQLiteConnection.
In its constructor it starts an SQLiteConnection, and normally it works.
Then it performs some CRUD operations ...
So I can represent the operation this way:
[WebService(Namespace = "...")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class SampleService : System.Web.Services.WebService
{
[WebMethod]
public ServerInfo Test()
{
var sampleBusinessClass = new SampleBusinessCLass();
sampleBusinessClass.DoSomething();
using(var connection = new SQLiteConnection()) //the constructor is the parameterless one
{
//...
}
}
}
And the stack will be this (this is not the real one):
System.EntryPointNotFoundException: Unable to find an entry point named 'sqlite3_config' in DLL 'SQLite.Interop.dll'.
at System.Data.SQLite.UnsafeNativeMethods.sqlite3_config_none(SQLiteConfigOpsEnum op)
at System.Data.SQLite.SQLite3.StaticIsInitialized()
at System.Data.SQLite.SQLiteLog.Initialize()
at System.Data.SQLite.SQLiteConnection..ctor(String connectionString, Boolean parseViaFramework)
at xxx.WebService.SampleService.Test()
It always works, but sometimes it starts to launch this error until the stop and start of the web application on iss (in our case: Azure).
Inspecting the System.Data.SQlite.dll I can clearly see the entry point and actually it always passes this internal code (no conditions that can bypass this part) and it generally works.
The System.Data.SQlite.dll (1.0.96.0 version) is provided by SqlCypher product. I think it is the original System.Data.SQLite one because at first sight I can see the same assembly manifest and content.
The interop System.Data.SQLite uses is probably modified by SqlCypher team to give their features.
To avoid possible issues we put the interop in the path /bin/x64, then we compile our web app ONLY in x64 and it runs on a x64 environment.

How to document models & services which are in a separate project

I'm looking to demonstrate ServiceStack to my team, and as part of the demo I want to create some documentation. The UseCases.SwaggerHelloWorld runs fine, but my project (which has 1 functioning service) returns an empty swagger page. When I visit /resources it returns:
{
"swaggerVersion":"1.2",
"apis":[],
"apiVersion":"1.0",
"basePath":"http://localhost:29672",
"info":{"title":"Reports.Api"}
}
I then copied the Hello* service and models into my API project (in the same assembly as the host) and this worked. I then moved the service and models out into separate projects and the docs disappeared again.
So my question is, how can you document APIs when the models are in a separate DLL? The project structure is the same as recommended when you create a solution using the servicestack solution template.
The AppHost constructor tells ServiceStack which assemblies it should scan to locate, register and auto-wire your Services, e.g:
public class AppHost : AppHostBase
{
public AppHost() : base("Hello App", typeof(HelloService).Assembly) {}
}
It supports specifying multiple Assemblies if your Services are maintained in different projects, e.g:
public class AppHost : AppHostBase
{
public AppHost() : base("Hello App",
typeof(HelloService).Assembly, typeof(OtherService).Assembly) {}
}
The Modularizing Services docs has more info.
The DTO's used by the Service can be in a different project, you just need to specify which assemblies the Services are in. Although it's recommended the DTO's are maintained in a separate dep-free project which is the recommended Physical Project Structure that's contained in each ServiceStackVS VS.NET Project Template.

Problems using an installclass in a web setup for a web site

I am trying to create a web setup for my web site, and I want to use an installer class to do some custom stuff. I am using VS 2010, and the web site and installer is .NET 3.5.
I have added reference to the installer class project output in the Install section under Custom Actions:
I have also set /targetdir="[TARGETDIR]/" on the CustomActionData for this action.
The InstallScript project is a standard class library (dll).
There is a public class that inherits from Installer class. It overrides the Install method as I have seen been done in several online examples:
using System.Collections;
using System.Windows.Forms;
namespace InstallScript
{
public class MyWebInstaller : System.Configuration.Install.Installer
{
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
var targetDir = Context.Parameters["targetdir"];
if(targetDir==null) targetDir = "No TARGETDIR!";
MessageBox.Show("TARGETDIR:\t" + targetDir);
}
}
}
I would think there should be shown a message box here som time during the install, but it seems like it is never called. No error is shown either. The setup just runs through as if this code was never called.
Anyone have idea of what is wrong?
OK, I found out what was missing.
You need to specify the class with the class attribute RunInstaller(true) for the setup to pick up and actually run the code.
So the class needs to be declared like this:
[System.ComponentModel.RunInstaller(true)]
public class MyWebInstaller : System.Configuration.Install.Installer
{
...

Subsonic 3 Class Library & Winforms App Null IDataProvider BUG

I have got a class library project and a winforms app.
Everything is getting geerated fine and my Winforms app references the class library but as soon as I run it and try to retreive data it comes up with dataprovider is null.
The one thing to note is that I do not have a app.config in my Winforms app only in the class library. Do I need one in the Winforms app and if so what do I put in it?
Thanks
UPDATE: I think I have found a bug in Query\Select.cs
public Select(IDataProvider provider, params string[] columns)
{
//_provider is null
//provider is populated correctly
this.sqlFragment = new SqlFragment(_provider);
_provider = provider;
SelectColumnList = columns;
SQLCommand = this.sqlFragment.SELECT;
}
Yes, you need an App.config in your Winforms app and you put your connection string there. It's worth noting that an App.config is pointless in a class library EXCEPT when you're using SubSonic :), which will pull one from the project.
Class libraries don't have their own configuration - they pull their config from the execution environment.

Resources