Calling service/repository methods in ASP.Net Core middleware - entity-framework-6

ASP.Net Core noob here...I am using an ASP.Net Core WebAPI core project using DNX451 with EF 6.
I have a requirement to implement API Key auth in our service. To do this I have created middleware that gets information from the request and proceeds with authentication. It is SUPPOSED to go to the database, get the key to match, and then return and do the validation.
Here is the middleware implemented to look at the context and get the APIKey
AuthenticationHandler
public class AuthorizationHandler
{
private readonly RequestDelegate _next;
private IAuthenticationService _authenticationService;
public AuthorizationHandler(RequestDelegate next, IAuthenticationService authService)
{
_authenticationService = authService;
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
var apiKey = context.Request.Headers["Key"];
var location = context.Request.Headers["Host"];
var locationKey = _authenticationService.GetApiKey(location);
if (apiKey == locationKey)
await _next(context);
context.Response.StatusCode = 403;
context.Response.Headers.Add("WWW-Authenticate",
new[] { "Basic" });
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
context.Response.Headers.Add("WWW-Authenticate",
new[] { "Basic" });
}
}
}
Here is the startup class with context and middleware registration
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfiguration Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(k => new DbContext(Configuration["Data:Context:ConnectionString"]));
// Add framework 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, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIISPlatformHandler();
app.UseStaticFiles();
app.RegisterAuthorizationHeader();
app.RegisterAuthorization();
app.UseMvc();
}
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
Here is Auth service
public interface IAuthenticationService
{
string GetApiKey(string location);
}
public class AuthenticationService: IAuthenticationService
{
private IApiKeyRepository _apiKeyRepository;
public AuthenticationService(IApiKeyRepository repo)
{
_apiKeyRepository= repo;
}
public string GetApiKey(string location)
{
return _apiKeyRepository.GetApiKeyByLocation(location);
}
}
The repo
public interface IApiRepository
{
string GetApiKeyByLocation(string location);
}
public class ApiRepository: IApiRepository
{
private DbContext _context;
public ApiRepository(DbContext context)
{
_context = context;
}
public string GetApiKeyByLocation(string location)
{
var apiRow = _context.ApiKeyStore.FirstOrDefault(a => a.Location == location);
return apiRow == null ? string.Empty : apiRow.APIKey;
}
}
When attempting this I get the following error:
The context cannot be used while the model is being created. This
exception may be thrown if the context is used inside the
OnModelCreating method or if the same context instance is accessed by
multiple threads concurrently. Note that instance members of DbContext
and related classes are not guaranteed to be thread safe.
Now, when I debug this every break point is hit twice. I believe I understand WHY this issue is occurring but have no idea how to fix it.
Can someone give me an idea, please? Any better solution ideas?

To use scoped dependencies in a middleware (which is necessarily a singleton by definition), the best approach is to flow it as a parameter of InvokeAsync instead of flowing it via the constructor:
public async Task Invoke(HttpContext context, IAuthenticationService authenticationService)
{
try
{
var apiKey = context.Request.Headers["Key"];
var location = context.Request.Headers["Host"];
var locationKey = authenticationService.GetApiKey(location);
if (apiKey == locationKey)
await _next(context);
context.Response.StatusCode = 403;
context.Response.Headers.Add("WWW-Authenticate",
new[] { "Basic" });
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
context.Response.Headers.Add("WWW-Authenticate",
new[] { "Basic" });
}
}

Related

How do you inject a repo into a service in ASP.Net Core MVC

I have a really odd problem.
I have defined a Repository for a DbSet Called PageHits.
This is the interface:
public interface IPageHitRepo
{
IQueryable<PageHit> PageHits { get; }
void AddPageHit(PageHit pageHit);
void SaveChanges();
}
This is the implementation:
public class PageHitRepo : IPageHitRepo
{
private CtasContext _context;
public PageHitRepo(CtasContext context)
{
_context = context;
}
public IQueryable<PageHit> PageHits => _context.PageHits;
public void AddPageHit(PageHit pageHit)
{
_context.Add(pageHit);
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
I configure in startup.ConfigureServices like this:
services.AddScoped<IPageHitRepo, PageHitRepo>();
This is all just normal stuff. I use this to inject into controllers all the time using constructor injection.
But right now I want to inject this repo service into another service called PageHitService.
It has a lot going on but the injection part should be simple.
I have a IPageHitService interface defined like this:
public interface IPageHitService
{
// Default implimentation overridden in PageHitService.
Task Invoke(IServiceProvider sp) => Task.FromResult(true);
Task EmailSiteHitNotification(PageHit pageHit, IServiceProvider sp);
Task LogPageHitToDB(PageHit pageHit, IServiceProvider sp);
}
Then the top of the PageHitService looks like this:
public class PageHitService : IPageHitService
{
private IPageHitRepo _pageHitRepo;
public PageHitService(IPageHitRepo pageHitRepo)
{
_pageHitRepo = pageHitRepo;
}
public async Task Invoke(IServiceProvider sp)
{
HttpContext httpContext =
sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
IWebHostEnvironment env = sp.GetRequiredService<IWebHostEnvironment>();
IConfiguration config = sp.GetRequiredService<IConfiguration>();
// _context = sp.GetRequiredService<CtasContext>();
bool visitSet =
!string.IsNullOrEmpty(httpContext.Session.GetString("VisitSet"));
and here is the registration in StartUp.ConfigureServices:
services.AddScoped<IPageHitService, PageHitService>();
But when I try to compile I get this error:
There is no argument given that corresponds to the required formal parameter 'pageHitRepo' of 'PageHitService.PageHitService(IPageHitRepo)'
Why do we not get this error in constructors?
The way I understand DI in .Net Core is it looks at the constructor in the controller and if it sees a dependency it resolves it by the registration in startup and then if it sees a dependency in the service it is resolving it resolves that service's dependency and so on.
Why is it giving me this error.
I understand if I make a default paramterless constructor the error goes away but then ASP.Net Core DI does not know which constructor to use and both constructors get hit and my page ends up with a 500 error and cannot be shown.
What am I missing here?
Update 1:
Don't know if this can help.
But in answer to Steve's comment below I do have a BaseController class and a Base PageModel.
public class BaseController : Controller
{
public BaseController(IServiceProvider sp)
{
IPageHitService pageHitService = sp.GetRequiredService<IPageHitService>();
// pageHitService.Invoke(sp).Await();
pageHitService.Invoke(sp).Wait();
}
#region Callbacks if you use AsyncMethod.Await() extension method.
private void Completed()
{
Console.WriteLine("Completed pageHitService.Invoke from BaseController");
}
private void HandleError(Exception ex)
{
// TODO: Log Error to DB;
}
#endregion
}
public class BasePageModel : PageModel
{
public BasePageModel(IServiceProvider sp)
{
IPageHitService pageHitService = sp.GetRequiredService<IPageHitService>();
// pageHitService.Invoke(sp).Await();
pageHitService.Invoke(sp).Wait();
}
#region Callbacks if you use AsyncMethod.Await() extension method.
private void Completed()
{
Console.WriteLine("Completed pageHitService.Invoke from BaseController");
}
private void HandleError(Exception ex)
{
// TODO: Log Error to DB;
}
#endregion
}
But I can't see this affecting the constructor and causing an error there.
Update 2
I found the offending code in the PageHitService:
PageHitService pageHitService = new PageHitService();
Invoke was static because I was fooling around with going the factory route.
And I could not call the non-static methods so I had to instantiate an instance to call the 2 methods in the API.
Nothing is static now so I am going to comment out that line and just try to call the methods straight up.
Here is the full class:
public class PageHitService : IPageHitService
{
private IPageHitRepo _pageHitRepo;
public PageHitService(IPageHitRepo pageHitRepo)
{
_pageHitRepo = pageHitRepo;
}
public async Task Invoke(IServiceProvider sp)
{
HttpContext httpContext =
sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
IWebHostEnvironment env = sp.GetRequiredService<IWebHostEnvironment>();
IConfiguration config = sp.GetRequiredService<IConfiguration>();
// _context = sp.GetRequiredService<CtasContext>();
bool visitSet =
!string.IsNullOrEmpty(httpContext.Session.GetString("VisitSet"));
PageHit PageHit = new PageHit
{
SessionID = httpContext.Session.Id,
UserIP = httpContext.Connection.RemoteIpAddress.ToString(),
EnvironmentName = env.EnvironmentName,
Url = httpContext.Request.PathBase + httpContext.Request.Path,
ReferrerUrl = httpContext.Request.Headers["Referer"].ToString(),
DateTime = DateTime.Now
};
PageHitService pageHitService = new PageHitService();
bool logPageHitsOn = bool.Parse(config["LogPageHitsOn"]);
bool emailSiteHitNotificationsOn = bool.Parse(config["EmailSiteHitNotificationsOn"]);
if (!visitSet)
{
// create session
httpContext.Session.SetString("VisitSet", "true");
// Emails (1st visit of session)
if (emailSiteHitNotificationsOn)
{
await pageHitService.EmailSiteHitNotification(PageHit, sp);
}
}
// Logs all PageHits to DB.
if (logPageHitsOn)
{
await pageHitService.LogPageHitToDB(PageHit, sp);
}
}
public async Task EmailSiteHitNotification(PageHit pageHit,
IServiceProvider sp)
{
IEmailService emailService = sp.GetRequiredService<IEmailService>();
StringBuilder body = new StringBuilder();
body.AppendFormat($"<b>SessionID:</b> {pageHit.SessionID}");
body.Append("<br /><br />");
body.AppendFormat($"<b>User Ip:</b> {pageHit.UserIP}");
body.Append("<br /><br />");
body.AppendFormat($"<b>Environment:</b> {pageHit.EnvironmentName}");
body.Append("<br /><br />");
body.AppendFormat($"<b>Url (Page Hit):</b> {pageHit.Url}");
body.Append("<br /><br />");
body.AppendFormat($"<b>ReferrerUrl:</b> {pageHit.ReferrerUrl}");
body.Append("<br /><br />");
body.AppendFormat($"<b>DateTime:</b> {pageHit.DateTime}");
body.Append("<br /><br />");
string subject = "CTAS Hit Notification";
await emailService.SendEmail(body.ToString(), subject);
}
public async Task LogPageHitToDB(PageHit pageHit,
IServiceProvider sp)
{
CtasContext context = sp.GetRequiredService<CtasContext>();
await context.PageHits.AddAsync(pageHit);
await context.SaveChangesAsync();
}
}

Background Thread that uses ApplicaitonDBContext

I am trying to wire up a background thread that will update the database once an hour from Active Directory. I am not sure how to pass the current
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Connection")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddSessionStateTempDataProvider();
services.AddSession();
services.AddHttpContextAccessor();
services.AddSingleton(Configuration);
services.AddScoped<IAppDbRepository, AppDbRepository>();
services.AddScoped<IActiveDirectoryUtility, ActiveDirectoryUtility>();
services.AddScoped<IActiveDirectoryManager, ActiveDirectoryManager>();
services.AddHostedService<LdapManager>();
services.AddScoped<ILdapManager, LdapManager>();
}
In the LdapManager class I would like to call the UpdateUsers method every hour:
public class LdapManager : ILdapManager, IHostedService
{
private IConfiguration _configuration = null;
private Logging _logger;
private List<string> ldapConnectorForDirectoryEntries = new List<string>();
public LdapManager(IConfiguration configuration)
{
_configuration = configuration;
UpdateUsers();
SyncActiveDirectoryUsers();
}
public void SyncActiveDirectoryUsers()
{
try
{
using (var waitHandle = new AutoResetEvent(false))
{
ThreadPool.RegisterWaitForSingleObject(waitHandle, (state, timeout) => { UpdateUsers(); }, null, TimeSpan.FromHours(1), false);
}
}
catch
{
throw;
}
}
}
The UpdateUsers() method should be able to call the applicationDBContext.SaveChanges() method.
How can I ensure that the LDAP manger class can use the Application DB context?
You probably want class LdapManager : BackgroundService, ILdapManager
BackgroundService is .NET Core 2.1, there is a code sample available for core 2.0
Inject IServiceScopeFactory and override Task ExecuteAsync( ), run a while loop there.
while(!stoppingToken.IsCancellationRequested)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
...; // do your stuff
}
await Task.Delay(myConfig.BackgroundDelay, stoppingToken);
}
And here is a good read about this on MSDN, including the code sample for 2.0
For accessing ApplicationDbContext from HostedService.
DbHostedService
public class DbHostedService : IHostedService
{
private readonly ILogger _logger;
public DbHostedService(IServiceProvider services,
ILogger<DbHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Consume Scoped Service Hosted Service is starting.");
DoWork();
return Task.CompletedTask;
}
private void DoWork()
{
_logger.LogInformation("Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var user = context.Users.LastOrDefault();
_logger.LogInformation(user?.UserName);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Consume Scoped Service Hosted Service is stopping.");
return Task.CompletedTask;
}
}
Register DbHostedService
services.AddHostedService<DbHostedService>();

Service Fabric with multiple endpoints and dependency injection

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();
}
}

Asp core, Object reference not set to an instance of an object in Repository pattern

I am working on an asp core 1.1 and i want to create a select repository pattern in my project.
my code in repository class :
public class AuthorReposetory : IDisposable
{
private static ApplicationDbContext _context;
public AuthorReposetory(ApplicationDbContext context)
{
_context = context;
}
public static List<Author> GetAuthorList()
{
List<Author> model = new List<Author>();
model = _context.authors.Select(a => new Author
{
AuthorId = a.AuthorId,
AuthorName = a.AuthorName,
AuthorDescription = a.AuthorDescription
}).ToList();
return model;
}
public void Dispose()
{
throw new NotImplementedException();
}
~AuthorReposetory()
{
Dispose();
}
}
and in controller
[HttpGet]
public IActionResult Index()
{
var q = AuthorReposetory.GetAuthorList();
return View(q);
}
Update
Here is my StartUp Class
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
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)
{
//define Connection string
services.AddDbContext<ApplicationDbContext>(option => option.UseSqlServer(Configuration.GetConnectionString("DefualtConnection")));
//For Create and use identity in database
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add framework services.
services.AddMvc();
services.AddAutoMapper();
services.AddPaging();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
app.UseMvc(routes =>
{
//New Area For Admin
routes.MapRoute(
name: "Admin",
template: "{area:exists}/{controller=Admin}/{action=Index}/{id?}");
//New Area For User
routes.MapRoute(
name: "UserProfile",
template: "{area:exists}/{controller=UserProfile}/{action=Index}/{id?}");
//tranditional Routing
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
My Connection string in appsettings.json
{
"ConnectionStrings": {
"DefualtConnection" : "Data Source=.;Initial Catalog=DataBaseName;User ID = sa; Password = 123"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
The problem is Object reference not set to an instance of an object when run model = _context.authors.Select(a => new Author in repository.
In the above i show controller code, Repository class code and start up code to figure out.
Where can the problem be?
Note : everything is good in controller. just problem is in repository class.
You are using static methods and the constructor is never called as you never create the object. You would have to change your constructor to:
public static AuthorReposetory()
{
_context = new ApplicationDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefualtConnection")));
}
but that's quite bad practice since you have to setup the connection in the constructor and this also created a hard dependency on your context class.
A better solution would be to create a non static class
public class AuthorRepository : IAuthorRepository
{
private ApplicationDbContext _context;
public AuthorRepository(ApplicationDbContext context)
{
_context = context;
}
// ...
}
public interface IAuthorRepository
{
// ...
}
Reverse the dependency with injection:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//define Connection string and setup dependency injection
services.AddDbContext<ApplicationDbContext>(option => option.UseSqlServer(Configuration.GetConnectionString("DefualtConnection")));
services.AddScoped<IAuthorRepository, AuthorRepository>();
// ...
}
Controller:
public HomeController
{
private IAuthorRepository _authorRepository;
public HomeController(IAuthorRepository authorRepository)
{
_authorRepository = authorRepository;
}
[HttpGet]
public IActionResult Index()
{
var q = _autorRepository.GetAuthorList();
return View(q);
}
}
Probably your _context object is null. How is your DI/IoC configured? I would investigate in that way.
Your db context should be added like this :
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext(options => options.UseSqlite("Data Source=blog.db"));
}
Here is the documentation on how to configure your db context: https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
You have not registered your repository class in the services so it is not getting resolved via the container. In your ConfigureServices method try this:
services.AddScoped<AuthorReposetory>();

Dependency injection in asp.net core

I have following code:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IWsApiProvider, WsApiProvider>();
services.AddScoped<IApplicationUserRepository, ApplicationUserRepository>();
...
}
WsApiProvider has following:
public Guid SessionId { get; set; }
public IWSocketProvider WsApi { get; set; }
In Invoke method I'm updating these properties:
public Task Invoke(HttpContext httpContext, IOptions<AppSettings> appSettings)
{
...
this._wsApiProvider.SessionId = sessionGuid;
this._wsApiProvider.WsApi = connection;
...
}
And then I'm going to Controller where I injected Repository:
public AccountController(IApplicationUserRepository applicationUserRepository)
{
this._applicationUserRepository = applicationUserRepository;
}
public ApplicationUserRepository(IWsApiProvider wsApi) : base(wsApi)
{
}
And here I have wsApi object with empty properties. Two questions:
Why in repository constructor I have this object with empty properties?
Is there any way to create one instance of IWsApiProvider for all dependencies per request (non-singleton solution)?
Thank you in advance
UPDATED. The whole middleware class:
public class WsApiMiddleware
{
private readonly RequestDelegate _next;
private readonly IWsApiProvider _wsApiProvider;
private const string QisSessionId = "QisSessionId";
public WsApiMiddleware(RequestDelegate next, IWsApiProvider wsApiProvider)
{
_next = next;
this._wsApiProvider = wsApiProvider;
}
public Task Invoke(HttpContext httpContext, IOptions<AppSettings> appSettings)
{
var sessionId = httpContext.Request.Cookies[QisSessionId];
var sessionGuid = Guid.Empty;
if (!string.IsNullOrEmpty(sessionId))
{
Guid.TryParse(sessionId, out sessionGuid);
}
var connection = ConnectionsPool.GetSocket(sessionGuid);
if (connection == null)
{
connection = new WSocketProvider(null);
var connectTask = Task.Run(async () =>
await connection.Connect(appSettings.Value.WsApiServerEndPointUri, CancellationToken.None)
);
Task.WaitAll(connectTask);
var sessionService = new SessionService(connection);
var sessionOpenTask = Task.Run(async () =>
{
SessionDataState sessionData = null;
//TODO [W-8/6/2017] - think about better solution for situation when sessionId doesn't exist on the server
try
{
sessionData = await sessionService.OpenSession(sessionGuid != Guid.Empty ? (Guid?)sessionGuid : null);
}
catch (Exception ex)
{
sessionData = await sessionService.OpenSession();
}
sessionGuid = sessionData.SessionId;
if (!sessionData.ClientType.HasValue)
{
await sessionService.LoginClient();
}
ConnectionsPool.TryAddConnection(sessionGuid, connection);
httpContext.Response.Cookies.Append(QisSessionId, sessionGuid.ToString());
});
Task.WaitAll(sessionOpenTask);
}
this._wsApiProvider.SessionId = sessionGuid;
this._wsApiProvider.WsApi = connection;
return this._next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class WsApiMiddlewareExtensions
{
public static IApplicationBuilder UseWsApiMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<WsApiMiddleware>();
}
}
From the ASP.Net core middleware doc :
Middleware is constructed once per application lifetime. Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors are not shared with other dependency-injected types during each request.
And the most important part in you situation:
If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by dependency injection.
Since IWsApiProvider is a scoped service(i.e. per request), it should be passed as an argument to the Invoke method, as follow:
public class WsApiMiddleware
{
private readonly RequestDelegate _next;
// no longer passed in the constructor
public WsApiMiddleware(RequestDelegate next)
{
_next = next;
}
// passed as an argument to Invoke, via dependency injection
public Task Invoke(HttpContext httpContext, IWsApiProvider wsApiProvider, IOptions<AppSettings> appSettings)
{
wsApiProvider.SessionId = "SessionId";
wsApiProvider.WsApi = "WsApi";
return this._next(httpContext);
}
}

Resources