C#:如何在 Blazor Wasm 托管中将动态 Razor 组件从服务器发送到客户端?

Posted

技术标签:

【中文标题】C#:如何在 Blazor Wasm 托管中将动态 Razor 组件从服务器发送到客户端?【英文标题】:C#: How to send Dynamic Razor Components from Server to Client in Blazor Wasm Hosted? 【发布时间】:2021-02-08 09:22:17 【问题描述】:

我想在 Blazor wasm 托管 项目中呈现动态剃须刀组件。这些组件是在 Razor 类库中使用IDynamicComponent 接口创建的。 Blazor 服务器端加载 dll 并将它们存储到 IEnumerable<Type> 中。问题是我无法通过 SignalR 将 System.Type 的各个组件发送到客户端并转换为 IDynamicComponent

IDynamicComponent.cs

public interface IDynamicComponent

    IDictionary<Type,Type> InjectableDependencies  get; 
    IDictionary<string,string> Parameters  get; 
    string Name  get; 
    Type Component  get;

组件示例

MyComponent.cs

public class MyComponent : IDynamicComponent

    public IDictionary<Type, Type> InjectableDependencies => new Dictionary<Type, Type>();
    public IDictionary<string, string> Parameters => new Dictionary<string, string>();
    public string Name => "Example1";
    public Type Component => typeof(Component1);


Component1.razor

<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code 
    private int currentCount = 0;
    private void IncrementCount()
    
        currentCount++;
    

Blazor 服务器端 加载组件

public IEnumerable<Type> Components  get; private set; 

// Loads the dlls and stores the components in the 'IEnumerable<Type>'
public void LoadComponents(string path)

    var components = new List<Type>();
    var assemblies = LoadAssemblies(path);

    foreach (var asm in assemblies)
    
        var types = GetTypesWithInterface(asm);
        foreach (var typ in types) components.Add(typ);
    
    Components = components;


// Gets the component by name
public IDynamicComponent GetComponentByName(string name)

     return Components.Select(x => (IDynamicComponent)Activator.CreateInstance(x))
            .SingleOrDefault(x => x.Name == name);

服务器端的 SignalR 函数

 // Sends a component to client via SignalR when requested.
 public async Task GetPlugin()
 
    // Converting component of System.Type to object to send to client
    string typeName = Component.FullName;
    Type type = GetTypeFrom(typeName);
    object obj = Activator.CreateInstance(type);
    string component = JsonConvert.SerializeObject(obj);
    
    await myHub.Clients.All.SendAsync("Plugin", component);
 

private Type GetTypeFrom(string valueType)
        
            var type = Type.GetType(valueType);
            if (type != null)
                return type;

            try
            
                var assemblies = AppDomain.CurrentDomain.GetAssemblies();

                //To speed things up, we check first in the already loaded assemblies.
                foreach (var assembly in assemblies)
                
                    type = assembly.GetType(valueType);
                    if (type != null)
                        break;
                
                if (type != null)
                    return type;

                var loadedAssemblies = assemblies.ToList();

                foreach (var loadedAssembly in assemblies)
                
                    foreach (AssemblyName referencedAssemblyName in loadedAssembly.GetReferencedAssemblies())
                    
                        var found = loadedAssemblies.All(x => x.GetName() != referencedAssemblyName);

                        if (!found)
                        
                            try
                            
                                var referencedAssembly = Assembly.Load(referencedAssemblyName);
                                type = referencedAssembly.GetType(valueType);
                                if (type != null)
                                    break;
                                loadedAssemblies.Add(referencedAssembly);
                            
                            catch
                            
                                //We will ignore this, because the Type might still be in one of the other Assemblies.
                            
                        
                    
                
            
            catch (Exception exception)
            
                //throw my custom exception    
            

            if (type == null)
            
                //throw my custom exception.
            

            return type;
        

客户端的 SignalR

尝试将对象强制转换为IDynamicComponent时发生错误。

public IDynamicComponent Component;
    
hubConnection.On<string>("Plugin", (component) =>

    object obj = JsonConvert.DeserializeObject<object>(component);
    Console.WriteLine(obj.ToString());

    // Casting object to 'IDynamicComponent' to be able to render
    Component = (IDynamicComponent)obj; // Error occurs here

    UpdateBlazorUIEvent?.Invoke();
);
 

浏览器控制台输出

 
  "InjectableDependencies": , 
  "Parameters": ,
  "Name": "Counter",
  "Component": "PluginOne.Component1, PluginOne, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",


Specified cast is not valid.

如何成功地将组件 (System.Type) 发送到客户端并转换为 IDynamicComponent 以呈现 UI?

【问题讨论】:

【参考方案1】:

你不能通过 Json 传递Types。这正是错误所说的 - 不支持“System.Type”实例的序列化和反序列化。

您需要使用反射来获取实际的完全限定类名作为字符串,通过IDynamicComponent 传递它,比如myassembly.myclass,然后在另一端获取对象的新实例。

代码看起来像:

// In Code
var myclass = typeof(myComponent).AssemblyQualifiedName;
// Out code
var assembly = Assembly.GetExecutingAssembly();
var mytype = assembly.GetTypes().First(item => item.Name.Equals(className));
var myclass = Activator.CreateInstance(type);

传递泛型类型object 也有类似的问题。最近被我咬了!

【讨论】:

你的回答对我来说有点不清楚。这是我第一次使用这种加载动态组件的概念。也许如果您可以添加更多信息,它将阐明我需要做什么。 嗨,是的,请澄清一下。您的 WASM 应用程序和服务器 API 控制器通过 Json 进行通信。我希望你能理解的是,你不能用 Json 传递 Type 类型的对象,因此不能通过 Json 传递 IDynamicComponent 的任何对象,因为接口包含 Type 类型的对象。你需要考虑不同的策略来实现你的目标。其中一种方法是将 Json WASM 中的类名传递给 Controller 数据交换。如果无法全面了解您要实现的目标,我们只能就您如何实现目标提出建议 嗨,肖恩。我设法获取对象的新实例并使用 newtonsoft 对其进行序列化并通过信号器发送给客户端。但是,在客户端中,我似乎无法将收到的对象转换为 IDynamicComponent。你知道我该怎么做吗? 如果它编译,运行并停止你可以看到 newtonsoft 序列化/反序列化的“对象”的位置。发布您看到的内容。 嗨,肖恩。我发布了浏览器控制台中显示的对象的输出。但是,我找不到将这个对象转换为 IDynamicComponent 以呈现 UI 的方法。我还改进了问题的格式,使其更加清晰。

以上是关于C#:如何在 Blazor Wasm 托管中将动态 Razor 组件从服务器发送到客户端?的主要内容,如果未能解决你的问题,请参考以下文章

如何绕过 blazor wasm 路由系统并调用服务器

IdentityServerBuilderConfigurationExtension 中的 Blazor WASM 托管身份验证空引用异常

使用身份验证托管的 Blazor Wasm 上出现 Azure 500 错误

升级到 .net 6 时托管的 Blazor WASM 身份验证中断

未注册身份验证 AuthenticationStateProvider 的 Blazor Wasm 托管预渲染

如何更改 blazor wasm 应用程序的基本 URL