I'm trying to figure out how to do dependency injection in an Azure WebJob using a ServiceCollection from Microsoft.Extensions.DependencyInjection
E.g.:
services.AddTransient<IAdminUserLogsService, AdminUserLogsService>();
I can't quite figure out how to wire up this service collection into something that the WebJobs JobHostConfiguration.JobActivator can understand
My intention is to re-use the default service wiring I've setup with this method as per the default AspNet core Startup.cs way.
Still wasn't able to find much after searching around last night.
But after a bit of fiddling, I managed to get something working with the following:
EDIT: I've added a more complete solution with Entity Framework.
I should note that my ASP.Net Core webapp is built upon 4.6.2 instead of pure core.
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.ServiceBus;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
namespace Settlements.WebJob
{
public class ServiceJobActivator : IJobActivator
{
IServiceProvider _serviceProvider;
public ServiceJobActivator(IServiceCollection serviceCollection) : base()
{
_serviceProvider = serviceCollection.BuildServiceProvider();
}
public T CreateInstance<T>()
{
return _serviceProvider.GetRequiredService<T>();
}
}
class Program
{
static void Main()
{
var config = new JobHostConfiguration();
var dbConnectionString = Properties.Settings.Default.DefaultConnection;
var serviceCollection = new ServiceCollection();
// wire up your services
serviceCollection.AddTransient<IThing, Thing>();
// important! wire up your actual jobs, too
serviceCollection.AddTransient<ServiceBusJobListener>();
// added example to connect EF
serviceCollection.AddDbContext<DbContext>(options =>
options.UseSqlServer(dbConnectionString ));
// add it to a JobHostConfiguration
config.JobActivator = new ServiceJobActivator(serviceCollection);
var host = new JobHost(config);
host.RunAndBlock();
}
}
}
Related
I'm pretty sure this is a DI configuration question specific to the AWS Lambda project template in VS.
I have a .NET 6 solution with multiple projects (Clean Architecture). For the purposes of this question, there are two projects:
ProjectName.Lambdas.Aggregator - based on the AWS Lambda template in Visual Studio. References ProjectProjectName.Infrastructure.
ProjectProjectName.Infrastructure - Holds all of the EF references, context class, entities, etc.
The function entrypoint triggers the DI configuration.
I'll paste relevant code below (any code not related to this question has been removed).
My question is: When I run dotnet ef migrations add InitialMigration (I'm setting the project of the migration to the Infrastructure project and the startup project to my Lambda function) I get the following error:
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOptions' while attempting to activate 'SolutionName.ProjectName.Infrastructure.Persistence.Relational.Postgres.ApplicationContext'
What I think is happening is that because this is an AWS Lambda project, the FunctionHandler entry point (whose constructor initializes the DI container) is not called during a migration, therefore it has no idea how to inject DbContextOptions.
How do I get migrations to work in this setup?
Startup.cs
using SolutionName.ProjectName.Infrastructure.Persistence.Relational.Postgres;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace SolutionName.ProjectName.SessionAggregator;
public class Startup
{
private readonly IConfigurationRoot _configuration;
public Startup()
{
_configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}
public IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddDbContext<ApplicationContext>(options => options.UseNpgsql(_configuration.GetConnectionString("ApplicationContext")));
IServiceProvider provider = services.BuildServiceProvider();
return provider;
}
}
Function.cs
using Amazon.Lambda.Core;
using Amazon.Lambda.SQSEvents;
using SolutionName.ProjectName.Infrastructure.Persistence.Relational.Postgres;
using Microsoft.Extensions.DependencyInjection;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace SolutionName.ProjectName.SessionAggregator;
public class Function
{
private readonly ApplicationContext _context;
public Function()
{
var startup = new Startup();
IServiceProvider provider = startup.ConfigureServices();
_context = provider.GetRequiredService<ApplicationContext>();
}
public async Task FunctionHandler(SQSEvent evnt, ILambdaContext context)
{
foreach (var message in evnt.Records)
{
await ProcessMessageAsync(message, context);
}
}
private async Task ProcessMessageAsync(SQSEvent.SQSMessage message, ILambdaContext context)
{
context.Logger.LogInformation($"Processed message {message.Body}");
// TODO: Do interesting work based on the new message
await Task.CompletedTask;
}
}
ApplicationContext.cs
using SolutionName.ProjectName.Infrastructure.Persistence.Relational.Postgres.Entities;
using Microsoft.EntityFrameworkCore;
namespace SolutionName.ProjectName.Infrastructure.Persistence.Relational.Postgres;
public class ApplicationContext : DbContext
{
public ApplicationContext(DbContextOptions options) : base(options) { }
public DbSet<SessionEntity> Sessions { get; set; }
}
So I found a series of blog posts that answered my question. It's a bit too involved to summarize here. I'm going to post links to the three blog posts, I guess a moderator can delete this question if/when the blog posts go down:
Part 1: https://blog.tonysneed.com/2018/12/16/add-net-core-di-and-config-goodness-to-aws-lambda-functions/
Part 2: https://blog.tonysneed.com/2018/12/20/idesigntimedbcontextfactory-and-dependency-injection-a-love-story/
Part 3: https://blog.tonysneed.com/2018/12/21/use-ef-core-with-aws-lambda-functions/
According to this tutorial I should:
Register your context with dependency injection
The Tutorial describes that I should locate the method
ConfigureServices() and put in there the code advised.
Here's my startup.cs:
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(MyProject.Startup))]
namespace MyProject
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
so I don't know where to correctly put the code.
Because the project isn't compatible with .net core 2.1, it was needed to change Project->Property to .Net Framework 4.6.1
and install packages Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.Tools
I tried to add the dependency injection to global.asax.cs file as follows:
protected void Application_Start()
{
var services = new ServiceCollection();
ConfigureServices(services);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void ConfigureServices(IServiceCollection services)
{
var connection = #"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0";
services.AddDbContext<BloggingContext>(options => options.UseSqlServer(connection));
}
I succeeded with the step and created the controller and it works, but I haven't chosen the right context (BloggingContext), so it created second database. So, I need to create a controller with BloggingContext, do you know how?
The version of the shown startup and the tutorial are in conflict with each other.
If this if for an Asp.Net Core MVC application then you can add the method your self. Startup class is part of the convention.
public partial class Startup {
//...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//...
}
}
Reference App startup in ASP.NET Core
The ConfigureServices method
The ConfigureServices method is:
Optional.
Called by the host before the Configure method to
configure the app's services.
Where configuration options are set by convention.
If however you are trying to use .Net Core technologies in an Asp.Net MVC 5+ (which is what the GitHub project targets) then you will need to modify your approach to adapt to using .Net Core Dependency Injection with a non core platform.
First you will need a IDependencyResolver which is the DI framework used by that version of Asp.Net MVC, and a way to replace the default resolver with your own.
public sealed class CoreDependencyResolver : System.Web.Mvc.IDependencyResolver {
private readonly System.Web.Mvc.IDependencyResolver mvcInnerResolver;
private readonly IServiceProvider serviceProvider;
public CoreDependencyResolver(IServiceProvider serviceProvider, System.Web.Mvc.IDependencyResolver dependencyResolver) {
this.serviceProvider = serviceProvider;
mvcInnerResolver = dependencyResolver;
}
public object GetService(Type serviceType) {
object result = this.serviceProvider.GetService(serviceType);
if (result == null && mvcInnerResolver != null)
result = mvcInnerResolver.GetService(serviceType);
return result;
}
public IEnumerable<object> GetServices(Type serviceType) {
IEnumerable<object> result = this.serviceProvider.GetServices(serviceType);
if (result == null && mvcInnerResolver != null)
result = mvcInnerResolver.GetServices(serviceType);
return result ?? new object[0];
}
}
With the custom resolver in place, you can now configure the application to use it.
Using your current example as a starting point (review comments)
protected void Application_Start() {
var services = new ServiceCollection();
ConfigureServices(services);
//build service provider
IServiceProvider provider = services.BuildServiceProvider();
//Get the current resolver used by MVC
var current = DependencyResolver.Current;
//use that and the provider to create your custom resolver
var resolver = new CoreDependencyResolver(provider, current);
//now set the MVC framework to use the resolver that wraps the service provider
//that was created from .Net Core Dependency Injection framework.
DependencyResolver.SetResolver(resolver);
//...
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void ConfigureServices(IServiceCollection services) {
//... omitted for brevity (register dependencies as normal)
}
Here I am using Oracle, but you could do the same with SQL Server...
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkOracle()
.AddDbContext<OracleDbContext>(builder => builder.UseOracle(Configuration["Data:OracleDbContext"]),ServiceLifetime.Scoped)
.AddDbContext<AppsDbContext>(option => option.UseOracle(Configuration["Data:AppsDbConnection:ConnectionString"]), ServiceLifetime.Scoped);
Then in my appsettings.json, I include the connection strings...
"Data": {
"OracleDbContext": "your connection string" },
"AppsDbContext": "your connection string" }
}
It appears you're using .Net Framework, rather than .Net Core.
2 simple ideas here:
Injecting DbContext into service layer: this layer will be class Library for .Net Framework or (use .Net Standard Class library if your platform is .Net Core). This thread shows you how to perform it: Injecting DbContext into service layer
use Ninject as dependency injector if you're on .Net Framework platform. This thread shows a good example: How to handle DBContext when using Ninject
Whilst using .NET Core tooling with full framework works quite well, if you have to use MVC5 and full framework I would not try to work it that way round.
There are many .NET 4.6.1 dependency injection frameworks, in this example I will use Autofac.
Install the NuGet packages Autofac and Autofac.Mvc5.
Add an AutofacRegistration.cs class to the App_Start folder
In the Application_Start() method in Global.asax add the line AutofacRegistration.BuildContainer();
Your AutofacRegistration class is where you wire up all your dependencies for dependency injection. The full docs are here https://autofaccn.readthedocs.io/en/latest/integration/mvc.html
public class AutofacRegistration
{
public static void BuildContainer()
{
var builder = new ContainerBuilder();
// Register your MVC controllers
builder.RegisterControllers(typeof(MvcApplication).Assembly);
// Now grab your connection string and wire up your db context
var conn = ConfigurationManager.ConnectionStrings["BloggingContext"];
builder.Register(c => new BloggingContext(conn));
// You can register any other dependencies here
// Set the dependency resolver to be Autofac.
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
This is assuming your BloggingContext has a constructor that takes the connection string as a parameter and passes it to the base class. Something like
public class BloggingContext : DbContext
{
public BloggingContext(string connectionString) : base(connectionString)
{
}
}
There is loads more in the documentation about scope etc that is worth reading but this should be the nuts and bolts of it.
I am having an issue registering a singleton using Microsoft.Extensions.DependencyInjection. I have created a Startup class (which is definitely working) and created an ITestService with implementation.
The singleton is injected into the function's constructor. Initially I had no issues with this implementation, however, a couple days later the function fails because it can't resolve ITestService. I have no clue why.
here is the Startup class
using Microsoft.Azure.WebJobs.Hosting;
[assembly: WebJobsStartup(typeof(Startup))]
namespace Test.Functions
{
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("local.settings.json", true, true)
.AddEnvironmentVariables()
.Build();
builder.Services.AddSingleton<ITestService>(new TestService("test string"));
}
}
}
And here is the function
public class TestFunction
{
private readonly ITestService testService
public TestFunction(ITestService testService)
{
this.testService = testService ?? throw new ArgumentNullException(nameof(testService));
}
[FunctionName(nameof(TestFunction))]
public void Run([ServiceBusTrigger("test", Connection = "ServiceBusConnection")]Message message, ILogger log)
{
////use test service here
}
}
When I debug startup and look at Services I see that the implementation type is null for ITestService, which I assume is why it won't resolve. Like I mentioned this totally worked for a couple days. The version of functions etc have not changed. Any ideas how to get this working again would be greatly appreciated.
update
I tried to simplify this even further and created another dummy interface with an implementation that has a parameter-less constructor. I added it using:
builder.AddSingleton<ITestService2, TestService2>()
It obviously assigned the type to the implementation type, but when it came time to inject it into the constructor it failed with the same can't activate exception.
There's a regression in the latest version of the function host that has broken Dependency Injection.
In order to work around this in an Azure environment, you can lock down the specific version of the functions host by setting the FUNCTIONS_EXTENSION_VERSION application setting to 2.0.12342.0.
If you're running the function host locally using the azure-functions-core-tools NPM package, be sure to use 2.4.419 as the latest version (2.4.498) results in the same issue. You can install that explicitly with the following:
npm i -g azure-functions-core-tools#2.4.419
See this GitHub issue for more background.
Try this in your code:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITestService, TestService>();
}
Have a go with this
builder.Services.AddSingleton<ITestService>(s => new TestService("test string"));
This uses the IServiceProvider in order to provide the string parameter to the constructor.
EDIT :
Try Changing your code to the below and installing Willezone.Azure.WebJobs.Extensions.DependencyInjection
This adds the extension method AddDependencyInjection and allows you to do the traditional ConfigureServices method call in a net core app startup.
using Microsoft.Azure.WebJobs.Hosting;
[assembly: WebJobsStartup(typeof(Startup))]
namespace Test.Functions
{
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("local.settings.json", true, true)
.AddEnvironmentVariables()
.AddDependencyInjection(ConfigureServices)
.Build();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITestService>(s => new TestService("test string"));
}
}
}
How do people go about Unit Testing their Startup.cs classes in a .NET Core 2 application? All of the functionality seems to be provided by Static extensions methods which aren't mockable?
If you take this ConfigureServices method for example:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BlogContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
How can I write tests to ensure that AddDbContext(...) & AddMvc() are called - the choice of implementing all of this functionality via extension methods seems to have made it untestable?
Well yes, if you want to check the fact that extension method AddDbContext was called on services you are in trouble.
The good thing is that you shouldn't actually check exactly this fact.
Startup class is an application composition root. And when testing a composition root you want to check that it actually registers all dependencies required for instantiation of the root objects (controllers in the case of ASP.NET Core application).
Say you have following controller:
public class TestController : Controller
{
public TestController(ISomeDependency dependency)
{
}
}
You could try checking whether Startup has registered the type for ISomeDependency. But implementation of ISomeDependency could also require some other dependencies that you should check.
Eventually you end up with a test that has tons of checks for different dependencies but it does not actually guarantee that object resolution will not throw missing dependency exception. There is not too much value in such a test.
An approach that works well for me when testing a composition root is to use real dependency injection container. Then I call a composition root on it and assert that resolution of the root object does not throw.
It could not be considered as pure Unit Test because we use other non-stubbed class. But such tests, unlike other integration tests, are fast and stable. And most important they bring the value of valid check for correct dependencies registration. If such test passes you could be sure that object will also be correctly instantiated in the product.
Here is a sample of such test:
[TestMethod]
public void ConfigureServices_RegistersDependenciesCorrectly()
{
// Arrange
// Setting up the stuff required for Configuration.GetConnectionString("DefaultConnection")
Mock<IConfigurationSection> configurationSectionStub = new Mock<IConfigurationSection>();
configurationSectionStub.Setup(x => x["DefaultConnection"]).Returns("TestConnectionString");
Mock<Microsoft.Extensions.Configuration.IConfiguration> configurationStub = new Mock<Microsoft.Extensions.Configuration.IConfiguration>();
configurationStub.Setup(x => x.GetSection("ConnectionStrings")).Returns(configurationSectionStub.Object);
IServiceCollection services = new ServiceCollection();
var target = new Startup(configurationStub.Object);
// Act
target.ConfigureServices(services);
// Mimic internal asp.net core logic.
services.AddTransient<TestController>();
// Assert
var serviceProvider = services.BuildServiceProvider();
var controller = serviceProvider.GetService<TestController>();
Assert.IsNotNull(controller);
}
I also had a similar problem, but managed to get around that by using the WebHost in AspNetCore and essentially re-creating what program.cs does, and then Asserting that all of my services exist and are not null. You could go a step further and execute specific extensions for IServices with .ConfigureServices or actually perform operations with the services you created to make sure they were constructed properly.
One key, is I created a unit test startup class that inherits from the startup class I'm testing so that I don't have to worry about separate assemblies. You could use composition if you prefer to not use inheritance.
[TestClass]
public class StartupTests
{
[TestMethod]
public void StartupTest()
{
var webHost = Microsoft.AspNetCore.WebHost.CreateDefaultBuilder().UseStartup<Startup>().Build();
Assert.IsNotNull(webHost);
Assert.IsNotNull(webHost.Services.GetRequiredService<IService1>());
Assert.IsNotNull(webHost.Services.GetRequiredService<IService2>());
}
}
public class Startup : MyStartup
{
public Startup(IConfiguration config) : base(config) { }
}
This approach works, and uses the real MVC pipeline, as things should only be mocked if you need to change how they work.
public void AddTransactionLoggingCreatesConnection()
{
var servCollection = new ServiceCollection();
//Add any injection stuff you need here
//servCollection.AddSingleton(logger.Object);
//Setup the MVC builder thats needed
IMvcBuilder mvcBuilder = new MvcBuilder(servCollection, new Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager());
IEnumerable<KeyValuePair<string, string>> confValues = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("TransactionLogging:Enabled", "True"),
new KeyValuePair<string, string>("TransactionLogging:Uri", "https://api.something.com/"),
new KeyValuePair<string, string>("TransactionLogging:Version", "1"),
new KeyValuePair<string, string>("TransactionLogging:Queue:Enabled", "True")
};
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddInMemoryCollection(confValues);
var confRoot = builder.Build();
StartupExtensions.YourExtensionMethod(mvcBuilder); // Any other params
}
As an alternative approach to #datchung's answer with ASP.net Core 6 (or 7) Minimal start-up, it's possible to leverage WebApplicationFactory<T> to run startup. Note that this requires defining InternalsVisibleTo from API to test project for the Program reference to be accessible.
Sample test, using xUnit:
[Fact]
public void StartupTest()
{
var waf = new WebApplicationFactory<Program>();
var server = waf.Server;
// Optional: check for individual services
var myService = server.Services.GetService<IMyService>();
Assert.NotNull(myService);
}
The .Server call there triggers the test server and ServiceCollection build. That, in turn, triggers validation unless "ValidateOnBuild" option has been turned off.
More about WAF internals in here: https://andrewlock.net/exploring-dotnet-6-part-6-supporting-integration-tests-with-webapplicationfactory-in-dotnet-6/
All of this does require that your Startup code works in test scenario (it shouldn't connect to online services etc.) but that is also useful for integration testing too (e.g. Alba).
In my case, I'm using .NET 6 with the minimal API (no Startup class).
My Program.cs originally looked like this:
// using statements
...
var builder = WebApplication.CreateBuilder(args);
...
builder.services.AddSingleton<IMyInterface, MyImplementation>();
...
I added StartupHelper.cs:
public class StartupHelper
{
private readonly IServiceCollection _services;
public StartupHelper(IServiceCollection services)
{
_services = services;
}
public void SetUpServices()
{
_services.AddSingleton<IMyInterface, MyImplementation>();
}
}
I used StartupHelper in Program.cs:
// using statements
...
var builder = WebApplication.CreateBuilder(args);
...
var startupHelper = new StartupHelper(builder.Services);
startupHelper.SetUpServices();
...
And my test (NUnit) looks like this:
[Test]
public void SetUpServices()
{
var builder = WebApplication.CreateBuilder(new string[0]);
var startupHelper = new StartupHelper(builder.Services);
startupHelper.SetUpServices();
var app = builder.Build();
var myImplementation = app.Services.GetService<IMyInterface>();
Assert.NotNull(myImplementation);
Assert.IsTrue(myImplementation is MyImplementation);
}
You should be install to Xunit project then add startup.cs file in base directory .
I recently upgraded from NServiceBus 5x to 6.0.0-beta0004 to be able to host a ASP.NET Core application (whose main function is to listen to NServiceBus messages). I'm having problems with the startup of the host as the endpoint doesn't seem to subscribe to the publisher.
I am using the pubsub example to fix the problem. Apart from the projects that are in there by default, I added one extra project, a fresh ASP.NET Core project (based on the full .NET framework). I tried to use the exact same NServiceBus configuration, but instead of using the app/web.config, I am using the following configuration:
public class ConfigurationSource : IConfigurationSource
{
public T GetConfiguration<T>() where T : class, new()
{
UnicastBusConfig config = new UnicastBusConfig()
{
MessageEndpointMappings = new MessageEndpointMappingCollection()
};
var endpointMapping = new MessageEndpointMapping
{
AssemblyName = "Shared",
Endpoint = "Samples.PubSub.MyPublisher"
};
config.MessageEndpointMappings.Add(endpointMapping);
return config as T;
}
}
The Startup class was extended with the following code:
public IConfigurationRoot Configuration
{
get;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
services.AddSingleton(this.GetBus());
}
private IEndpointInstance GetBus()
{
LogManager.Use<DefaultFactory> ().Level(NServiceBus.Logging.LogLevel.Info);
var endpointConfiguration = new EndpointConfiguration("Samples.PubSub.Subscriber3");
endpointConfiguration.UseSerialization<JsonSerializer>();
endpointConfiguration.DisableFeature<AutoSubscribe>();
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.SendFailedMessagesTo("error");
endpointConfiguration.EnableInstallers();
// Skip web.config settings and use programmatic approach
endpointConfiguration.CustomConfigurationSource(new ConfigurationSource());
var endpointInstance = Endpoint.Start(endpointConfiguration).Result;
endpointInstance.Subscribe<IMyEvent>().Wait();
return endpointInstance;
}
The Message Handler is identical to the other Subscriber projects in the solution:
public class EventMessageHandler : IHandleMessages<IMyEvent>
{
static ILog log = LogManager.GetLogger<EventMessageHandler>();
public Task Handle(IMyEvent message, IMessageHandlerContext context)
{
log.Info($"Subscriber 2 received IEvent with Id {message.EventId}.");
log.Info($"Message time: {message.Time}.");
log.Info($"Message duration: {message.Duration}.");
return Task.FromResult(0);
}
}
If I run the sample, I notice Subscriber1 and Subscriber2 are subscribed perfectly and they receive messages if I execute some of the commands in the Publisher console application. However Subscriber3 doesn't appear to be doing anything. No exceptions are thrown and in this sample I don't seem to find any relevant log information that could lead to misconfiguration.
Has anyone tried a similar setup with ASP.NET Core & NServiceBus 6 and if so, what should I do next?
Update 04/04/2017
There are some updates that provide some interesting insights:
https://groups.google.com/forum/#!topic/particularsoftware/AVrA1E-VHtk
https://particular.net/blog/nservicebus-on-net-core-why-not