I'm trying to build my first NserviceBus application, but I cannot get it to work. The message arrives on the "backend" but the handler uses DI and NServicebus claims it cannot find the service for the interface. But the code was lifted directly from the examples. I'm missing something and I cannot figure it out.
The Error:
dbug: NServiceBus.LoadHandlersConnector[0]
Processing message type: Messages.JobStartCommand
Message headers:
NServiceBus.MessageId : 0044f261-e3b3-4287-b6f0-ad7400ef43cb
NServiceBus.MessageIntent : Send
NServiceBus.ConversationId : bb4f276f-63fe-450b-b234-ad7400ef43cd
NServiceBus.CorrelationId : 0044f261-e3b3-4287-b6f0-ad7400ef43cb
NServiceBus.ReplyToAddress : ClientUI
NServiceBus.OriginatingMachine : L19002992
NServiceBus.OriginatingEndpoint : ClientUI
$.diagnostics.originating.hostid : e1fecb2b72b8185e47341bb4dfb37dd7
NServiceBus.ContentType : text/xml
NServiceBus.EnclosedMessageTypes : Messages.JobStartCommand, Messages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
NServiceBus.Version : 7.5.0
NServiceBus.TimeSent : 2021-07-29 14:31:08:228497 Z
NServiceBus.Retries : 3
NServiceBus.Retries.Timestamp : 2021-07-29 14:31:40:397277 Z
Handlers to invoke:
Extractor.JobStartHandler
Extractor.JobStartHandler
info: NServiceBus.RecoverabilityExecutor[0]
Immediate Retry is going to retry message '0044f261-e3b3-4287-b6f0-ad7400ef43cb' because of an exception:
System.InvalidOperationException: Unable to resolve service for type 'IExtract' while attempting to activate 'Extractor.JobStartHandler'.
at Microsoft.Extensi
My backend program.cs
using Microsoft.Extensions.Hosting;
using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NServiceBus;
using System.Threading.Tasks;
using Messages;
using Extractor;
namespace ExtractorOsiris
{
class Program
{
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
var builder = Host.CreateDefaultBuilder(args);
builder.UseWindowsService();
//builder.UseMicrosoftLogFactoryLogging();
builder.ConfigureLogging((ctx, logging) =>
{
logging.AddConfiguration(ctx.Configuration.GetSection("Logging"));
//logging.AddEventLog();
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Debug);
});
#region back-end-use-nservicebus
builder.UseNServiceBus(ctx =>
{
var endpointConfiguration = new EndpointConfiguration("Sample.Extractor");
endpointConfiguration.UseTransport<LearningTransport>();
var transport = endpointConfiguration.UseTransport<LearningTransport>();
transport.Routing().RouteToEndpoint(typeof(ProcessObjectCommand), "Sample.Processor");
endpointConfiguration.DefineCriticalErrorAction(OnCriticalError);
return endpointConfiguration;
});
#endregion
#region back-end-register-service
builder.ConfigureServices(services =>
{
services.AddSingleton<IExtract, ExtractOsiris>();
});
#endregion
return builder;
}
private static Task OnCriticalError(ICriticalErrorContext arg)
{
throw new NotImplementedException();
}
}
}
The Interface
using Newtonsoft.Json.Linq;
using NServiceBus;
using System;
using System.Threading.Tasks;
namespace Extractor
{
public interface IExtract
{
Task<JArray> Extract(string #object, DateTime deltaTime);
}
}
The handler
using System.Threading.Tasks;
using Messages;
using Newtonsoft.Json.Linq;
using NServiceBus;
namespace Extractor
{
#region back-end-handler
public class JobStartHandler : IHandleMessages<JobStartCommand>
{
private readonly IExtract extractor;
public JobStartHandler(IExtract extractor)
{
this.extractor = extractor;
}
public async Task Handle(JobStartCommand message, IMessageHandlerContext context)
{
Task<JArray> result = extractor.Extract("Medewerkers", message.DeltaTime);
await result;
JArray test = result.Result;
foreach (JObject x in test)
{
// send the object to be processed.
//await context.Send(new ProcessObjectCommand(x.ToString()));
}
}
}
#endregion
}
The implementation of the interface
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Extractor;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NServiceBus;
namespace ExtractorOsiris
{
public class ExtractOsiris : IExtract
{
private readonly ILogger logger;
public ExtractOsiris(ILogger<ExtractOsiris> logger)
{
this.logger = logger;
}
public Task<JArray> Extract(string #object, DateTime deltaTime)
{
logger.LogInformation($"getting {#object} for delta time {deltaTime}");
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://xx.yy");
WebResponse response = request.GetResponse();
JObject temp = JObject.Load(new JsonTextReader(new StreamReader(response.GetResponseStream())));
return Task<JArray>.FromResult(temp["items"] as JArray);
}
}
}
I know the code ain't pretty, but it's pure quick and dirty testing code.
I cleaned out the offending interface from the code and still got the error on an interface that was not even there anymore. I removed the bin folder and build everything back up step by step. Stupid thing is, source control says I am back where I started, without changes. And it just works....
So must have bin something stupid with VS2019. Sorry to anyone who's time I wasted
Related
I am trying to implement health checks in my blazor application. To do so, I have used the Microsoft.AspNetCore.Diagnostics.HealthChecks package among others. Below you can see sql and url health checks.
startup.cs
//using AjuaBlazorServerApp.Data;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AjuaBlazorServerApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHostedService<PeriodicExecutor>();
services.AddHealthChecks().AddUrlGroup(new Uri("https://api.example.com/post"),
name: "Example Endpoint",
failureStatus: HealthStatus.Degraded)
.AddSqlServer(Configuration["sqlString"],
healthQuery: "select 1",
failureStatus: HealthStatus.Degraded,
name: "SQL Server");
services.AddHealthChecksUI(opt =>
{
opt.SetEvaluationTimeInSeconds(5); //time in seconds between check
opt.MaximumHistoryEntriesPerEndpoint(60); //maximum history of checks
opt.SetApiMaxActiveRequests(1); //api requests concurrency
opt.AddHealthCheckEndpoint("Ajua API", "/api/health"); //map health check api
}).AddInMemoryStorage();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
endpoints.MapHealthChecks("/api/health", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.
WriteHealthCheckUIResponse
});
endpoints.MapHealthChecksUI();
});
}
}
}
The sql one works perfectly. However the url health check returns the following error:
Discover endpoint #0 is not responding with code in 200...299 range, the current status is MethodNotAllowed.
What i would like to know is if there is a way to maybe set the method type and if need be send some test details to the endpoint so that we can actually get a valid response.
AddUrlGroup has an overload that allows you to specify the method through the httpMethod parameter. Try using :
.AddUrlGroup(new Uri("https://api.example.com/post"),
httpMethod: HttpMethod.Post,
name: "Example Endpoint",
failureStatus: HealthStatus.Degraded)
Another overload allows configuring the HttpClient and HttpMessageHandler explicitly, to add specific default headers for example, enable compression or redirection.
.AddUrlGroup(new Uri("https://api.example.com/post"),
httpMethod: HttpMethod.Post,
name: "Example Endpoint",
configureClient: client => {
client.DefaultRequest.Headers.IfModifiedSince=
DateTimeOffset.Now.AddMinutes(-10);
},
failureStatus: HealthStatus.Degraded)
Yet another overload allows explicitly configuring the UriHealthCheckOptions class generated by other AddUrlGroup overloads:
.AddUrlGroup(uriOptions=>{
uriOptions
.UsePost()
.AddUri(someUrl,setup=>{
setup.AddCustomHeader("...","...");
});
});
There's no way to specify content headers because the health check code doesn't send a body.
I accepted the answer above because it technically answered my question. However, this is the implementation I ended up using. Basically you will have to create your own custom healthcheck.
Add a new folder under you projects main directory and name it accordingly
2. Create a new class in that folder and add code similar to what I have below
EndpointHealth.cs
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using Microsoft.Extensions.Configuration;
namespace BlazorServerApp.HealthChecks
{
public class EndpointHealth : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken =
default)
{
//create a json string of parameters and send it to the endpoint
var data = new
{
test = "Example",
};
string jsonString = JsonSerializer.Serialize(data);
var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://api.example.com/post");
httpWebRequest.ContentType = Configuration["application/json"];
httpWebRequest.Method = Configuration["POST"];
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write(jsonString);
}
//Get the endpoint result and use it to return the appropriate health check result
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
if (((int)httpResponse.StatusCode) >= 200 && ((int)httpResponse.StatusCode) < 300)
return Task.FromResult(HealthCheckResult.Healthy());
else
return Task.FromResult(HealthCheckResult.Unhealthy());
}
}
}
Then add the following code to the top of your startup.cs file using BlazorServerApp.HealthChecks;
and finally the below code:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHealthChecks()
.AddCheck<EndpointHealth>("Endpoint",null);
}
So I am building a template API project and am currently working on adding in some integration tests.
At the moment, when I run my test, for some reason it returns a JSON parsing error:
Newtonsoft.Json.JsonReaderException : Unexpected character encountered
while parsing value: A. Path '', line 0, position 0.
This looks like it's an issue with my endpoint, but it works fine when just running localhost with a normal startup routine, so I think it's because I can't get my fixture just right. especilaly because, when i try another fixture (listed below) it doesn't give me an error anymore and just hangs.
The test itself can be found here.
namespace Foundation.Api.Tests.IntegrationTests
{
using FluentAssertions;
using Foundation.Api.Data;
using Foundation.Api.Models;
using Foundation.Api.Tests.Fakes;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
public class GetValueToReplaceIntegrationTests : IClassFixture<CustomWebApplicationFactory<Startup>>
{
public GetValueToReplaceIntegrationTests(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
}
private readonly CustomWebApplicationFactory<Startup> _factory;
[Fact]
public async Task GetValueToReplaces_ReturnsSuccessCodeAndResourceWithAccurateFields()
{
var fakeValueToReplaceOne = new FakeValueToReplace { }.Generate();
var fakeValueToReplaceTwo = new FakeValueToReplace { }.Generate();
var appFactory = _factory;
using (var scope = appFactory.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ValueToReplaceDbContext>();
context.Database.EnsureCreated();
context.ValueToReplaces.RemoveRange(context.ValueToReplaces);
context.ValueToReplaces.AddRange(fakeValueToReplaceOne, fakeValueToReplaceTwo);
context.SaveChanges();
}
var client = appFactory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
var result = await client.GetAsync($"api/v1/ValueToReplaceLowers")
.ConfigureAwait(false);
var responseContent = await result.Content.ReadAsStringAsync()
.ConfigureAwait(false);
var response = JsonConvert.DeserializeObject<IEnumerable<ValueToReplaceDto>>(responseContent);
// Assert
result.StatusCode.Should().Be(200);
response.Should().ContainEquivalentOf(fakeValueToReplaceOne);
response.Should().ContainEquivalentOf(fakeValueToReplaceTwo);
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
The fixture can be found here.
namespace Foundation.Api.Tests
{
using Foundation.Api.Data;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder
.ConfigureServices(services =>
{
// Remove the app's ValueToReplaceDbContext registration.
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<ValueToReplaceDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Add ValueToReplaceDbContext using an in-memory database for testing.
services.AddDbContext<ValueToReplaceDbContext>(options =>
{
options.UseInMemoryDatabase("TestingDb");
});
// Build the service provider.
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database
// context (ValueToReplaceDbContext).
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ValueToReplaceDbContext>();
// Ensure the database is created.
db.Database.EnsureCreated();
try
{
db.RemoveRange(db.ValueToReplaces);
// Seed the database with test data.
//Utilities.InitializeDbForTests(db);
}
catch (Exception ex)
{
}
}
});
}
}
}
I've also tried a fixture like so inspired by this one which just times out.
namespace Foundation.Api.Tests
{
using Foundation.Api.Data;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder
.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context using an in-memory
// database for testing.
services.AddDbContext<ValueToReplaceDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
options.UseInternalServiceProvider(serviceProvider);
});
services.AddScoped(provider => provider.GetService<ValueToReplaceDbContext>());
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database
using var scope = sp.CreateScope();
var scopedServices = scope.ServiceProvider;
var context = scopedServices.GetRequiredService<ValueToReplaceDbContext>();
// Ensure the database is created.
context.Database.EnsureCreated();
try
{
// Seed the database with test data.
//Utilities.InitializeDbForTests(context);
}
catch (Exception ex)
{
//logger.LogError(ex, "An error occurred seeding the " +
// $"database with test messages. Error: {ex.Message}");
}
})
.UseEnvironment("Test");
}
}
}
The repo can be found here.
Below is the sample I found from online Tutorial to host the website suing OWIN, however when I try to run on my machine, I got this error
CS0246 The type or namespace name 'Func<,>' could not be found (are you missing a using directive or an assembly reference?)
I think for using 'Func<,>' I have using System, and for IDictionary, I have using System.Collections.Generic; so I don't understand why it still can't work.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using AppFunc = Func<IDictionary<string, object>, Task>;
public class Startup
{
public void Configuration(IAppBuilder app)
{
var middleware = new Func<AppFunc, AppFunc>(MyMiddleWare);
app.Use(middleware);
app.Use<OtherMiddleware>();
}
public AppFunc MyMiddleWare(AppFunc next)
{
AppFunc appFunc = async (IDictionary<string, object> environment) =>
{
var response = environment["owin.ResponseBody"] as Stream;
byte[] str = Encoding.UTF8.GetBytes("My First Middleware");
await response.WriteAsync(str, 0, str.Length);
await next.Invoke(environment);
};
return appFunc;
}
public class OtherMiddleware : OwinMiddleware
{
public OtherMiddleware(OwinMiddleware next) : base(next) { }
public override async Task Invoke(IOwinContext context)
{
byte[] str = Encoding.UTF8.GetBytes(" Other middleware");
context.Response.Body.Write(str, 0, str.Length);
await this.Next.Invoke(context);
}
}
}
You need to put the AppFunc in the class so it can use the using,
Or you can use full namespace for Func, IDictionary and Task
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
// Use this
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
public class Startup
{
// Or this
using AppFunc = Func<IDictionary<string, object>, Task>;
...
}
For me it was low .NET Target Framework version 2.0 of application. Changed to 4.7 (it seems minimal is 3.5).
You need to make sure you added the reference System.Runtime.CompileServices to your project as you see in here: https://msdn.microsoft.com/en-us/library/bb549151(v=vs.110).aspx the delegate is part of mscorlib assembly.
I am migrating an ASP.NET Web API application to OWIN. That is not intended to use none OWIN deployments. So Global.asax is going to be removed. There are some code put into Global.asax event handlers specially in Application_EndRequest that should be handled by OWIN.
I have read some article about OWIN and searched the internet but couldn't determine how it can be done. Can anyone please describe how it can be done?
My environment:
Visual Studio 2015 RC
.Net Framework 4.5
Microsoft.AspNet.Cors.5.0.0
Microsoft.AspNet.WebApi.5.2.3
Microsoft.Owin.3.0.1
Owin.1.0
UPDATE: Here it is some sections of current code
using System;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using ProjectX.Web.AppStart;
using ProjectY.Domain.Contracts;
namespace ProjectX.UI
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_EndRequest(object sender, EventArgs e)
{
var unitOfWork = DependencyResolver.Current.GetService(typeof(IUnitOfWork)) as IUnitOfWork;
unitOfWork.SaveChanges();
}
}
}
namespace ProjectY.Domain.Contracts
{
public interface IUnitOfWork
{
void SaveChanges();
IRepository<T> GetRepository<T>() where T : class, IEntity, IHistory;
IDbContext GetDbContext();
}
}
using ProjectY.Core.Repositories;
using ProjectY.Domain.Contracts;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ProjectY.Core.UnitOfWork
{
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork(IProjectYDbContextFactory contextFactory)
{
_context = contextFactory.GetContext();
}
public void SaveChanges()
{
if (_context == null)
throw new ApplicationException("Something wrong has been happened. _context must not be null.");
_context.SaveChanges();
}
}
}
I stumbled upon this question while updating some legacy applications. For those still seeking the answer: you can solve this by creating a middleware:
app.Use(async (context, next) =>
{
await next.Invoke().ConfigureAwait(false);
//Do stuff after request here!
var unitOfWork = DependencyResolver.Current.GetService(typeof(IUnitOfWork)) as IUnitOfWork;
unitOfWork.SaveChanges();
});
You can use stage markers if you need more control on when your middleware will be called in the request processing pipeline.
See also https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-middleware-in-the-iis-integrated-pipeline#stage-markers
I am using signalR 1.0.0 rc2 and have the code as following:
using System.Web;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
[HubName("chat")]
public class Chat : Hub
{
public Chat()
{
}
[HubMethodName("send")]
public void Send(string message,string connectionId)
{
Clients.All(message);
}
}
and i have the client side js :
$(function () {
var myHub= $.connection.chat;
}
here i get the chat undefined.
i looked at the signalr/hubs and found
$.extend(signalR, {
//here should have extended hub methods but has nothing
});
what is the problem here is the hub not being initilized .I haven't included
RouteTable.Routes.MapHubs() in global.asax.cs (is it necessary to include this line?) . also when i include this line i get an error :
Method not found: 'Void Microsoft.AspNet.SignalR.DependencyResolverExtensions.InitializePerformanceCoun>ters(Microsoft.AspNet.SignalR.IDependencyResolver, System.String,System.Threading.CancellationToken)'.
thanks .