Skip to content

Instantly share code, notes, and snippets.

@dfch
Created March 17, 2017 23:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfch/884945aa6f9581e99ed4d7a351656749 to your computer and use it in GitHub Desktop.
Save dfch/884945aa6f9581e99ed4d7a351656749 to your computer and use it in GitHub Desktop.
Creating SignalR Hub Classes on the fly - http://d-fens.ch/2017/03/17/creating-signalr-hub-classes-on-the-fly/ - d-fens.ch
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.AspNet.SignalR.Infrastructure;
namespace Net.Appclusive.WebApi.SignalR
{
public class HubFactory<THubBase, THubClient>
where THubBase : Hub<THubClient>
where THubClient : class
{
private const string NAMESPACE_SEPARATOR = ".";
private const string FILE_EXTENSION = ".dll";
private readonly bool useTypeFullNameAsHubName;
private IDictionary<string, Type> typeMap;
public HubFactoryDependencyResolver DependencyResolver =>
new HubFactoryDependencyResolver
(
useTypeFullNameAsHubName
? typeMap
: typeMap.Values.ToDictionary(e => e.Name, e => e)
);
public HubFactory(ICollection<string> hubNames)
: this(hubNames, false)
{
// N/A
}
public HubFactory(ICollection<string> hubNames, bool useTypeFullNameAsHubName)
{
this.useTypeFullNameAsHubName = useTypeFullNameAsHubName;
CreateHubTypes(hubNames);
}
private void CreateHubTypes(ICollection<string> hubNames)
{
Contract.Requires(null != hubNames);
typeMap = new Dictionary<string, Type>();
var path = Path.GetTempPath();
var assemblyName = string.Concat(GetType().Namespace, NAMESPACE_SEPARATOR, Path.GetRandomFileName());
var assemblyFileName = assemblyName + FILE_EXTENSION;
var asmName = new AssemblyName(assemblyName);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave, path);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName);
foreach (var hubName in hubNames)
{
var typeBuilder = moduleBuilder.DefineType(hubName, TypeAttributes.Public, typeof(THubBase));
var type = typeBuilder.CreateType();
typeMap.Add(hubName, type);
}
assemblyBuilder.Save(assemblyFileName);
}
public IDictionary<string, IHubContext<THubClient>> GetHubContextMap()
{
Contract.Ensures(null != Contract.Result<IDictionary<string, IHubContext<THubClient>>>());
const int METHOD_GENERIC_ARGUMENTS_COUNT = 2;
var result = new Dictionary<string, IHubContext<THubClient>>();
var methodInfos = GlobalHost.ConnectionManager.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
var genericMethodInfoGetHubContext = methodInfos.FirstOrDefault(e =>
e.IsGenericMethod
&& e.ContainsGenericParameters
&& METHOD_GENERIC_ARGUMENTS_COUNT == e.GetGenericArguments().Length
&& typeof(IHub).IsAssignableFrom(e.GetGenericArguments().First())
&& e.Name == nameof(ConnectionManager.GetHubContext));
Contract.Assert(null != genericMethodInfoGetHubContext);
foreach (var kvp in typeMap)
{
var hubName = kvp.Key;
var hubType = kvp.Value;
var methodInfo = genericMethodInfoGetHubContext.MakeGenericMethod(hubType, typeof(THubClient));
var hubContext = (IHubContext<THubClient>) methodInfo.Invoke(GlobalHost.ConnectionManager, null);
result.Add(hubName, hubContext);
}
return result;
}
public class HubFactoryDependencyResolver : DefaultDependencyResolver
{
private readonly IDictionary<string, Type> typeMap;
internal HubFactoryDependencyResolver(IDictionary<string, Type> typeMap)
{
Contract.Requires(null != typeMap);
this.typeMap = typeMap;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var services = base.GetServices(serviceType).ToList();
if (typeof(IHubDescriptorProvider).IsAssignableFrom(serviceType))
{
services.Add(new HubFactoryHubDescriptorProvider(typeMap));
}
return services;
}
public class HubFactoryHubDescriptorProvider : IHubDescriptorProvider
{
private readonly IDictionary<string, Type> typeMap;
public HubFactoryHubDescriptorProvider(IDictionary<string, Type> typeMap)
{
Contract.Requires(null != typeMap);
this.typeMap = typeMap;
}
public IList<HubDescriptor> GetHubs()
{
return typeMap
.Select(e => new HubDescriptor
{
Name = e.Key,
NameSpecified = true,
HubType = e.Value
})
.ToList();
}
public bool TryGetHub(string hubName, out HubDescriptor descriptor)
{
if (!typeMap.ContainsKey(hubName))
{
descriptor = default(HubDescriptor);
return false;
}
descriptor = new HubDescriptor
{
Name = hubName,
NameSpecified = true,
HubType = typeMap[hubName]
};
return true;
}
}
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using biz.dfch.CS.Commons.Diagnostics;
using biz.dfch.CS.Commons.Linq;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Net.Appclusive.Public.Constants;
using Net.Appclusive.Public.SignalR;
using Owin;
namespace Net.Appclusive.WebApi.SignalR
{
public static class SignalRConfiguration
{
public static IDictionary<string, IHubContext<IWorkerClient>> HubMap;
public static void Configure(IAppBuilder app)
{
Contract.Requires(null != app);
Logger.Get(Logging.TraceSourceName.WEB_API).TraceEvent(TraceEventType.Start, (int)Logging.EventId.Start, Message.SignalRConfiguration_Configure__START);
var connectionStringSettings = ConfigurationManager.ConnectionStrings[typeof(SignalRConfiguration).FullName];
Contract.Assert(null != connectionStringSettings);
GlobalHost.DependencyResolver.UseSqlServer(connectionStringSettings.ConnectionString);
var hubFactory = new HubFactory<WorkerHubBase, IWorkerClient>(Core.Messaging.Hub.Queues.Keys, true);
var hubConfiguration = new HubConfiguration
{
Resolver = hubFactory.DependencyResolver,
};
app.MapSignalR(hubConfiguration);
HubMap = hubFactory.GetHubContextMap();
Logger.Get(Logging.TraceSourceName.WEB_API).TraceEvent(TraceEventType.Stop, (int)Logging.EventId.Stop, Message.SignalRConfiguration_Configure__SUCCEEDED);
Sender.Initialise();
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Net.Appclusive.Public.SignalR;
namespace Net.Appclusive.WebApi.SignalR
{
public class WorkerHubBase : Hub<IWorkerClient>, IWorkerHub
{
public async void NotifyServer(string message)
{
await Task.Yield();
var x = message;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment