I've set up a very simple middleware as a test project to learn, currently it just dumps out the request headers.
I was wondering, given the set-up below if it is possible to either:
Populate a field within the Startup class (that can then be accessed via DI)
or to directly access a field within the Middleware (say in OnActionExecuting)
Startup:
using HeaderAuthentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace ServiceLayer
{
// ReSharper disable once ClassNeverInstantiated.Global
public class Startup
{
private IConfiguration Configuration { get; }
public Startup(IConfiguration Configuration)
{
this.Configuration = Configuration;
}
// ReSharper disable once UnusedMember.Global
public void ConfigureServices(IServiceCollection Services)
{
Services.AddMvc().AddJsonOptions(Options =>
Options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
}
// ReSharper disable once UnusedMember.Global
public void Configure(
IApplicationBuilder App,
IHostingEnvironment Env,
ILoggerFactory LoggerFactory
)
{
App.UseHeaderChecking();
if (Env.IsDevelopment())
{
App.UseDeveloperExceptionPage();
}
App.UseMvc();
}
}
}
Extension method:
using Microsoft.AspNetCore.Builder;
namespace HeaderAuthentication
{
public static class RequestHeaderCheckingMiddleware
{
public static IApplicationBuilder UseHeaderChecking(
this IApplicationBuilder Builder
)
{
return Builder.UseMiddleware<CheckHeaders>();
}
}
}
CheckHeader code:
using InterfaceLayer.Entities;
using Microsoft.AspNetCore.Http;
using System;
using System.Threading.Tasks;
namespace HeaderAuthentication
{
public class CheckHeaders
{
private readonly RequestDelegate Next;
public CheckHeaders(RequestDelegate NextDelegate)
{
Next = NextDelegate;
}
public Task Invoke(HttpContext Context, SupportContext Support)
{
if (Context.Request == null)
{
//return null;
}
var testA = GetRequestHeader(Context, "X-HeaderTest-A"); // sandwich
var testB = GetRequestHeader(Context, "X-HeaderTest-B"); // biscuit
return Next(Context);
}
private static string GetRequestHeader(HttpContext Context, string Key)
{
if (!Context.Request.Headers.TryGetValue(Key, out var buffer))
{
return string.Empty;
}
return buffer;
}
}
}
I'd like to access the values within testA and testB within the OnActionExecuting method within my BaseController to trigger the "sandwich" and "biscuit" cases, as below:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Threading.Tasks;
namespace ServiceLayer.Controllers
{
public partial class BaseController : Controller
{
public BaseController()
{
}
public override void OnActionExecuting(ActionExecutingContext Context)
{
switch (testValue)
{
case "sandwich":
break;
case "biscuit":
break;
}
base.OnActionExecuting(Context);
}
}
}
Is this feasible?
A dirty way could be your values into the Context.Items collection under a separate well known key inside CheckHeaders.Invoke method, and to query the context items for the presence of the values inside BaseController.OnActionExecuting method and dependig on it to act appropriately.
Related
I am having trouble trying to apply dependency injection. After a lot of research and looking at various videos on YouTube and answers on Stack overflow, my ITaskRepository keeps returning a null instead of it being an instance of my repository. Looking at my code it seems I have added all the right things to make dependency injection work.
My Base Repository interface
using portfolio_backend.Data.Base;
using System.Collections.Generic;
namespace portfolio_backend.Business.Repositories.Base
{
public interface IBaseRepository<TEntity> where TEntity : BaseModel
{
void Add(TEntity model);
void Delete(TEntity model);
bool Exists(int Id);
TEntity Get(int Id);
IEnumerable<TEntity> GetAll();
void Update(int Id, TEntity model);
}
}
My BaseRepository class
using Microsoft.EntityFrameworkCore.Internal;
using portfolio_backend.Data;
using portfolio_backend.Data.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace portfolio_backend.Business.Repositories.Base
{
public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : BaseModel
{
protected PortfolioContext _context;
public BaseRepository(PortfolioContext context)
{
_context = context;
}
public void Add(TEntity model)
{
if (!Exists(model.Id))
{
_context.Set<TEntity>().Add(model);
_context.SaveChanges();
}
}
public void Delete(TEntity model)
{
if (Exists(model.Id))
{
_context.Set<TEntity>().Remove(model);
_context.SaveChanges();
}
}
public bool Exists(int Id)
{
return _context.Set<TEntity>().Any(model => model.Id == Id);
}
public TEntity Get(int Id)
{
return _context.Set<TEntity>().FirstOrDefault(model => model.Id == Id);
}
public IEnumerable<TEntity> GetAll()
{
return _context.Set<TEntity>().ToList();
}
public void Update(int Id, TEntity model)
{
var modelToFind = Get(Id);
_context.Set<TEntity>().Update(modelToFind);
_context.SaveChanges();
}
}
}
My ITaskRepository interface
using portfolio_backend.Business.Repositories.Base;
using portfolio_backend.Data;
using System.Collections.Generic;
namespace portfolio_backend.Business.Repositories
{
public interface ITaskRepository : IBaseRepository<Task>
{
IEnumerable<Task> GetTaskByProjects(int ProjectId);
}
}
TaskRepository Implementation
using portfolio_backend.Business.Repositories.Base;
using portfolio_backend.Data;
using System.Collections.Generic;
using System.Linq;
namespace portfolio_backend.Business.Repositories
{
public class TaskRepository : BaseRepository<Task>, ITaskRepository
{
public TaskRepository(PortfolioContext context) : base(context)
{
}
public IEnumerable<Task> GetTaskByProjects(int ProjectId)
{
return _context.Tasks.OrderByDescending(task => task.Project.Id == ProjectId).ToList();
}
}
}
My Startup class:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using portfolio_backend.Business.Repositories.Base;
using portfolio_backend.Business.Repositories;
using portfolio_backend.Data;
using Blazorise;
using Blazorise.Bootstrap;
using Blazorise.Icons.FontAwesome;
namespace portfolio_backend.Presentation
{
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.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PortfolioContext>(options =>
options.UseMySQL(Configuration.GetConnectionString("portfolio")));
services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>));
services.AddScoped<ITaskRepository, TaskRepository>();
services.AddBlazorise(options =>{
options.ChangeTextOnKeyPress = true;})
.AddBootstrapProviders()
.AddFontAwesomeIcons();
services.AddRazorPages();
services.AddServerSideBlazor();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.ApplicationServices
.UseBootstrapProviders()
.UseFontAwesomeIcons();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
I am trying to apply dependency injection for the following two classes:
Tasks.razor.cs ( a code-behind for a Blazor component)
using portfolio_backend.Business;
using portfolio_backend.Business.Repositories;
namespace portfolio_backend.Presentation.Pages
{
public partial class Tasks
{
private ITaskRepository _taskRepository;
private TaskViewModel _taskViewModel => new TaskViewModel(_taskRepository);
protected override void OnInitialized()
{
_taskViewModel.SeedTasks();
}
}
}
and the view model for this component
using portfolio_backend.Business.Repositories;
using portfolio_backend.Data;
using System.Collections.Generic;
namespace portfolio_backend.Business
{
public class TaskViewModel
{
private ITaskRepository _taskRepository {get; set;}
private List<Task> _allTasks;
public TaskViewModel(ITaskRepository repository)
{
_taskRepository = repository;
}
public List<Task> AllTasks
{
get => _allTasks;
set => _taskRepository.GetAll();
}
public void SeedTasks()
{
_taskRepository.Add( new Task { Description = "Task 1"} );
_taskRepository.Add(new Task { Description = "Task 2" });
_taskRepository.Add(new Task { Description = "Task 3" });
}
}
}
_taskRepository always returns null, and this is the error message that appears:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
what can I do to solve this? or how can I apply DI in a better way under these circumstances?
UPDATE:
I made the following changes based on one of the suggested solutions in the comments:
using portfolio_backend.Business;
using portfolio_backend.Business.Repositories;
namespace portfolio_backend.Presentation.Pages
{
public partial class Tasks
{
private ITaskRepository _taskRepository;
private TaskViewModel _taskViewModel;
public Tasks(ITaskRepository repository)
{
_taskRepository = repository;
_taskViewModel = new TaskViewModel(_taskRepository);
}
protected override void OnInitialized()
{
_taskViewModel.SeedTasks();
}
}
}
This will trigger the following error:
MissingMethodException: No parameterless constructor defined for type 'portfolio_backend.Presentation.Pages.Tasks'.
As the error suggested I added an additional parameterless constructor
using portfolio_backend.Business;
using portfolio_backend.Business.Repositories;
namespace portfolio_backend.Presentation.Pages
{
public partial class Tasks
{
private ITaskRepository _taskRepository;
private TaskViewModel _taskViewModel;
public Tasks()
{
}
public Tasks(ITaskRepository repository)
{
_taskRepository = repository;
_taskViewModel = new TaskViewModel(_taskRepository);
}
protected override void OnInitialized()
{
_taskViewModel.SeedTasks();
}
}
}
The change above created the same issue with the taskRepository being null.
_taskRepository must be either a property or a constructor parameter. You have it as a class member. It can't be injected like that.
You have to register your dependency in ConfigureServices(IServiceCollection services) of your startup file :
services.AddScoped<ITaskRepository, TaskRepository>();
or
services.AddTransient<ITaskRepository, TaskRepository>();
You have to decide what fits you app better.
Following the edits you've made to your initial question, it seems that your are using Dependency Injection mechanisms on a class which is not registered : Tasks. How is this class implemented?
If you want to use DI on a specific class, you should register it as you did with ITaskRepository for example.
Add the following line to your ConfigureServices() method :
services.AddScoped<Tasks>();
There were two main challenges in this scenario.
The first one was that I didn't design my app properly. What I mean by that is that I initially intended to use dependency injection on an instance of my repository to be able to create an instance of my TaskViewModel in my code behind like this.
public Tasks(ITaskRepository repository)
{
_taskRepository = repository;
_taskViewModel = new TaskViewModel(_taskRepository);
}
A better way to do it which was part of my solution was to also create an interface for my TaskViewModel so I can use dependency injection in my Blazor component code-behind. The TaskViewModel itself should have had an instance of my repository through dependency injection.
ITaskViewModel:
public interface ITaskViewModel : IBaseViewModel<Task>
{
List<Task> AllTasks { get; set; }
void SeedTasks();
}
My Implementation for the TaskViewModel
public class TaskViewModel : BaseViewModel<Task>, ITaskViewModel
{
private ITaskRepository _taskRepository;
private List<Task> _allTasks;
public TaskViewModel(ITaskRepository repository) : base(repository)
{
_taskRepository = repository;
}
public List<Task> AllTasks
{
get => _allTasks;
set
{
_allTasks = value;
}
}
public void SeedTasks()
{
var task1 = new Task { Description = "Task 1" };
var task2 = new Task { Description = "Task 2" };
var task3 = new Task { Description = "Task 3" };
_taskRepository.Add(task1);
_taskRepository.Add(task2);
_taskRepository.Add(task3);
}
}
Component registration on the ConfigureServices method of the Startup.cs file
services.AddScoped<ITaskViewModel, TaskViewModel>();
The 2nd problem was that I could not use the constructor or the member property approach to use dependency injection in a Blazor component code-behind. Not sure if the same applies to razor page code behind, but the way you use dependency inject for a Blazor component code-behind is by using the Inject attribute
Tasks.razor.cs
[Inject]
private ITaskViewModel _viewModel { get; set; }
Make sure you also have the following Nuget package installed for the Inject attribute to work.
using Microsoft.AspNetCore.Components;
I'm creating an api and inside a controller I want to call a method, this method belongs to a class, namespaceService. I create a new instance of this class whenever I create a new Instance of the class k8sClient. To use the k8sClient class inside my api I created a interface of this class Ik8sClient.
Whenever I call the method, which is called GetNamespaces, I get a MissingMethodException.
The full exception is as follows: "MissingMethodException: Method not found: 'k8s.Models.V1NamespaceList k8s.KubernetesExtensions.ListNamespace(k8s.IKubernetes, System.String, System.String, System.String, System.Nullable1<Int32>, System.String, System.Nullable1, System.Nullable`1, System.String)'."
This is my code:
NamespacesController
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Administration.Libary;
using Administration.Model;
using k8s.Models;
namespace Administration.Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class NamespacesController : ControllerBase
{
private readonly IK8sClient _k8sClient;
public NamespacesController(IK8sClient client)
{
_k8sClient = client;
}
//GET: api/namespaces
[HttpGet]
public async Task<IEnumerable<NameSpace>> GetNamespaces()
{
var result = await _k8sClient.nsService.GetNamespaces();
return result;
}
}
}
K8sClient.cs
using k8s;
using System;
namespace Administration.Libary
{
public class K8sClient : IK8sClient
{
private readonly Kubernetes _k8sClient = null;
public NamespaceService nsService { get; private set; }
public Kubernetes Client
{
get
{
return _k8sClient;
}
}
public K8sClient()
{
string kubeConfigFile = System.IO.Path.Combine(Environment.CurrentDirectory, "kubeconfig");
if (!System.IO.File.Exists(kubeConfigFile)) throw new System.IO.FileNotFoundException("Kube config file not found");
var config = KubernetesClientConfiguration.BuildConfigFromConfigFile(kubeConfigFile);
_k8sClient = new Kubernetes(config);
InitiateServices(this);
}
public void InitiateServices(K8sClient client)
{
if (client == null)
throw new ArgumentNullException("Client was null.");
nsService = new NamespaceService(client);
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_k8sClient.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
public interface IK8sClient : IDisposable
{
Kubernetes Client { get; }
NamespaceService nsService { get; }
void InitiateServices(K8sClient client);
}
NamespaceService.cs
using Administration.Model;
using k8s;
using System.Collections.Generic;
using System;
using System.Threading.Tasks;
namespace Administration.Libary
{
public class NamespaceService
{
private IK8sClient _k8sClient;
private CreateNsService createNsService;
public NamespaceService(K8sClient client)
{
_k8sClient = client;
createNsService = new CreateNsService(client);
}
public NameSpace selectedNamespace;
private List<string> namespacesToIgnore = new List<string> { "default", "kube-node-lease", "kube-public", "kube-system"};
public Task<List<NameSpace>> GetNamespaces()
{
var namespaceList = new Task<List<NameSpace>>(() => new List<NameSpace>());
var namespaces = _k8sClient.Client.ListNamespace();
foreach (var ns in namespaces.Items)
{
string nsName = ns.Metadata.Name;
if (String.IsNullOrEmpty(nsName))
throw new Exception("Failed to retrieve namespace ID.");
if (!namespacesToIgnore.Contains(nsName) && ns.Status.Phase != "Terminating")
{
namespaceList.Result.Add(new NameSpace()
{
Id = ns.Metadata.Name
});
}
}
return namespaceList;
}
}
}
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Administration.Libary;
namespaceAdministration.Api
{
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().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddScoped<IK8sClient, K8sClient>();
}
// 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();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
The line which should throw the error is: "var namespaces = _k8sClient.Client.ListNamespace();" in the NamespaceService class. It's also notable that I can't enter my NamespaceService class while debugging, when I set a breakpoint in the class it never stops there, even tho it appears that my program enters the class.
Update
I changed the startup to :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Actio.Api.Handlers;
using Actio.Api.Repositories;
using Actio.Common.Auth;
using Actio.Common.Events;
using Actio.Common.Mongo;
using Actio.Common.RabbitMq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Actio.Api
{
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();
services.AddJwt(Configuration);
services.AddRabbitMq(Configuration);
services.AddMongoDB(Configuration);
services.AddScoped<IEventHandler<ActivityCreated>, ActivityCreatedHandler>();
services.AddScoped<IActivityRepository, ActivityRepository>();
}
// 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.ApplicationServices.GetService<IDatabaseInitializer>().InitializeAsync();
using (var serviceScope = app.ApplicationServices.CreateScope())
{
serviceScope.ServiceProvider.GetService<IDatabaseInitializer>().InitializeAsync();
}
app.UseAuthentication();
app.UseMvc();
}
}
}
But now I am having Error in SubscribeToEvent:
Cannot resolve scoped service
'Actio.Common.Events.IEventHandler`1[Actio.Common.Events.ActivityCreated]' from root provider.'
in my ServiceHost.cs.
ServiceHost.cs
using System;
using Actio.Common.Commands;
using Actio.Common.Events;
using Actio.Common.RabbitMq;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using RawRabbit;
namespace Actio.Common.Services
{
public class ServiceHost : IServiceHost
{
private readonly IWebHost _webHost;
public ServiceHost(IWebHost webHost)
{
_webHost = webHost;
}
public void Run() => _webHost.Run();
public static HostBuilder Create<TStartup>(string[] args) where TStartup : class
{
Console.Title = typeof(TStartup).Namespace;
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
var webHostBuilder = WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
.UseStartup<TStartup>();
return new HostBuilder(webHostBuilder.Build());
}
public abstract class BuilderBase
{
public abstract ServiceHost Build();
}
public class HostBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public HostBuilder(IWebHost webHost)
{
_webHost = webHost;
}
public BusBuilder UseRabbitMq()
{
_bus = (IBusClient)_webHost.Services.GetService(typeof(IBusClient));
return new BusBuilder(_webHost, _bus);
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
public class BusBuilder : BuilderBase
{
private readonly IWebHost _webHost;
private IBusClient _bus;
public BusBuilder(IWebHost webHost, IBusClient bus)
{
_webHost = webHost;
_bus = bus;
}
public BusBuilder SubscribeToCommand<TCommand>() where TCommand : ICommand
{
var handler = (ICommandHandler<TCommand>)_webHost.Services
.GetService(typeof(ICommandHandler<TCommand>));
_bus.WithCommandHandlerAsync(handler);
return this;
}
public BusBuilder SubscribeToEvent<TEvent>() where TEvent : IEvent
{
var handler = (IEventHandler<TEvent>)_webHost.Services
.GetService(typeof(IEventHandler<TEvent>));
_bus.WithEventHandlerAsync(handler);
return this;
}
public override ServiceHost Build()
{
return new ServiceHost(_webHost);
}
}
}
}
------------------------------------------------------------------------------
I recently started learning Microservices with RabbitMQ. After much struggle I got a code sample but I am unable to run it as it is giving error :"System.InvalidOperationException: 'Cannot resolve scoped service 'Actio.Common.Mongo.IDatabaseInitializer' from root provider.'"
I wish to understand this code so that I can have better understanding of Microservices.
Code-Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Actio.Api.Handlers;
using Actio.Api.Repositories;
using Actio.Common.Auth;
using Actio.Common.Events;
using Actio.Common.Mongo;
using Actio.Common.RabbitMq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Actio.Api
{
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();
services.AddJwt(Configuration);
services.AddRabbitMq(Configuration);
services.AddMongoDB(Configuration);
services.AddScoped<IEventHandler<ActivityCreated>, ActivityCreatedHandler>();
services.AddScoped<IActivityRepository, ActivityRepository>();
}
// 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();
}
//Giving Error in below line
app.ApplicationServices.GetService<IDatabaseInitializer>().InitializeAsync();
app.UseAuthentication();
app.UseMvc();
}
}
}
Can someone please help so that I can debug and l have better understanding.
Thanks
The below error is trying to tell you that you are registering IDatabaseInitializer as a scoped service but trying to access it outside the scope:
"System.InvalidOperationException: 'Cannot resolve scoped service 'Actio.Common.Mongo.IDatabaseInitializer' from root provider.'"
Try to create a scope and then use the service like:
using (var serviceScope = app.ApplicationServices.CreateScope())
{
serviceScope.ServiceProvider.GetService<IDatabaseInitializer>().InitializeAsync();
}
The solution is to disable scope validation.
So in file src\actio.common\services\servicehost.cs between lines 31-32 add the following:
.UseDefaultServiceProvider(options => options.ValidateScopes = false)
Another alternative approach to solve the issue is changing from services.AddScoped to services.AddSingleton in program.cs, working with a singleton is gonna avoid all the scope validation as well.
I'm working with ASP.NET core on linux (ubuntu 16.04) and I'm trying to connect entity framework core with MySQL.
At first, I used EF without external database (I don't understand much but, I think there's an internal DB that EF use by default) and all http methods (get, post, put ...) worked.
then I connected EF with MySQL using Pomelo, this is my NuGet.config file :
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="Pomelo" value="https://www.myget.org/F/pomelo/api/v3/index.json"/>
<add key="nuget.org" value="https://www.nuget.org/api/v2" />
</packageSources>
</configuration>
and this is my startup.cs file :
`
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using test.Context;
using MySql.Data.EntityFrameworkCore.Extensions;
using System.ComponentModel.DataAnnotations;
namespace test
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => {
options.AddPolicy("AllowAllHeaders",
builder => {
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddDbContext<ValuesContext>(opt =>
opt.UseMySql("server=localhost;database=test;uid=root;pwd=pfe2018"));
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("AllowAllHeaders");
app.UseMvc();
}
}
}
`
Then, I tested the project with a simple console code in program.cs and it worked.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using test.Context;
using test.Models;
namespace test
{
public class Program
{
public static void Main(string[] args) {
using (var context = new ValuesContext())
{
context.Database.EnsureCreated();
// Creating a new Value and saving it to the database
context.values.Add(new Values {
fname = "abc",
lname = "efg",
age = 100
});
var count = context.SaveChanges();
Console.WriteLine("{0} records saved to database", count);
// Retrieving and displaying data
Console.WriteLine();
Console.WriteLine("All Values in the database:");
foreach (var value in context.values)
{
Console.WriteLine("{2} |{0} | {1}", value.fname, value.lname, value.id);
}
}
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
the new value was successfuly added to the database, so I thought I just have to run the WEB API by changing the code in the main() method in Program.cs :
public static void Main(string[] args) {
string url = "http://localhost:5000";
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseUrls(url)
.Build();
host.Run();
}
My DbContext and Controller are as follows :
ValuesContext :
using Microsoft.EntityFrameworkCore;
using test.Models;
namespace test.Context
{
public class ValuesContext : DbContext
{
public ValuesContext(DbContextOptions<ValuesContext> options) : base(options) { }
public ValuesContext() {}
public DbSet<Values> values { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder.UseMySQL("server=localhost;database=test;uid=root;pwd=pfe2018");
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Values>(entity => {
entity.HasKey(v => v.id).HasName("VALUE_ID");
});
}
}
}
ValuesController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using test.Context;
using test.Models;
namespace test.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller {
private readonly ValuesContext _context;
public ValuesController(ValuesContext context) {
_context = context;
_context.Database.EnsureCreated();
}
// GET api/values
[HttpGet]
public IEnumerable<Values> GetAll()
{
Console.WriteLine(_context.values.ToList());
return _context.values.ToList();
}
[HttpPost]
public IActionResult Post([FromBody]Values value) {
if (value == null) {
return BadRequest();
}
_context.values.Add(value);
_context.SaveChanges();
return new CreatedAtRouteResult("itemRoute", new {id = value.id} , value);
}
}
}
when I try GET method with cURL :
curl -I http://localhost:5000/api/values
this is the response status :
HTTP/1.1 404 Not Found
Date: Thu, 22 Feb 2018 16:46:07 GMT
Server: Kestrel
any suggestion would be helpfull.
I am using Microsoft.AspNetCore.SignalR (latest release) and would like to get the hub context from within another object that's not a Controller. In the "full" SignalR, I could use GlobalHost.ConnectionManager.GetHubContext<MyCoolHub>();
I have seen a lot of examples of just adding Microsoft.AspNetCore.SignalR.IHubContext<MyCoolHub> as a parameter to the Ctor of a Controller, but no examples (that work) for otherwise.
ETA:
So, this is what I have working. Is this hacky?
public class MyHub : Hub
public static IHubContext<MyHub> GlobalContext { get; private set; }
public MyHub(IHubContext<MyHub> ctx){
GlobalContext = ctx;
}
}
Then I can call it as so:
await MyHub.GlobalContext.Clients.All.InvokeAsync(...)
Just set IHubContext<MyHub> hubContext on calling-side constructor.
I would recommend using .net core default DI container mechanism, not creating static property.
Please refer to How do I get a reference to a Hub?
public class MyHub : Hub
{
}
public class CallingSideClass
{
private readonly IHubContext<MyHub> _hubContext;
public CallingSideClass(IHubContext<MyHub> hubContext)
{
_hubContext = hubContext;
}
public async Task FooMethod(...)
{
await _hubContext.Clients.All.InvokeAsync(...);
}
}
public class Startup
{...
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddScoped<CallingSideClass>();
}
...
}
So after looking over this example from the accepted answer, I didn't quite get where he was going, so I tried a few things and I think I got what he was saying. So, for folks who have this issue in the future, I want to change that example to a fully working example.
So we are going to create a "Shared Methods", like in the example:
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace YouDontNeedToKnow.Core.Main.Hubs
{
internal class HubMethods<THub> where THub : Hub
{
private readonly IHubContext<THub> _hubContext;
public HubMethods(IHubContext<THub> hubContext)
{
_hubContext = hubContext;
}
public Task InvokeOnGroupAsync(string groupName, string method, params object[] args) =>
_hubContext.Clients.Group(groupName).InvokeAsync(method, args);
public Task InvokeOnAllAsync(string method, params object[] args) =>
_hubContext.Clients.All.InvokeAsync(method, args);
public Task AddConnectionIdToGroupAsync(string connectionId, string groupName) =>
_hubContext.Groups.AddAsync(connectionId, groupName);
// ...
}
}
Then, in your Hub object, you add a constructor, like so:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
namespace YouDontNeedToKnow.Core.Main.Hubs
{
internal class MyHub : Hub
{
public static string HubName => "myHub";
private readonly HubMethods<MyHub> _hubMethods;
public PlayerServicesHub(HubMethods<MyHub> hubMethods)
{
_hubMethods = hubMethods;
}
public override Task OnConnectedAsync()
{
return base.OnConnectedAsync();
}
}
}
In your Startup.cs, you inject the shared class like so:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<HubMethods<MyHub>>();
services.AddSignalR();
services.AddMvc();
}
This still works as it normally would:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider sp)
{
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseSignalR(routes =>
{
routes.MapHub<MyHub>(MyHub.HubName);
});
app.UseMvc();
// This is just an example line of how you can get the hub with context:
var myHub = sp.GetService<HubMethods<MyHub>>();
}