I have an ASP.NET Web Application (.NET Framework 4.8) in which I've set up NInject but any services I set up with InRequestScope are coming through as if transient scope (i.e. new instances are created for every entity that depends a dependency on it - within the same web request).
The NuGet packages I am using are as follows (latest):
Ninject v3.3.4
Ninject.Web.Common v.3.32 ("Bootstrapper for web projects")
App_Start\Ninject.Web.Common.cs is present and correct and is as follows:
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(ScormWebService.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(ScormWebService.App_Start.NinjectWebCommon), "Stop")]
namespace ScormWebService.App_Start
{
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
using Ninject.Web.Common.WebHost;
using Services;
using System;
using System.Web;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
public static IKernel Kernel { get; private set; }
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
kernel.Bind<IDumpConfigService>().To<DumpConfigService>().InSingletonScope();
// These objects are created fresh for each request
kernel.Bind<ILogService>().To<LogService>().InRequestScope();
kernel.Bind<IDumpService>().To<DumpService>().InRequestScope();
kernel.Bind<ISaveDataRequestReader>().To<SaveDataRequestReaderXml>().InRequestScope();
kernel.Bind<ISaveDataResponseWriter>().To<SaveDataResponseWriterXml>().InRequestScope();
kernel.Bind<IHttpContextAccessor>().To<HttpContextAccessor>().InRequestScope();
Kernel = kernel;
}
}
}
The incoming request is actually an implementation of IHttpHandler (i.e. an ashx rather than aspx file). However, it is still a page request with a current request and an HttpContext.Current.
Here is how I am setting up the entities for the page request
public class SaveDataHandler : IHttpHandler, IRequiresSessionState
{
/// <summary>
/// A new handler is required for each and every incoming request
/// </summary>
public bool IsReusable => false;
public SaveDataHandler()
{
var kernel = App_Start.NinjectWebCommon.Kernel;
LogService = (ILogService)kernel.GetService(typeof(ILogService));
Reader = (ISaveDataRequestReader)kernel.GetService(typeof(ISaveDataRequestReader));
Writer = (ISaveDataResponseWriter)kernel.GetService(typeof(ISaveDataResponseWriter));
DumpService = (IDumpService)kernel.GetService(typeof(IDumpService));
}
}
So for example, three instances of ILogService are created per request during the SaveDataHandler constructor instead of one: SaveDataHandler itself requests it (see above) as does class DumpService : IDumpService and class SaveDataRequestReaderXml : ISaveDataRequestReader.
Can anyone provide insight as to why InRequestScope is acting like a transient scope? I suspect the cause is using a IHttpHandler (ashx) instead of Web Form (aspx) page but I can't see why that wouldn't work as HttpContext.Current is the same across the request and that is what NInject.Web.Common uses as a request scope identifier. I've created a WebForm.aspx page but the same issue occurs for this too so it's not specific to ashx/IHttpHandler requests:
namespace ScormWebService
{
public partial class WebForm1 : Page
{
protected void Page_Init(object sender, EventArgs e)
{
var kernel = App_Start.NinjectWebCommon.Kernel;
var LogService = (ILogService)kernel.GetService(typeof(ILogService));
var Reader = (ISaveDataRequestReader)kernel.GetService(typeof(ISaveDataRequestReader));
var Writer = (ISaveDataResponseWriter)kernel.GetService(typeof(ISaveDataResponseWriter));
var DumpService = (IDumpService)kernel.GetService(typeof(IDumpService));
// At this point, three instances of LogService have been created.
}
}
}
Edit: I've created a fresh minimal ASP.NET Web Forms project that reproduces the problem which you can download here but all the essential elements are already described in the code above.
Thanks.
Related
I have a .net core winform application and am implementing n-tier architecture(ApplicationLayer(winform), BLL, DAL)
Installed MediatR and MediatR.Extensions.Microsoft.DependencyInjection
I am currently following this site:
https://dotnetcoretutorials.com/2019/04/30/the-mediator-pattern-part-3-mediatr-library/
Where do I put this code
public void ConfigureServices(IServiceCollection services)
{
services.AddMediatR(Assembly.GetExecutingAssembly());
//Other injected services.
}
I have tried putting it on Main() like so:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(IServiceCollection services)
{
services.AddMediatR(Assembly.GetExecutingAssembly());
services.AddTransient<IApplicationHandler, ApplicationHandler>();
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
and its giving me this error
Program does not contain a static 'Main' method suitable for an entry point
The Main() method is the entry point of your application and thus cannot be modified. As you were adding a parameter to it, the compiler tells that the it could not find the Main() (parameterless) method.
If you want to work with dependency injection + windows forms some additional steps are needed.
1 - Install the package Microsoft.Extensions.DependencyInjection. Windows Forms doesn't have DI capabilities natively so we need do add it.
2 - Change your Program.cs class to be like this
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// This class will contains your injections
var services = new ServiceCollection();
// Configures your injections
ConfigureServices(services);
// Service provider is the one that solves de dependecies
// and give you the implementations
using (ServiceProvider sp = services.BuildServiceProvider())
{
// Locates `Form1` in your DI container.
var form1 = sp.GetRequiredService<Form1>();
// Starts the application
Application.Run(form1);
}
}
// This method will be responsible to register your injections
private static void ConfigureServices(IServiceCollection services)
{
// Inject MediatR
services.AddMediatR(Assembly.GetExecutingAssembly());
// As you will not be able do to a `new Form1()` since it will
// contains your injected services, your form will have to be
// provided by Dependency Injection.
services.AddScoped<Form1>();
}
}
3 - Create your Command Request
public class RetrieveInfoCommandRequest : IRequest<RetrieveInfoCommandResponse>
{
public string Text { get; set; }
}
4 - Create your Command Response
public class RetrieveInfoCommandResponse
{
public string OutputMessage { get; set; }
}
5 - Create your Command Handler
public class RetrieveInfoCommandHandler : IRequestHandler<RetrieveInfoCommandRequest, RetrieveInfoCommandResponse>
{
public async Task<RetrieveInfoCommandResponse> Handle(RetrieveInfoCommandRequest request, CancellationToken cancellationToken)
{
RetrieveInfoCommandResponse response = new RetrieveInfoCommandResponse();
response.OutputMessage = $"This is an example of MediatR using {request.Text}";
return response;
}
}
6 - Form1 implementation
public partial class Form1 : Form
{
private readonly IMediator _mediator;
public Form1(IMediator mediator)
{
_mediator = mediator;
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
var outputMessage = await _mediator.Send(new RetrieveInfoCommandRequest
{
Text = "Windows Forms"
});
label1.Text = outputMessage.OutputMessage;
}
}
Working code
I'd never thought about using MediatR along Windows Forms, it was a nice study case. Nice question =)
I'm receiving the following error when executing the web api controller.
I receive this error:
Error loading Ninject component ICache\r\nNo such component has been
registered in the kernel's component container.
The controller I am calling has several Interfaces which are used by the controller.
My controller constructor:
private ISDKConfiguration iSDKConfiguration;
private ISDKLogin iSDKLogin;
private ISDKLogout iSDKLogout;
private ISDKRetrieveUserSession iSDKRetrieveUserSession;
public SDKAccountController(ISDKConfiguration sdkConfiguration, ISDKLogin sdkLogin, ISDKLogout sdkLogout, ISDKRetrieveUserSession sdkRetrieveUserSession)
{
iSDKConfiguration = sdkConfiguration;
iSDKLogin = sdkLogin;
iSDKLogout = sdkLogout;
iSDKRetrieveUserSession = sdkRetrieveUserSession;
}
My HTTP Post Method
[HttpPost]
[Route("Login")]
public SDKUserDTO Login([FromBody]SDKUserDTO sdkUserDTO)
{
iSDKConfiguration.Load();
iSDKLogin.SDKUserDTO = sdkUserDTO;
iSDKLogin.Process();
iSDKRetrieveUserSession.SDKUserDTO = sdkUserDTO;
sdkUserDTO = iSDKRetrieveUserSession.Process();
return sdkUserDTO;
}
The NinjectWebCommon.cs which was included when I downloaded the Ninject.Web.Common.WebHost nuget package.
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>
/// The created kernel.
/// </returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new Ninject.Web.WebApi.NinjectDependencyResolver(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">
/// The kernel.
/// </param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISDKConfiguration>().To<SDKConfiguration>();
kernel.Bind<ISDKLogin>().To<SDKLogin>();
kernel.Bind<ISDKLogout>().To<SDKLogout>();
kernel.Bind<ISDKRetrieveUserSession>().To<SDKRetrieveUserSession>();
}
}
I've edited as per several articles online to include this line of code
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new Ninject.Web.WebApi.NinjectDependencyResolver(kernel);
I'm expecting to return the following json.
{
"UserID": "dhu",
"Password": "password",
"Identifier": "LoginTestUsingPostman"
}
But it's throwing the above exception.
I need to create a class that enable to Intercept httpRequest within the application (so no need for proxy) and log request and response details.
Create HttpRequest can created using any way (HttpClient, HttpWebRequest, Helper library like RestSharp .. anything), that's way I hope to create dynamic class contains event that fire when HttpRequest initialized.
I already use Network Tracing but all I can log are strings, So Is there a safer way to intercept request and response objects instead of parsing strings
public class DebugTraceListener : System.Diagnostics.TextWriterTraceListener
{
public override void Write(string message)
{
using (StreamWriter w = File.AppendText("D:\\Log\\log2.txt"))
{
Log(message, w);
}
}
public override void WriteLine(string message)
{
using (StreamWriter w = File.AppendText("D:\\Log\\log2.txt"))
{
Log(message, w);
}
}
public static void Log(string logMessage, TextWriter w)
{
w.WriteLine(" :{0}", logMessage);
w.WriteLine("-------------------------------");
}
}
I am assuming you are using asp.net webforms or asp.net mvc. You need to create a class which extends the class HttpApplication and implements the interface IHttpModule
I have created the following module:
using System.Web;
namespace WebApplication1.App_Start
{
using System;
public class RequestHandler : HttpApplication,IHttpModule
{
/// <summary>
/// Initializes a module and prepares it to handle requests.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application </param>
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
context.EndRequest += context_RequestCompleted;
}
void context_RequestCompleted(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var context = application.Context;
var response = context.Response;
context.Response.Write(response.Charset);
}
void context_BeginRequest(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var context = application.Context;
var url = context.Request.Url;
context.Response.Write(url.AbsoluteUri);
}
/// <summary>
/// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
/// </summary>
public void Dispose()
{
throw new NotImplementedException();
}
}
}
Next, register the HttpModule in your web.config by adding the following line in under :
<system.webServer>
<modules>
<add name="Handler" type="WebApplication1.App_Start.RequestHandler"/>
<remove name="FormsAuthenticationModule" />
</modules>
</system.webServer>
Every request you make, it will write the url and the charset of the response received.
replace the following line by your logging instructions:
context.Response.Write(url.AbsoluteUri);
context.Response.Write(response.Charset);
for e.g.:
Logger.Log(url.AbsoluteUri) //in context_BeginRequest
Logger.Log(response.ContentType) //in context_RequestCompleted
You can log different attributes of the response and url variable which i have defined above.
Hope it helps
I am using Ninject as dependency injection tool. I have quite big asp.net-mvc 5 project (80 bindings) which, when I step through my code creates the bindings perfectly in the kernel.The NinjectWebCommon.cs. file sporadically failed on the following line of code
(kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);)
resulting in a new kernel being created and me losing all bindings to my SQL Server database.
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
AddBindings(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
The result is that when I execute the code, I get a NULL model error in the view. The error has corrected itself automagically but I need to know what causes it so that I can rather deal with it. I include the full NinjectWebCommon.cs file
using eMedic.Domain.Abstract;
using eMedic.Domain.Concrete;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(eMedic.WebUI.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(eMedic.WebUI.App_Start.NinjectWebCommon), "Stop")]
namespace eMedic.WebUI.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
AddBindings(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
private static void AddBindings(IKernel kernel)
{
// add bindings here
kernel.Bind<IAccountRepository>().To<EFAccountRepository>();
kernel.Bind<IAddressRepository>().To<EFAddressRepository>();
kernel.Bind<IAddressTypeRepository>().To<EFAddressTypeRepository>();
//other bindings as well (omitted for brevity)
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
System.Web.Mvc.DependencyResolver.SetResolver(new eMedic.WebUI.Infrastructure.NinjectDependencyResolver(kernel));
}
}
}
As well as the NinjectDependencyResolver.cs class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Ninject;
using eMedic.Domain.Abstract;
using eMedic.Domain.Concrete;
namespace eMedic.WebUI.Infrastructure
{
public class NinjectDependencyResolver : IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernelParam)
{
kernel = kernelParam;
}
public object GetService(Type serviceType)
{
return kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return kernel.GetAll(serviceType);
}
}
}
The data interface
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace eMedic.Domain.Abstract
{
public interface IAddressRepository
{
IEnumerable<address> Address { get; }
}
}
and the other data interface...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using eMedic.Domain.Abstract;
namespace eMedic.Domain.Concrete
{
public class EFAddressRepository:IAddressRepository
{
private EFDbContext context = new EFDbContext();
public IEnumerable<address> Address
{
get { return context.address; }
}
}
}
I really would appreciate assistance so that I can get to the root cause of the error.
I managed to figure out what the problem was and for reference I add the reason here:
I had an error with a foreign key (FK) value in one of the tables I was referencing in my SQL Server database. It had a NULL value and when I changed the record to give that specific FK in that specific table a value, the error was cleared.
I need to convert a Kentico 7 web application to Kentico 8.0.21. The old code has a CMSModuleLoader file in the App_Code folder which has code for Authenticate_Execute event.
The init event suggested by kentico does not get fired
public partial class CMSModuleLoader
{
private class AuthenticationHandler : CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
// Assigns a handler to the SecurityEvents.Authenticate.Execute event
// This event occurs when users attempt to log in on the website
SecurityEvents.Authenticate.Execute += OnAuthentication;
}
private void OnAuthentication(object sender, AuthenticationEventArgs args)
{
if (args.User != null) //the authenticate was successful
{
try
{
var accountFacade = WebContainer.Instance.Container.GetInstance<IAccountFacade>();
accountFacade.ReconcileOnLogin(args.UserName);
}
catch (Exception e)
{
var logger = LogManager.GetCurrentClassLogger();
var ex = new Exception("IAccountFacade.ReconcileOnLogin method throw an error communicating with dynamics, the issue is not resolvable from Kentico thus regardless of the permission level of the current user, the exception will be bubbled up and the user will be shown error details or the custom error page.", e);
logger.Fatal(x => x("The current exception is caused by dynamics/data problems and the user will not be allowed to login. A system admin with access to dynamics is required to resolve the problem.", e));
throw ex;
}
//ResetPasswordAttempts(args.User);
}
}
}
/// <summary>
/// Attribute class that ensures the loading of custom handlers
/// </summary>
private class CustomSecurityEventsAttribute : CMS.Base.CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
SecurityEvents.Authenticate.Execute += new EventHandler<AuthenticationEventArgs>(Authenticate_Execute);
}
/// <summary>
/// called on every kentico authenticate attempt
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
All authentication-related events were moved to CMS.Membership.SecurityEvents in Kentico 8.0. The usage is as follows:
using System.Data;
using CMS.Base;
using CMS.Membership;
using CMS.DataEngine;
[AuthenticationHandler]
public partial class CMSModuleLoader
{
/// <summary>
/// Custom attribute class.
/// </summary>
private class AuthenticationHandler : CMSLoaderAttribute
{
/// <summary>
/// Called automatically when the application starts
/// </summary>
public override void Init()
{
// Assigns a handler to the SecurityEvents.Authenticate.Execute event
// This event occurs when users attempt to log in on the website
SecurityEvents.Authenticate.Execute += OnAuthentication;
}
}
}
For more info refer to the documentation.