繁体   English   中英

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

[英]C#: How to send Dynamic Razor Components from Server to Client in Blazor Wasm Hosted?

我想在Blazor wasm 托管项目中渲染动态 razor 组件。 这些组件是使用IDynamicComponent接口在 Razor Class 库中创建的。 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;}
}

组件示例

我的组件.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);

}

组件1.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 Function 在服务器

 // 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 在客户端

尝试将 object 转换为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();
});
 

浏览器控制台 Output

{ 
  "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?

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

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

代码看起来像:

// 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也有类似的问题。 最近被我咬了!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM