in src/StreamJsonRpc/Reflection/RpcTargetInfo.cs [188:277]
internal IDisposable? AddLocalRpcTarget(Type exposingMembersOn, object target, JsonRpcTargetOptions? options, bool requestRevertOption)
{
RevertAddLocalRpcTarget? revert = requestRevertOption ? new RevertAddLocalRpcTarget(this) : null;
options = options ?? JsonRpcTargetOptions.Default;
IReadOnlyDictionary<string, List<MethodSignature>> mapping = GetRequestMethodToClrMethodMap(exposingMembersOn.GetTypeInfo(), options.AllowNonPublicInvocation, options.UseSingleObjectParameterDeserialization, options.ClientRequiresNamedArguments);
lock (this.SyncObject)
{
foreach (KeyValuePair<string, List<MethodSignature>> item in mapping)
{
string rpcMethodName = options.MethodNameTransform is not null ? options.MethodNameTransform(item.Key) : item.Key;
Requires.Argument(rpcMethodName is not null, nameof(options), nameof(JsonRpcTargetOptions.MethodNameTransform) + " delegate returned a value that is not a legal RPC method name.");
bool alreadyExists = this.targetRequestMethodToClrMethodMap.TryGetValue(rpcMethodName, out List<MethodSignatureAndTarget>? existingList);
if (!alreadyExists)
{
this.targetRequestMethodToClrMethodMap.Add(rpcMethodName, existingList = new List<MethodSignatureAndTarget>());
}
// Only add methods that do not have equivalent signatures to what we already have.
foreach (MethodSignature newMethod in item.Value)
{
if (!alreadyExists || !existingList.Any(e => e.Equals(newMethod)))
{
var signatureAndTarget = new MethodSignatureAndTarget(newMethod, target, null);
this.TraceLocalMethodAdded(rpcMethodName, signatureAndTarget);
revert?.RecordMethodAdded(rpcMethodName, signatureAndTarget);
existingList!.Add(signatureAndTarget);
}
else
{
if (this.TraceSource.Switch.ShouldTrace(TraceEventType.Information))
{
this.TraceSource.TraceEvent(TraceEventType.Information, (int)JsonRpc.TraceEvents.LocalMethodAdded, "Skipping local RPC method \"{0}\" -> {1} because a method with a colliding signature has already been added.", rpcMethodName, newMethod);
}
}
}
}
if (options.NotifyClientOfEvents)
{
HashSet<string>? eventsDiscovered = null;
for (TypeInfo? t = exposingMembersOn.GetTypeInfo(); t is not null && t != typeof(object).GetTypeInfo(); t = t.BaseType?.GetTypeInfo())
{
foreach (EventInfo evt in t.DeclaredEvents)
{
if (evt.AddMethod is object && (evt.AddMethod.IsPublic || exposingMembersOn.IsInterface) && !evt.AddMethod.IsStatic)
{
if (this.eventReceivers is null)
{
this.eventReceivers = new List<EventReceiver>();
}
if (eventsDiscovered is null)
{
eventsDiscovered = new HashSet<string>(StringComparer.Ordinal);
}
if (!eventsDiscovered.Add(evt.Name))
{
// Do not add the same event again. It can appear multiple times in a type hierarchy.
continue;
}
if (this.TraceSource.Switch.ShouldTrace(TraceEventType.Information))
{
this.TraceSource.TraceEvent(TraceEventType.Information, (int)JsonRpc.TraceEvents.LocalEventListenerAdded, "Listening for events from {0}.{1} to raise notification.", target.GetType().FullName, evt.Name);
}
var eventReceiver = new EventReceiver(this.jsonRpc, target, evt, options);
revert?.RecordEventReceiver(eventReceiver);
this.eventReceivers.Add(eventReceiver);
}
}
}
}
if (options.DisposeOnDisconnect)
{
if (this.localTargetObjectsToDispose is null)
{
this.localTargetObjectsToDispose = new List<object>();
}
revert?.RecordObjectToDispose(target);
this.localTargetObjectsToDispose.Add(target);
}
}
return revert;
}