Skip to content

Commit

Permalink
Merge pull request #231 from Cysharp/hotfix/DI
Browse files Browse the repository at this point in the history
Add support for IServiceLocator / IServiceProvider Scope.
  • Loading branch information
mayuki authored Dec 5, 2019
2 parents d41a104 + bbf56c2 commit e3ef908
Show file tree
Hide file tree
Showing 18 changed files with 504 additions and 105 deletions.
81 changes: 75 additions & 6 deletions sandbox/Sandbox.Hosting/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace Sandbox.Hosting
Expand All @@ -31,6 +34,10 @@ static async Task Main(string[] args)
.UseMagicOnion(configurationName: "MagicOnion-Management", types: new[] { typeof(ManagementService) })
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<MySingletonService>();
services.AddScoped<MyScopedService>();
services.AddTransient<MyTransientService>();

services.Configure<MagicOnionHostingOptions>(options =>
{
if (hostContext.HostingEnvironment.IsDevelopment())
Expand All @@ -53,17 +60,30 @@ static async Task Main(string[] args)
services.Configure<MagicOnionHostingOptions>("MagicOnion-Management", options =>
{
});

services.AddHostedService<MyHostedService>();
})
.RunConsoleAsync();

//var clientMyService = MagicOnionClient.Create<IMyService>(new Channel("localhost", 12345, creds));
var clientManagementService = MagicOnionClient.Create<IManagementService>(new Channel("localhost", 23456, creds));
var result = await clientMyService.HelloAsync();
result = await clientMyService.HelloAsync();
result = await clientMyService.HelloAsync();
var result2 = await clientManagementService.FooBarAsync();

var clientHub = StreamingHubClient.Connect<IMyHub, IMyHubReceiver>(new Channel("localhost", 12345, creds), new MyHubReceiver());
var result3 = await clientHub.HelloAsync();

var streamingHubClient = StreamingHubClient.Connect<IMyHub, IMyHubReceiver>(new Channel("localhost", 12345, creds), null);
await streamingHubClient.HelloAsync();

var streamingHubClient2 = StreamingHubClient.Connect<IMyHub, IMyHubReceiver>(new Channel("localhost", 12345, creds), null);
await streamingHubClient2.HelloAsync();
await streamingHubClient2.HelloAsync();
await streamingHubClient2.HelloAsync();
await streamingHubClient2.DisposeAsync();

await hostTask;
}

Expand All @@ -76,6 +96,41 @@ public void OnNantoka(string value)
}
}

public class MyHostedService : IHostedService
{
public MyHostedService(MySingletonService service)
{

}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}

public class MySingletonService : IDisposable
{
public MySingletonService() => Console.WriteLine($"{this.GetType().Name}..ctor");
public void Dispose() => Console.WriteLine($"{this.GetType().Name}.Dispose");
}

public class MyTransientService : IDisposable
{
public MyTransientService() => Console.WriteLine($"{this.GetType().Name}..ctor");
public void Dispose() => Console.WriteLine($"{this.GetType().Name}.Dispose");
}

public class MyScopedService : IDisposable
{
public MyScopedService() => Console.WriteLine($"{this.GetType().Name}..ctor");
public void Dispose() => Console.WriteLine($"{this.GetType().Name}.Dispose");
}

public class MyStreamingHubFilterAttribute : StreamingHubFilterAttribute
{
private readonly ILogger _logger;
Expand Down Expand Up @@ -105,6 +160,16 @@ public MyFilterAttribute(ILogger<MyFilterAttribute> logger)

public override async ValueTask Invoke(ServiceContext context, Func<ServiceContext, ValueTask> next)
{
context.ServiceLocator.GetService<MyTransientService>();
context.ServiceLocator.GetService<MyTransientService>();
context.ServiceLocator.GetService<MyTransientService>();
context.ServiceLocator.GetService<MySingletonService>();
context.ServiceLocator.GetService<MySingletonService>();
context.ServiceLocator.GetService<MySingletonService>();
context.ServiceLocator.GetService<MyScopedService>();
context.ServiceLocator.GetService<MyScopedService>();
context.ServiceLocator.GetService<MyScopedService>();

_logger.LogInformation($"MyFilter Begin: {context.CallContext.Method}");
await next(context);
_logger.LogInformation($"MyFilter End: {context.CallContext.Method}");
Expand Down Expand Up @@ -164,9 +229,8 @@ public interface IMyService : IService<IMyService>
}
public class MyService : ServiceBase<IMyService>, IMyService
{
public MyService()
public MyService(MySingletonService foo, MyScopedService bar)
{

}
[MyFilterUsingFactoryAttribute("PerMethod")]
public async UnaryResult<string> HelloAsync()
Expand All @@ -187,18 +251,23 @@ public async UnaryResult<int> FooBarAsync()
}
}

public interface IMyHub : IStreamingHub<IMyHub, IMyHubReceiver>
public interface IMyHubReceiver
{
Task<string> HelloAsync();
void OnNantoka(string value);
}

public interface IMyHubReceiver
public interface IMyHub : IStreamingHub<IMyHub, IMyHubReceiver>
{
void OnNantoka(string value);
Task<string> HelloAsync();
}

public class MyHub : StreamingHubBase<IMyHub, IMyHubReceiver>, IMyHub
{
public MyHub(MyScopedService scopedService)
{ }

public MyHub() => Console.WriteLine($"{this.GetType().Name}..ctor");

public async Task<string> HelloAsync()
{
var group = await this.Group.AddAsync("Nantoka");
Expand Down
7 changes: 4 additions & 3 deletions src/MagicOnion.Hosting/MagicOnionServerServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace MagicOnion.Hosting
{
Expand Down Expand Up @@ -161,15 +162,15 @@ private static void AddMagicOnionHostedServiceDefinition(
{
if (services.Any(x => (x.ImplementationInstance as MagicOnionHostedServiceDefinition)?.Name == configurationName))
{
throw new InvalidOperationException($"MagicOnion ServiceDefinition for '{configurationName}' is already registerd.");
throw new InvalidOperationException($"MagicOnion ServiceDefinition for '{configurationName}' is already registered.");
}

services.AddSingleton<MagicOnionHostedServiceDefinition>(serviceProvider =>
{
var hostingOptions = serviceProvider.GetService<IOptionsMonitor<MagicOnionHostingOptions>>().Get(configurationName);
var options = hostingOptions.Service;
var serviceLocator = new ServiceLocatorBridge(services);
options.ServiceLocator = serviceLocator;
options.ServiceLocator = new MicrosoftExtensionsServiceLocator(serviceProvider, options);
options.MagicOnionServiceActivator = new MicrosoftExtensionsMagicOnionServiceActivator();

// Build a MagicOnion ServiceDefinition from assemblies/types.
MagicOnionServiceDefinition serviceDefinition;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using MagicOnion.Server;
using Microsoft.Extensions.DependencyInjection;

namespace MagicOnion.Hosting
{
public class MicrosoftExtensionsMagicOnionServiceActivator : IMagicOnionServiceActivator
{
public T Create<T>(IServiceLocator serviceLocator)
{
return ActivatorUtilities.GetServiceOrCreateInstance<T>((IServiceProvider)serviceLocator);
}
}

}
63 changes: 63 additions & 0 deletions src/MagicOnion.Hosting/MicrosoftExtensionsServiceLocator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using MagicOnion.Server;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace MagicOnion.Hosting
{
public class MicrosoftExtensionsServiceLocator : IServiceLocator, IServiceProvider
{
readonly IServiceProvider serviceProvider;

public MicrosoftExtensionsServiceLocator(IServiceProvider serviceProvider, MagicOnionOptions options)
{
this.serviceProvider = serviceProvider;
}

public IServiceLocatorScope CreateScope()
{
return new ServiceLocatorScope(serviceProvider.CreateScope());
}

public T GetService<T>()
{
return serviceProvider.GetService<T>();
}

object IServiceProvider.GetService(Type serviceType)
{
return serviceProvider.GetService(serviceType);
}

class ServiceLocatorScope : IServiceLocatorScope, IServiceLocator, IServiceProvider
{
readonly IServiceScope scope;

public IServiceLocator ServiceLocator => this;

public ServiceLocatorScope(IServiceScope scope)
{
this.scope = scope;
}

public void Dispose()
{
scope.Dispose();
}

object IServiceProvider.GetService(Type serviceType)
{
return scope.ServiceProvider.GetService(serviceType);
}

public T GetService<T>()
{
return scope.ServiceProvider.GetService<T>();
}

public IServiceLocatorScope CreateScope()
{
return new ServiceLocatorScope(scope.ServiceProvider.CreateScope());
}
}
}
}
39 changes: 0 additions & 39 deletions src/MagicOnion.Hosting/ServiceLocatorBridge.cs

This file was deleted.

8 changes: 3 additions & 5 deletions src/MagicOnion.Redis/RedisGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,15 @@ namespace MagicOnion.Redis
{
public class RedisGroupRepositoryFactory : IGroupRepositoryFactory
{
public IGroupRepository CreateRepository(IServiceLocator serviceLocator)
public IGroupRepository CreateRepository(IFormatterResolver formatterResolver, IMagicOnionLogger logger, IServiceLocator serviceLocator)
{
var resolver = serviceLocator.GetService<IFormatterResolver>();
var logger = serviceLocator.GetService<IMagicOnionLogger>();
var connection = serviceLocator.GetService<ConnectionMultiplexer>();
if (connection == null)
{
throw new InvalidOperationException("RedisGroup requires add ConnectionMultiplexer to MagicOnionOptions.ServiceLocator before create it. Please try new MagicOnionOptions{DefultServiceLocator.Register(new ConnectionMultiplexer)}");
throw new InvalidOperationException("RedisGroup requires add ConnectionMultiplexer to MagicOnionOptions.ServiceLocator before create it. Please try new MagicOnionOptions{DefaultServiceLocator.Register(new ConnectionMultiplexer)}");
}

return new RedisGroupRepository(resolver, connection, logger);
return new RedisGroupRepository(formatterResolver, connection, logger);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/MagicOnion/Server/Hubs/Group.ConcurrentDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ namespace MagicOnion.Server.Hubs
{
public class ConcurrentDictionaryGroupRepositoryFactory : IGroupRepositoryFactory
{
public IGroupRepository CreateRepository(IServiceLocator serviceLocator)
public IGroupRepository CreateRepository(IFormatterResolver formatterResolver, IMagicOnionLogger logger, IServiceLocator serviceLocator)
{
return new ConcurrentDictionaryGroupRepository(serviceLocator.GetService<IFormatterResolver>(), serviceLocator.GetService<IMagicOnionLogger>());
return new ConcurrentDictionaryGroupRepository(formatterResolver, logger);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/MagicOnion/Server/Hubs/Group.ImmutableArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ namespace MagicOnion.Server.Hubs
{
public class ImmutableArrayGroupRepositoryFactory : IGroupRepositoryFactory
{
public IGroupRepository CreateRepository(IServiceLocator serviceLocator)
public IGroupRepository CreateRepository(IFormatterResolver formatterResolver, IMagicOnionLogger logger, IServiceLocator serviceLocator)
{
return new ImmutableArrayGroupRepository(serviceLocator.GetService<IFormatterResolver>(), serviceLocator.GetService<IMagicOnionLogger>());
return new ImmutableArrayGroupRepository(formatterResolver, logger);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/MagicOnion/Server/Hubs/Group.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using MessagePack;

namespace MagicOnion.Server.Hubs
{
Expand All @@ -23,7 +24,7 @@ public IGroupRepositoryFactory Create()

public interface IGroupRepositoryFactory
{
IGroupRepository CreateRepository(IServiceLocator serviceLocator);
IGroupRepository CreateRepository(IFormatterResolver formatterResolver, IMagicOnionLogger logger, IServiceLocator serviceLocator);
}

public interface IGroupRepository
Expand Down
Loading

0 comments on commit e3ef908

Please sign in to comment.