0

Is there a way to pass a service provider to constructor of custom attribute, along with other parameters. I would like to use the service provider to get the dbcontext.

I could make the same work using OnAuthorization but that is not my intent as I would like to write the data in db independent of when the page is requested for auth. I could try TypeFilter but would like to explore if there are alternate ways with type safety.

 public class OmsAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
    {
        public enum AccessEnablers
        {
            PostLogin, 
            Anytime, 
            Default
        }
        
        private string _moduleName;
        private string _moduleDescription;
        public AccessEnablers AllowAccess { get; set; }

        private OmsDbContext storageContext;

         public OmsAuthorizeAttribute(string moduleName, string moduleDescription, AccessEnablers OverrideAccess = AccessEnablers.Default)
        {
            _moduleDescription = moduleDescription;
            _moduleName = moduleName;
            AllowAccess = OverrideAccess;
            if (OmsEnvironment.settings.WizardCompleted)
            {
               /* Would like to get the db-context here and call below method */
               //-> Call storage method //
               //  OmsModuleHelper.registerModule(_moduleName, _moduleDescription,  storageContext);
               
               /* I was able to create a new db-context using hard-coded connection string for local-db, but as I move to prod/cloud implementations this doesnt seem the right approach*/
 
                //var option = DbContextOptionsBuilder<OmsDbContext>().UseSqlServer("my-hard-coded-string");
                //storageContext = new OmsDbContext(option.Options);
                //-> Call storage method //
                //OmsModuleHelper.registerModule(_moduleName, _moduleDescription,  storageContext);

                /* Alternatively, I could also get the connection string from configuration but havent been able to find a way to achieve dependency injection along with parameter */
            }
        }



        public void OnAuthorization(AuthorizationFilterContext context)
        {
            // Runtime/On-auth requested works.. 
            var storageContext = (OmsDbContext) 
            context.HttpContext.RequestServices.GetService(typeof(OmsDbContext));
            if(OmsEnvironment.settings.WizardCompleted)
                OmsModuleHelper.registerModule(_moduleName, _moduleDescription, storageContext);

            Printer.print("Authorization Requested for module: " + _moduleName);
            if(!context.HttpContext.User.Identity.IsAuthenticated && AllowAccess != AccessEnablers.Anytime)
                context.Result = new RedirectToActionResult("Login", "Account", context);
            else
            {
                // custom auth logic here. 
            }
        }
    }
/// Edit 11/29 Adding register module code below
public class OmsDbContext : DbContext
    {
        public OmsDbContext(DbContextOptions<OmsDbContext> options) : base(options) { }

 public static void registerModule(OmsModule module, OmsDbContext context)
        {
            _storageContext = context;
            if (!_modules.Any(n => n.moduleName == module.moduleName))
            {
                Printer.print("Registering authorization tagged module with Modulization. Module name: " + module.moduleName);
                _modules.Add(module);
                if (!_storageContext.OmsModules.Any(n => n.moduleName == module.moduleName))
                {
                    _storageContext.OmsModules.Add(module);
                    _storageContext.SaveChanges();
                }
            }
        }

        public static void registerModule(string mouduleName, string moduleDescription, OmsDbContext context)
                => registerModule(new OmsModule()
                {
                    //OmsModuleDetailsID = moduleID,
                    moduleName = mouduleName,
                    moduleDescription = moduleDescription,
                }, context);
}
ROMSCore
  • 1
  • 1

1 Answers1

0

As Shaun Luttin says, we cannot use dependency injection to inject a dependency into an attribute. Attributes are for metadata not behavior. [AttributeSpecification()] encourages this by forbidding reference types as arguments.

Normally, we will just set some parameter in the constructor method and then we will use these parameters in the method. As your question shows , you could get the dbcontext in the OnAuthorization method.

The only way to use DI in attribute in asp.net core is using filter, more details about how to use filter, you could refer to this article.

Brando Zhang
  • 22,586
  • 6
  • 37
  • 65
  • Thanks Brando. While I agree attributes are not for behavior, I found it as a really simple way to populate Modules/Pages for authorization automatically at code-start in the database. Maybe there are better ways to do this. I did manage to make this work. Initially, my config string was local but as I moved to Azure the string was overriden. I simply added the config sting in startup.cs to my static global class and read it from there. – ROMSCore Dec 09 '20 at 19:06