public void ConfigureServices(IServiceCollection services)
{
//other configuration
services.AddScoped<IMyDependency, MyDependency>(); //register dependency
services.AddScoped(typeof(MySendFilter<>)); //register generic filter
services.AddMassTransit(x =>
{
x.UsingRabbitMq((context, cfg) =>
{
cfg.UseSendFilter(typeof(MySendFilter<>), context); //generic filter
});
});
}
https://masstransit-project.com/advanced/middleware/scoped.html#usage
I am trying to execute the code above. I created a .net-core 3.1 app and installed MassTransit, MassTransit.AspNetCore, MassTransit.Extensions.DependencyInjection, and MassTransit.RabbitMQ (v6.3.2). However, neither UseSendFilter nor UsingRabbitMq methods could be found.
Related
I'm developing a .NET core 3.1 Console application (web-api).
I use a Serilog service (it is basically using the Microsoft.Extensions.Logging).
The Serilog is injected and can be used in the FW Controllers methods.
Now - I need something a little bit different - Whenever the system is starting up (after being down) I need to make an http post request - you can see it when executing the ConnectionInitiator.Initiate(), in the startup method. In that same scope (method\class) - I need to use the logger, in order to log some data. Now - If the request would be through the controller - the logger, would be available (by the DI).
To make a long story short - I need somehow to inject the Ilogger to the class or to make it available in some other way. I've tried use the logger in the startUp, but this seems to be impossible (since .net core 3.0 - if I understand correctly)
See my code:
Program.cs:
public class Program
{
public static void Main(string[] args)
{
var loggerConfig = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
//Reading the appconfig.json
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(loggerConfig).CreateLogger();
try
{
Log.Information("System Started up");
CreateWebHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "THE APPLICATION FAILED TO START UP");
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return WebHost.CreateDefaultBuilder(args).ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
}).UseSerilog().UseStartup<Startup>();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
StartUp.cs
public class Startup
{
public Startup(IConfiguration configuration/*, Microsoft.Extensions.Logging.ILogger logger*/)
{
Configuration = configuration;
ConnectionInitiator.Initiate(configuration/*, logger*/);
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers();
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().SetIsOriginAllowed((host) => true).AllowCredentials());
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
ConnectionInitiator.cs:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace AAA.BL
{
public static class ConnectionInitiator
{
private static readonly object threadlock = new object();
private static MyConfig myConfig;
private static ILogger ilogger;
/*
public ConnectionInitiator(ILogger _logger)
{
ilogger = _logger;
}
*/
public static void/*async Task*/ Initiate(IConfiguration configuration/*, ILogger ilogger*/)
{
HttpRequester httpRequester = new HttpRequester();
if (myConfig == null)
{
myConfig = new myConfig(configuration);
}
IssueOTPResponse response = /*await*/ httpRequester.PostSomething(myConfig, ilogger).Result; //Double check thread safe singleton implementation
if (response.ststuacode != 200)
{
ilogger.logcritical($"critical error when initiate connection (connectioninitiator): {response.statusdescription}");
}
}
}
}
It seems like the answer is much simpler that I expected - By using the Serilog and was added as a service in the Configure method - It can be reached globally (in every place of the namepsace) by using the static class Log and its static method Logger, for example:
Log.Logger.Information("XXXXX");
i am using the following code in configureservices in startup.cs in netcore 3
Can someone show how i can do this a better way, how do i inject it into configure method? I need to access the database before dependency injection has resolved it but buildservices isnt recommended.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<bookingsstrathContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MyEntities2")));
services.AddScoped<IMyAccountService, MyAccountService>();
var sp = services.BuildServiceProvider();
// Resolve the services from the service provider
var _myAccountService = sp.GetService<IMyAccountService>();
//get the local role from the local database
if (_myAccountService.IsUserInRole(context.Principal.GetUserGraphSamAccountName(), "Administrator"))
{
//myIdentity.AddClaim(new Claim(ClaimTypes.Role, "Administrator"));
context.Principal.AddUserSystemRole("Administrator");
}
}
You can add your account service in program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices(serviceCollection =>
serviceCollection.AddScoped<IMyAccountService, MyAccountService>())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
and then you can use it in startup.cs
private readonly IMyAccountService _myAccountService;
public Startup(IConfiguration configuration, IMyAccountService AccountService)
{
Configuration = configuration;
_myAccountService = AccountService
}
I'm currently working on a POC project and I'm trying to figure out how I can share a service dependency between different endpoints to control application state and handle all service requests (lets call it ControlService) - specifically when one of those endpoints is a KestrelCommunicationListener / HttpSysCommunicationListener and combined with a FabricTransportServiceRemotingListener (or any other type of custom listener)
Autofac looked promising but the examples don't show how to get a HTTP listener working when the container is built in startup rather than the main entry point - would I need to pass the container to MyFabricService so it can be passed into and added to by the startup registrations?
I've seen references to using container.Update() or adding registrations on the fly using container.BeginLifetimeScope() but they are all using a container built in main and then I'm not sure how I would add the APIs created by the HTTP listener to the original container.
I'm possibly not explaining it that well so in summary I'm looking to have something like the below service that can receive communications via n. different endpoints - process the message and then send messages out via n. clients (aka other service endpoints)
Happy to clarify if anything is unclear - perhaps even using another creative diagram :)
Updated:
From Program.Main()
ServiceRuntime.RegisterServiceAsync("ManagementServiceType",
context => new ManagementService(context)).GetAwaiter().GetResult();
Here is my fabric service
public ManagementService(StatefulServiceContext context)
: base(context)
{
//this does not work but is pretty much what I'm after
_managementService = ServiceProviderFactory.ServiceProvider.GetService(typeof(IManagementService)) as IManagementService;
}
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners() =>
new ServiceReplicaListener[]
{
//create external http listener
ServiceReplicaListenerFactory.CreateExternalListener(typeof(Startup), StateManager, (serviceContext, message) => ServiceEventSource.Current.ServiceMessage(serviceContext, message), "ServiceEndpoint"),
//create remoting listener with injected dependency
ServiceReplicaListenerFactory.CreateServiceReplicaListenerFor(() => new RemotingListenerService(_managementService), "ManagmentServiceRemotingEndpoint", "ManagementServiceListener")
};
ServiceReplicaListener
public static ServiceReplicaListener CreateExternalListener(Type startupType, IReliableStateManager stateManager, Action<StatefulServiceContext, string> loggingCallback, string endpointname)
{
return new ServiceReplicaListener(serviceContext =>
{
return new KestrelCommunicationListener(serviceContext, endpointname, (url, listener) =>
{
loggingCallback(serviceContext, $"Starting Kestrel on {url}");
return new WebHostBuilder().UseKestrel()
.ConfigureServices((hostingContext, services) =>
{
services.AddSingleton(serviceContext);
services.AddSingleton(stateManager);
services.AddApplicationInsightsTelemetry(hostingContext.Configuration);
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => new FabricTelemetryInitializer(serviceContext));
})
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddServiceFabricConfiguration(serviceContext);
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddDebug();
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
.UseStartup(startupType)
.UseUrls(url)
.Build();
});
});
}
Startup
public class Startup
{
private const string apiTitle = "Management Service API";
private const string apiVersion = "v1";
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
var modules = new List<ICompositionModule>
{
new Composition.CompositionModule(),
new BusinessCompositionModule()
};
foreach (var module in modules)
{
module.AddServices(services, configuration);
}
services.AddSwashbuckle(configuration, apiTitle, apiVersion, "ManagementService.xml");
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddApplicationInsights(app.ApplicationServices);
// app.UseAuthentication();
// app.UseSecurityContext();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseMvc();
app.UseSwagger(apiTitle, apiVersion);
//app.UseMvc(routes =>
//{
// routes.MapRoute(
// name: "default",
// template: "{controller=Home}/{action=Index}/{id?}");
//});
}
}
All the service dependencies are added in the CompositionModules using Microsoft.Extensions.DependencyInjection (not autofac) in startup.cs
This works great and creates my HTTP listener - I now just need a way of getting access to my services that were added to the container during startup of my http listener / webhost.
You can use Autofac.Integration.ServiceFabriŅ, an Autofac extension to support Service Fabric. You need to create a container in Program.cs
var builder = new ContainerBuilder();
builder.RegisterServiceFabricSupport();
builder.RegisterType<SomeService>().As<IManagementService>();
builder.RegisterStatelessService<ManagementService>("ManagementServiceType");
using (builder.Build())
{
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
}
Then you can inject it to the constructor of your fabric service. You can find more information on this topic on https://alexmg.com/posts/introducing-the-autofac-integration-for-service-fabric
#Tim
Sorry for a late response. Currently I am working on library package that we use in our company for internal projects. This library simplifies configuration of Reliable Services. I think our recent enhancements can do what you need to do (hope I get the use case correctly).
All the information about the library can be found on project page on GitHub and NuGet package can be found here (please note that it is a pre-release version but we are planning to turn into complete release soon).
In case you have any questions or need more information feel free to contact me.
UPDATE
I have create a sample application. Please feel free to try it.
Here is a code example.
public interface IManagementService
{
string GetImportantValue();
}
public interface IMessageProvider
{
string GetMessage();
}
public class MessageProvider : IMessageProvider
{
public string GetMessage()
{
return "Value";
}
}
public class ManagementService : IManagementService
{
private readonly IMessageProvider provider;
public ManagementService(
IMessageProvider provider)
{
this.provider = provider;
}
public string GetImportantValue()
{
// Same instances should have the same hash
return this.provider.GetMessage() + $"Hash: {this.GetHashCode()}";
}
}
public interface IRemotingImplementation : IService
{
Task<string> RemotingGetImportantValue();
}
public class RemotingImplementation : IRemotingImplementation
{
private readonly IManagementService managementService;
public RemotingImplementation(
IManagementService managementService)
{
this.managementService = managementService;
}
public Task<string> RemotingGetImportantValue()
{
return Task.FromResult(this.managementService.GetImportantValue());
}
}
public class WebApiImplementationController : ControllerBase
{
private readonly IManagementService managementService;
public WebApiImplementationController(
IManagementService managementService)
{
this.managementService = managementService;
}
[HttpGet]
public Task<string> WebApiGetImportantValue()
{
return Task.FromResult(this.managementService.GetImportantValue());
}
}
public class WebApiStartup
{
private readonly IConfiguration configuration;
public WebApiStartup(
IConfiguration configuration)
{
this.configuration = configuration;
}
public void ConfigureServices(
IServiceCollection services)
{
services.AddMvc();
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseMvcWithDefaultRoute();
}
}
internal static class Program
{
/// <summary>
/// This is the entry point of the service host process.
/// </summary>
private static void Main()
{
var host = new HostBuilder()
.ConfigureServices(
services =>
{
services.AddTransient<IMessageProvider, MessageProvider>();
services.AddSingleton<IManagementService, ManagementService>();
})
.ConfigureStatefulService(
serviceBuilder =>
{
serviceBuilder
.UseServiceType("StatefulServiceType")
.DefineAspNetCoreListener(
listenerBuilder =>
{
listenerBuilder
.UseEndpointName("ServiceEndpoint")
.UseKestrel()
.UseUniqueServiceUrlIntegration()
.ConfigureWebHost(
webHostBuilder =>
{
webHostBuilder.UseStartup<WebApiStartup>();
});
})
.DefineRemotingListener(
listenerBuilder =>
{
listenerBuilder
.UseEndpointName("ServiceEndpoint2")
.UseImplementation<RemotingImplementation>();
});
})
.Build()
.Run();
}
}
Created a new ASP.NET Core 2.0 project and it runs fine locally.
Then after running it in a Docker container locally it also works fine. But when I try to use the Docker image in a Kubernetes pod, it will run for a couple minutes and then give me this:
Unhandled Exception: System.InvalidOperationException: A path base can
only be configured using IApplicationBuilder.UsePathBase().
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.
<BindAddressAsync>d__7.MoveNext()
Here is my Program.cs:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
ReactHotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
When this error was happening, we were using the FROM microsoft/aspnetcore-build:1.1 base image as our build and runtime. At the time we were encountering the error, we had simply tried to upgrade to FROM microsoft/aspnetcore-build:2.0. I'm not certain what specifically the issue with this image was, but Kubernetes didn't like it.
At a later date, we switched the dockerfile to multistage; building with FROM microsoft/aspnetcore-build:1.1 and running with FROM microsoft/dotnet:1.1-runtime, and when we upgraded that to the corresponding 2.0 versions, we didn't encounter this error again.
I am trying to inject a interface into to my HomeController and I am getting this error:
InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Configuration.IConfiguration' while attempting to activate
My Startup class is as follows:
public Startup(IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options => options
.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
services.AddSingleton(provider => Configuration);
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
try
{
using (var serviceScope = app.ApplicationServices
.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider
.GetService<ApplicationDbContext>()
.Database.Migrate();
}
}
catch { }
}
app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseIdentity();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.Run((async (context) =>
{
await context.Response.WriteAsync("Error");
}));
}
and my HomeController constructor is:
public HomeController(IConfiguration configuration, IEmailSender mailService)
{
_mailService = mailService;
_to = configuration["emailAddress.Support"];
}
Please tell me where I am mistaken.
Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
Try injecting it as an IConfigurationRoot instead of IConfiguration:
public HomeController(IConfigurationRoot configuration
, IEmailSender mailService)
{
_mailService = mailService;
_to = configuration["emailAddress.Support"];
}
In this case, the line
services.AddSingleton(provider => Configuration);
is equivalent to
services.AddSingleton<IConfigurationRoot>(provider => Configuration);
because the Configuration property on the class is declared as such, and injection will be done by matching whatever type it was registered as. We can replicate this pretty easily, which might make it clearer:
public interface IParent { }
public interface IChild : IParent { }
public class ConfigurationTester : IChild { }
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
IChild example = new ConfigurationTester();
services.AddSingleton(provider => example);
}
public class HomeController : Controller
{
public HomeController(IParent configuration)
{
// this will blow up
}
}
However
As stephen.vakil mentioned in the comments, it would be better to load your configuration file into a class, and then inject that class into controllers as needed. That would look something like this:
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
You can grab these configurations with the IOptions interface:
public HomeController(IOptions<AppSettings> appSettings)
In Core 2.0 it's recommended to use IConfiguration instead of IConfigurationRoot
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
from https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#add-configuration-providers
When moving a project from .Net Core 1.x to 2.0, change all IConfigurationRoot to IConfiguration