gpt4 book ai didi

.net - SignalR 中的多态性

转载 作者:行者123 更新时间:2023-12-04 02:44:35 25 4
gpt4 key购买 nike

无法正常工作。

我希望我的集线器能够处理参数中的泛型。所以参数类型是一个抽象类,它将由一个具体的泛型类型实现——因为我不可能创建一个泛型方法。像这样:

         public void Process(MyAbstractClass arg)

但是当我告诉客户端序列化类型信息时注册失败。

这是客户端 (SignalR WinRT) 序列化配置。

        _hubConnecton.JsonSerializer = new JsonSerializer()
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple
};

这是我从 fiddler trace 得到的错误:

[JsonSerializationException]: Could not load assembly 'Microsoft.AspNet.SignalR.Client'.
at Newtonsoft.Json.Serialization.DefaultSerializationBinder.GetTypeFromTypeNameKey(TypeNameKey typeNameKey)
at Newtonsoft.Json.Utilities.ThreadSafeStore`2.AddValue(TKey key)
at Newtonsoft.Json.Utilities.ThreadSafeStore`2.Get(TKey key)
at Newtonsoft.Json.Serialization.DefaultSerializationBinder.BindToType(String assemblyName, String typeName)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadSpecialProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
[JsonSerializationException]: Error resolving type specified in JSON 'Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData, Microsoft.AspNet.SignalR.Client'. Path '[0].$type', line 1, position 111.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadSpecialProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IWrappedCollection wrappedList, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(TextReader reader, Type objectType)
at Microsoft.AspNet.SignalR.Json.JsonNetSerializer.Parse(TextReader reader, Type targetType)
at Microsoft.AspNet.SignalR.Json.JsonSerializerExtensions.Parse[T](IJsonSerializer serializer, String json)
at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.AuthorizeRequest(IRequest request)
at Microsoft.AspNet.SignalR.PersistentConnection.Authorize(IRequest request)
at Microsoft.AspNet.SignalR.Owin.CallHandler.Invoke(IDictionary`2 environment)
at Microsoft.AspNet.SignalR.Owin.Handlers.HubDispatcherHandler.Invoke(IDictionary`2 environment)
at Microsoft.Owin.Host.SystemWeb.OwinCallContext.Execute()
at Microsoft.Owin.Host.SystemWeb.OwinHttpHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object extraData)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Owin.Host.SystemWeb.Infrastructure.ErrorState.Rethrow()
at Microsoft.Owin.Host.SystemWeb.CallContextAsyncResult.End(IAsyncResult result)
at Microsoft.Owin.Host.SystemWeb.OwinHttpHandler.EndProcessRequest(IAsyncResult result)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

显然它在注册期间发送类型信息导致抛出上述错误

GET http://127.0.0.1:81/signalr/connect?transport=serverSentEvents&connectionToken=JKbyIAOOvt5BYGu_Ly2Yk9dNYVR7B180TobrrJpc5BYN5-DxdSwXs6i71pF0nJrLC3C7kaB-4VwD8Lu76vgVbIoWLE5Ux42GhJOJ_REslxuvo0bcCkbvf3rfki3Rk6TJ0&connectionData=[%7B%22$id%22:%221%22,%22$type%22:%22Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData,%20Microsoft.AspNet.SignalR.Client%22,%22Name%22:%22BusGatewayHub%22%7D] HTTP/1.1

如果我将以下行 TypeNameHandling = TypeNameHandling.Objects 更改为 TypeNameHandling = TypeNameHandling.Auto,然后我会收到一个错误,提示 MyAbstractClass无法实例化,因为它是抽象类型。

似乎我需要手动处理序列化,但我宁愿尽可能避免这种情况。

想法?

最佳答案

这可以做到,但并不容易 - SignalR 团队中的某个人一定一直在非常努力地努力,以使其几乎不可能扩展解析例程。

我看到了一堆 JSonSerializer 实例化,而不是提供已经在 GlobalConfig 中注册的实例化。

无论如何,这是如何做到的:

在客户端,实现 IHttpClient。此实现将从消息信封中删除类型信息。我们不需要保留信封上的类型信息,就我们而言,一个信封与另一个信封相同。重要的是内容类型。另外,WinRT 的信封引用了与标准框架不兼容的 WinRT 框架。

public class PolymorphicHttpClient : IHttpClient
{
private readonly IHttpClient _innerClient;

private Regex _invalidTypeDeclaration = new Regex(@"""?\$type.*?:.*?"".*?SignalR\.Client.*?"",?");

public PolymorphicHttpClient(IHttpClient innerClient)
{
_innerClient = innerClient;
}

public Task<IResponse> Get(string url, Action<IRequest> prepareRequest)
{
url = _invalidTypeDeclaration.Replace(url, "");
return _innerClient.Get(url, prepareRequest);
}

public Task<IResponse> Post(string url, Action<IRequest> prepareRequest, IDictionary<string, string> postData)
{
if (postData != null)
{
var postedDataDebug = postData;
//TODO: check out what the data looks like and strip out irrelevant type information.
var revisedData = postData.ToDictionary(_ => _.Key,
_ =>
_.Value != null
? _invalidTypeDeclaration.Replace(_.Value, "")
: null);
return _innerClient.Post(url, prepareRequest, revisedData);
}

return _innerClient.Post(url, prepareRequest, null);
}
}

您想像这样在客户端开始连接(在我的例子中是应用商店应用)

 _hubConnecton.Start(new AutoTransport(new PolymorphicHttpClient(new DefaultHttpClient())))

我希望这就足够了,但是在服务器端,内置的解析器是一团乱七八糟的烂摊子,所以我也不得不一起“破解”它。

您想实现 IJsonValue。原始实现创建了一个不符合您的配置的 JSonSerializer 的新实例。

 public class SerializerRespectingJRaw : IJsonValue
{
private readonly IJsonSerializer _jsonSerializer;
private readonly JRaw _rawJson;

public SerializerRespectingJRaw(IJsonSerializer jsonSerializer, JRaw rawJson)
{
_jsonSerializer = jsonSerializer;
_rawJson = rawJson;
}

public object ConvertTo(Type type)
{
return _jsonSerializer.Parse<object>(_rawJson.ToString());
}

public bool CanConvertTo(Type type)
{
return true;
}
}

然后您想创建自己的解析器。注意反射黑客,您可能希望将类型更改为任何版本。你拥有的 SignalR。这也是为什么我说写这部分的人一定非常讨厌 OO,因为所有模块都是内部的——这使得它们很难扩展。

public class PolymorphicHubRequestParser : IHubRequestParser
{
private readonly IJsonSerializer _jsonSerializer;
private JsonConverter _converter;

public PolymorphicHubRequestParser(IJsonSerializer jsonSerializer)
{
_converter =
(JsonConverter) Type.GetType(
"Microsoft.AspNet.SignalR.Json.SipHashBasedDictionaryConverter, Microsoft.AspNet.SignalR.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Single(_ => !_.GetParameters().Any())
.Invoke(null);

_jsonSerializer = jsonSerializer;
}

private IDictionary<string, object> GetState(HubInvocation deserializedData)
{
if (deserializedData.State == null)
return (IDictionary<string, object>)new Dictionary<string, object>();
string json = ((object)deserializedData.State).ToString();
if (json.Length > 4096)
throw new InvalidOperationException("Maximum length exceeded.");
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(_converter);
return JsonSerializerExtensions.Parse<IDictionary<string, object>>((IJsonSerializer)new JsonNetSerializer(settings), json);
}

public HubRequest Parse(string data)
{
var deserializedInvocation = new JsonNetSerializer().Parse<HubInvocation>(data);
var secondPass = new HubRequest()
{
Hub = deserializedInvocation.Hub,
Id = deserializedInvocation.Id,
Method = deserializedInvocation.Method,
State = GetState(deserializedInvocation),
ParameterValues =
deserializedInvocation.Args.Select(
_ => new SerializerRespectingJRaw(_jsonSerializer, _))
.Cast<IJsonValue>()
.ToArray()
};
return secondPass;
}

private class HubInvocation
{
[JsonProperty("H")]
public string Hub { get; set; }

[JsonProperty("M")]
public string Method { get; set; }

[JsonProperty("I")]
public string Id { get; set; }

[JsonProperty("S")]
public JRaw State { get; set; }

[JsonProperty("A")]
public JRaw[] Args { get; set; }
}
}

现在一切就绪,您想要使用以下覆盖启动您的 SignalR 服务。容器是您向主机注册的任何 DI。在我的例子中,容器是 IUnityContainer 的一个实例。

        //Override the defauult json serializer behavior to follow our default settings instead.
container.RegisterInstance<IJsonSerializer>(
new JsonNetSerializer(Serialization.DefaultJsonSerializerSettings));
container.RegisterType<IHubRequestParser, PolymorphicHubRequestParser>();

关于.net - SignalR 中的多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19129875/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com