I want to inject to Hangfire AuthorizationFilter my custom serice and do something with it an i face problem my service is always null.
Owin startup class :
public class Startup
{
public void Configuration(IAppBuilder app )
{
GlobalConfiguration.Configuration
.UseSqlServerStorage("SomeContext")
.UseNinjectActivator(new Bootstrapper().Kernel);
app.UseHangfireDashboard("/hangfire", new DashboardOptions { AuthorizationFilters = new[] { new RestrictiveAuthorizationFilter() } });
app.UseHangfireServer();
}
}
Custom filter :
public class RestrictiveAuthorizationFilter : IAuthorizationFilter
{
[Inject]
public IUserService _userService { get; set; }
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
//do something with _userService but it is always null
return true;
}
}
Ninject kernel :
private static void RegisterServices(IKernel kernel)
{
kernel.Bind(x => { x.From(typeof(BookService).Assembly).SelectAllClasses().EndingWith("Service").BindDefaultInterface(); });
kernel.Bind<LibraryDBContext>().ToSelf().InRequestScope();
kernel.Bind<IBackgroundJobClient>().To<BackgroundJobClient>();
}
I don't see my error could you help me ?
You can pass the kernel as an argument to RestrictiveAuthorizationFilter constructor and ask for the IUserService.
public class RestrictiveAuthorizationFilter : IAuthorizationFilter
{
public IUserService _userService { get; set; }
public RestrictiveAuthorizationFilter(IKernel kernel)
{
_userService = kernel.Get<IUserService>()
}
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
//do something with _userService but it is always null
return true;
}
}
Related
When I enter URL localhost:xxx/api/Customers, it showed "is null". I looked at the variable in debugger mode, it show the table customers object is null;
Remember there is data in the table. Any idea?
Customer class
public class Customer
{
public string Id { get; set; }
public string lName { get; set; }
}
appsettings.json
{
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=.\\sqlexpress;Database=EF7;Trusted_Connection=True;"
}
}
AppDbContext
public class AppDbContext : DbContext
{
public DbSet<Customer> Customers;
protected override void OnModelCreating(ModelBuilder Builder)
{
Builder.Entity<Customer>().HasKey(c => c.Id);
base.OnModelCreating(Builder);
}
}
CustomersController
[Route("api/[controller]")]
public class CustomersController : Controller
{
AppDbContext _ctx { get; set; }
public CustomersController([FromServices] AppDbContext ctx)
{
_ctx = ctx;
}
// GET: api/values
[HttpGet]
public JsonResult Get()
{
if(_ctx.Customers!=null)
return Json(_ctx.Customers.ToList());
else {
return Json("is null");
}
}
}
Startup.cs
public class Startup
{
IConfigurationRoot Configuration;
public Startup(IHostingEnvironment env)
{
var Builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = Builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddEntityFramework().AddSqlServer().AddDbContext<AppDbContext>(o=>o.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
app.UseIISPlatformHandler();
app.UseMvc();
}
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
Never mind, my Dbset is a field instead of property. So I just changed to DbSet Customer {get;set}
Here is my project responsible for data:
public interface IRepository<T> where T : class
{
IQueryable<T> All();
T GetById(int id);
void Add(T entity);
void Update(T entity);
....
}
EF implement IRepository
public class GenericRepository<T> : IRepository<T> where T : class
{
public GenericRepository() : this(new ApplicationDbContext())
{
}
public GenericRepository(DbContext context)
{
if (context == null)
{
throw new ArgumentException("An instance of DbContext is required to use this repository.", "context");
}
this.Context = context;
this.DbSet = this.Context.Set<T>();
}
protected IDbSet<T> DbSet { get; set; }
protected DbContext Context { get; set; }
public virtual IQueryable<T> All()
{
return this.DbSet.AsQueryable();
}
public virtual T GetById(int id)
{
return this.DbSet.Find(id);
}........
I use unit of work pattern
public interface IUowData : IDisposable
{
IRepository<House> Houses { get; }
IRepository<Floor> Floors { get; }
...
int SaveChanges();
}
And this is his implementation
public class UowData:IUowData
{
private readonly DbContext context;
private readonly Dictionary<Type, object> repositories = new Dictionary<Type, object>();
public UowData(){}
public UowData(DbContext context)
{
this.context = context;
}
private IRepository<T> GetRepository<T>() where T : class
{
if (!this.repositories.ContainsKey(typeof(T)))
{
var type = typeof(GenericRepository<T>);
this.repositories.Add(typeof(T), Activator.CreateInstance(type, this.context));
}
return (IRepository<T>)this.repositories[typeof(T)];
}
public int SaveChanges()
{
return this.context.SaveChanges();
}
public void Dispose()
{
this.context.Dispose();
}
The second project is of type Web API through which I try to access data from database:
public class ArduinoController : ApiController
{
private IEmailSender sender;
private IUowData data;
public ArduinoController(IEmailSender sender, IUowData data)
{
this.sender = sender;
this.data = data;
}
[HttpPost]
[ActionName("PostAlarm")]
public void PostAlarm(dynamic sensorJson)
{
var alartModel = this.data.Sensors.All()
.....
When I try to use Ninject dependancy resolve i get exception:
"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.".
Everything works fine if I fetch one page at a time. I am using a simple tool 'XENU' to fetch multiple pages simultaneously. This is when I get errors with DBContext by fetching multiple pages at a time.
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
public static void Stop()
{
bootstrapper.ShutDown();
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new Ninject.WebApi.DependencyResolver.NinjectDependencyResolver(kernel);
return kernel;
}
private static void RegisterServices(IKernel kernel)
{
EmailSettings emailSettings = new EmailSettings
{
ServerName = Settings.Default.EmailServerName,
...
};
kernel.Bind<IEmailSender>().To<EmailSender>().WithConstructorArgument("settings", emailSettings);
kernel.Bind<IUowData>().To<UowData>().InRequestScope().WithConstructorArgument("context",new ApplicationDbContext());
}
}
.WithConstructorArgument("context",new ApplicationDbContext());
results in one DB Context for the whole application. Remove that and add a binding instead.
.Bind<DbContext>().To<ApplicationDbContext>().InRequestScope();
I have an interface and a class that implements that interface
public interface ISessionVariables
{
bool IsSessionTokenValidated { get; set; }
}
public class HttpContextSessionVariablesAdapter : ISessionVariables
{
private T Get<T>(string key)
{
return (T)HttpContext.Current.Session[key];
}
private void Set(string key, object value)
{
HttpContext.Current.Session[key] = value;
}
public bool IsSessionTokenValidated
{
get { return Get<bool>("IsSessionTokenValidated"); }
set { Set("IsSessionTokenValidated", value); }
}
}
I bind the interface to implementation using Ninject:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISessionVariables>().To<HttpContextSessionVariablesAdapter>();
}
I have an ActionFilter
public class ValidateSessionTokenAttribute : ActionFilterAttribute
{
[Inject]
public ISessionVariables SessionVariables { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (SessionVariables.IsSessionTokenValidated == false)
{
throw new HttpException((int)HttpStatusCode.Unauthorized, "User session has expired");
}
base.OnActionExecuting(filterContext);
}
}
Strange bug:
If i add the ValidateSessionTokenAttribute to an ActionResult, SessionVariables property gets populated.
[ValidateSessionToken]
public ActionResult Index()
{
}
If i bind the same filter to all ActionResults, (in FilterConfig), the SessionVariables property is always null.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ValidateSessionTokenAttribute());
}
}
What i am doing wrong?
In MVC4,I inject the Controller by using the Ninject and I want to test the "Index".
Injection of the writing(The controller for injection):
public class NinjectDependencyResolver : IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver()
{
kernel = new StandardKernel();
AddBindings();
}
private void AddBindings()
{
kernel.Bind<IDB>().To<DB>();
}
public object GetService(Type serviceType)
{
return kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return kernel.GetAll(serviceType);
}
}
The IDB:
public interface IDB
{
IBugTrackRepository iBugTrackRepository { get; }
ICategoryRepository iCategoryRepository { get; }
...
...
IUserRepository iUserRepository { get; }
}
To achieve:
public class DB : IDB
{
public IBugTrackRepository iBugTrackRepository
{
get { return new BugTrackRepository(); }
}
public ICategoryRepository iCategoryRepository
{
get { return new CategoryRepository(); }
}
...
...
public IUserRepository iUserRepository
{
get { return new UserRepository(); }
}
}
To achieve:
public class BugTrackRepository : IBugTrackRepository
{
private DBEntities context = new DBEntities ();
public IQueryable<BugTrack> bugtrack
{
get { return context.BugTrack; }
}
...
//Other database operations...
}
The Controller:
public class HomeController : Controller
{
private IDB repository;
public HomeController(IDB repo)
{
repository = repo;
}
public ActionResult Index()
{
ViewBag.mytitle = "Home Page";
return View();
}
}
The Test Code:
[TestMethod]
public void TestIndex()
{
HomeController controller = new HomeController(??);
ViewResult result = controller.Index() as ViewResult;
Assert.AreEqual("Home Page", result.ViewBag.mytitle);
}
but this test is wrong and I don't konw how to instantiate this Controller.Please help me!Have any questions please leave a message.
Your Home's constructor contains IDB interface, so you need to pass it there.
As you have interface you can use mock objects (https://stackoverflow.com/questions/37359/what-c-sharp-mocking-framework-to-use) to imitate your IDB.
My question is very similar to this one: MVC3 tool using Entity Framework caching issues with Ninject however my mapping is a bit more complex and when I use InRequestScope I get the following error:
The operation cannot be completed because the DbContext has been disposed.
If I don't include InRequestScope everything works except EF Code First seems to cache my entities and it doesn't match up to the values in the Db.
Here's my ninject mapping I'm using the ninject mvc3 nuget package (without InRequestScope):
kernel.Bind<MyContext>()
.ToSelf()
.WithConstructorArgument("connectionString", context => MvcApplication.GetConnectionStringName);
kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
// Service Layer.
kernel.Bind<ICustomerService>().To<CustomerService>();
kernel.Bind<IMessageService>().To<MessageService>();
kernel.Bind<IUserService>().To<UserService>();
// Repository Layer.
kernel.Bind<IRepository<Customer>>().To<GenericRepository<Customer>>();
kernel.Bind<IRepository<Message>>().To<GenericRepository<Message>>();
kernel.Bind<IRepository<User>>().To<GenericRepository<User>>();
NinjectContainer.Initialize(kernel);
My IUnitOfWork
public interface IUnitOfWork
{
IUserService UserService { get; }
ICustomerService CustomerService { get; }
IMessageService MessageService { get; }
void CommitChanges();
}
My UnitOfWork
public class UnitOfWork : IUnitOfWork
{
MyContext _context;
private readonly IUserService _userService;
private readonly ICustomerService _customerService;
private IMessageService _messageService;
public UnitOfWork(IUserService userService,
ICustomerService customerService,
IMessageService messageService,
MyContext context)
{
_userService = userService;
_customerService = customerService;
_messageService = messageService;
SetContext(optimaContext);
}
private void SetContext(MyContext context)
{
_context = context;
_userService.Context = _context;
_customerService.Context = _context;
_messageService.Context = _context;
}
public void CommitChanges()
{
_context.SaveChanges();
}
public IUserService UserService { get { return _userService; } }
public ICustomerService CustomerService { get { return _customerService; } }
public IMessageService MessageService { get { return _messageService; } }
}
My ICustomerService
public interface ICustomerService
{
DbContext Context { get; set; }
IQueryable<Customer> All();
}
My CustomerService
public class CustomerService : ICustomerService
{
IRepository<Customer> _customerRepo;
public CustomerService(IRepository<Customer> customerRepo)
{
_customerRepo = customerRepo;
}
private DbContext _context;
public DbContext Context
{
get { return _context; }
set { _context = value; _customerRepo.Context = value; }
}
public IQueryable<Customer> All()
{
return _customerRepo.All();
}
}
My other services follow a similar patter.
My IRepository
public interface IRepository<T> where T : class, new()
{
DbContext Context { get; set; }
T Single(Expression<Func<T, bool>> expression);
T Find(object id);
IQueryable<T> All();
void Delete(Expression<Func<T, bool>> expression);
void Delete(T item);
}
My Repository
public class GenericRepository<T> : IRepository<T> where T : class, new()
{
DbContext _context;
public DbContext Context
{
get { return _context; }
set { _context = value; }
}
public virtual T Single(Expression<Func<T, bool>> expression)
{
return All().FirstOrDefault(expression);
}
public virtual T Find(object id)
{
return _context.Set<T>().Find(id);
}
public virtual IQueryable<T> All()
{
return _context.Set<T>();
}
public virtual void Delete(Expression<Func<T, bool>> expression)
{
var items = All().Where(expression);
foreach (var item in items)
{
Delete(item);
}
}
public virtual void Delete(T item)
{
_context.Set<T>().Remove(item);
}
}
If anyone can help with the Ninject mapping and the correct way to inject classes it would be greatly appreciated.
I found the problem, I was using the [Inject] attribute with a FilterAttribute when the attribute was being called it was before my context had been initialised and that producted the dbContext error.
I followed the wiki on the ninject github site here to setup ninject on FilterAttribute. On issue I did have was finding the BindFilter method, this is hidden away Ninject.Web.Mvc.FilterBindingSyntax namespace.
My ninject mappings now look like:
kernel.Bind<MyContext>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument("connectionString", context => MvcApplication.GetConnectionStringName);
kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
// Service Layer.
kernel.Bind<ICustomerService>().To<CustomerService>();
kernel.Bind<IMessageService>().To<MessageService>();
kernel.Bind<IUserService>().To<UserService>();
// Repository Layer.
kernel.Bind<IRepository<Customer>>().To<GenericRepository<Customer>>();
kernel.Bind<IRepository<Message>>().To<GenericRepository<Message>>();
kernel.Bind<IRepository<User>>().To<GenericRepository<User>>();
// Attributes
kernel.BindFilter<AuthorizeWithTokenAttribute>(FilterScope.Controller, 0)
.WhenControllerHas<AuthorizeWithTokenFilter>()
.WithConstructorArgumentFromControllerAttribute<AuthorizeWithTokenFilter>("roles", attribute => attribute.Roles)
.WithConstructorArgumentFromControllerAttribute<AuthorizeWithTokenFilter>("users", attribute => attribute.Users);