Property injector in controller and dbcontext - autofac

I'm have my BaseController, and have a property RunTimeContext
public class BaseController : Controller
{
public IRunTimeContext RunTimeContext {get;set;}
}
my method OnActionExecuting using RunTimeContext
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
_logRequest = new LogRequest
{
TenantId = RunTimeContext.TenantId
}
base.OnActionExecuting(filterContext);
}
But, my RunTimeContext is ever null
My autofac configuration is:
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<RunTimeContext>().As<IRunTimeContext>().PropertiesAutowired();

You should add .PropertiesAutowired() to your controllers registration, not to your RunTimeContext type registration, as you want controller's property to be set, not RunTimeContext:
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();

Related

Inheriting logging services from base class

I have a .Net Core 3.1 API controller with a constructor that looks like this:
public class MachineListsController : ControllerBase
{
private readonly jiWeb_ProdContext _context;
private readonly ILogger _logger;
private readonly ILoggingMessageService _loggingMessage;
public MachineListsController(jiWeb_ProdContext context, ILogger<MachineListsController> logger, ILoggingMessageService loggingMessage)
{
_context = context;
_logger = logger;
_loggingMessage = loggingMessage;
}
public string Message { get; set; }
...
}
You can see that I am injecting a .Net Core logging service and the database context into it.
Then I use the logging like this in my controller methods:
[HttpGet("FactoryMachines/{factoryId}")]
public async Task<ActionResult<IEnumerable<MachineList>>> GetMachinesForFactory(Guid factoryId)
{
var machineList = await _context.MachineList.Where(n => n.FactoryId == factoryId).ToListAsync();
Message = _loggingMessage.GetLogSuccess(this.GetType().Name.ToString(), ControllerActions.GetAction, "FactoryMachines", factoryId.ToString());
_logger.LogInformation(Message);
return machineList;
}
The logging is working great, but I'm realizing that I should create a base class that handles logging so I don't have to add or change it inside of every controller I write.
So I started to write this base controller:
[ApiController]
public class MyBaseController : ControllerBase
{
readonly jiWeb_ProdContext _context;
readonly ILogger _logger;
readonly ILoggingMessageService _loggingMessage;
public BaseController(jiWeb_ProdContext context, ILogger<BaseController> logger, ILoggingMessageService loggingMessage)
{
_context = context;
_logger = logger;
_loggingMessage = loggingMessage;
}
}
Then I changed my controller to inherit from it like this:
public class MachineListsController : MyBaseController
{
[HttpGet("FactoryMachines/{factoryId}")]
public async Task<ActionResult<IEnumerable<MachineList>>> GetMachinesForFactory(Guid factoryId)
{
var machineList = await _context.MachineList.Where(n => n.FactoryId == factoryId).ToListAsync();
return machineList;
}
}
But I'm getting error and I'm unsure of what to do on the next step.
Here's the error:
There is no argument given that corresponds to the required formal parameter 'context' of 'BaseController.BaseController(jiWeb_ProdContext, ILogger<BaseController>, ILoggingMessageService)'
Specifically, how do I set up my controllers so that they can just use the base class for logging so I don't have to write logging code for every new controller action I create?
Thanks!
As far as I know, if the base class constructor method contains value, we should pass it in the subclass constructor method and also you should follow Nkosi comment to modify the property to protected.
More details, you could refer to below codes:
[ApiController]
public class MyBaseController : ControllerBase
{
protected readonly ILogger _logger;
public MyBaseController(ILogger<MyBaseController> logger)
{
_logger = logger;
}
}
[Route("api/[controller]")]
public class MachineListsController : MyBaseController
{
public MachineListsController(ILogger<MyBaseController> logger) :base(logger)
{
}
[HttpGet]
public IActionResult Get() {
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace,"aaa" );
return Ok();
}
}
I am wondering, would there be a way to do the logging in the base class? Like where you call _logger.Log in the MachineListsController class, could that be moved to base?
As far as I know, we could only add logs before the MachineListsController's action executed or after the MachineListsController's action executed.
If this match your requirement, you could try to use action filter.
You could add iactionfilter interface to the basecontroller and overried the OnActionExecuted and OnActionExecuting method.
More details, you could refer to below codes:
[Route("api/[controller]")]
[ApiController]
public class MyBaseController : ControllerBase, IActionFilter
{
protected readonly ILogger _logger;
public MyBaseController(ILogger<MyBaseController> logger)
{
_logger = logger;
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace, "aaa");
int i = 0;
}
public void OnActionExecuting(ActionExecutingContext context)
{
_logger.Log(Microsoft.Extensions.Logging.LogLevel.Trace, "bbb");
int i = 0;
}
}
Result:

How to initialize and use Custom / Support classes in MVC 6 with Asp.Net Core 2.0

public sealed class SessionContext
{
private ISession httpContext;
public SessionContext(ISession httpContext)
{
this.httpContext = httpContext;
}
public string UserType
{
get
{
return httpContext.GetString("_UserType");
}
set
{
httpContext.SetString("_UserType", value);
}
}
...... More properties .....
}
public class HomeController : Controller
{
private AppSettings _appSettings;
private SessionContext session = null;
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession httpContext => _httpContextAccessor.HttpContext.Session;
//I don't like this constructor as it is getting initialize or every controller call.
public HomeController(IOptions<AppSettings> myAppSettings, IHttpContextAccessor httpContextAccessor)
{
_appSettings = myAppSettings.Value;
_httpContextAccessor = httpContextAccessor;
appSettings = new AppSettings(_appSettings); //Should initialize only once.
session = new SessionContext(httpContext);
}
}
I have questions regarding ...
How to initialize and use Custom / Support classes in MVC 6 with Asp.Net Core 2.0
When I Initialize these classes, they getting initialize or every controller call. That is very redundant.
my SessionContext class is getting re-initialize every time. So I am loosing the values when I call this class from another controller.
I tried this approach but, not much of use.
services.AddSingleton();
Move from question to answer:
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.AddSingleton<SessionContext, SessionContext>();
//calling the extension class to instantiate the classes which we require earlier.
services.AddMyProjectHelper(Configuration)
}
Created a extension class... where it initializes the support classes
public static class MyProjectHelperExtensions
{
public static IServiceCollection AddMyProjectHelper(this IServiceCollection services, IConfiguration configuration)
{
var section = configuration.GetSection("AppSettings");
// we first need to create an instance
var settings = new AppSettings();
// then we set the properties
new ConfigureFromConfigurationOptions<AppSettings>(section).Configure(settings);
var session = services.BuildServiceProvider().GetService<SessionContext>();
// then we register the instance into the services collection
services.AddSingleton<MyProjectHelper>(new MyProjectHelper(settings, session));
return services;
}
}
finally controller ctor uses the DI for the required class. Now I have avoided redundant initialization of support classes.
public SecurityController(MyProjectHelper objHelper, SessionContext sessionContext)
{
session = sessionContext;
projectHelper = projectHelper ?? objHelper;
}
Now, I am able to share the session variables which I have set in my support classes
private SessionContext session = null;
public HomeController(SessionContext sessionContext)
{
session = sessionContext;
}
[Authorize]
public IActionResult Index()
{
if (session.CurrEmployee != null)
{
ViewBag.Name = (session.CurrEmployee.FirstName + " " + session.CurrEmployee.LastName);
return View();
}
}

Maintain User Control State in UWP Application using Template 10

I am creating UWP app using Template 10. I have created user control like this.
<my:DeviceInfoUserControl OnEndpointTypeChange="{Binding OnEndpointTypeChangeCommand}" Component="{Binding DeviceManagementViewModel,Mode=TwoWay}"></my:DeviceInfoUserControl>
I have Radio Buttons on User Control. I have added User Control on Multiple screens.
This user control has its own ViewModel as well as Some Dependency Properties as follows:
public class DeviceManagementViewModel : ViewModelBase
{
}
public sealed partial class DeviceInfoUserControl : UserControl
{
public bool IsToggled = true;
public DeviceInfoUserControl()
{
this.InitializeComponent();
}
public static readonly DependencyProperty OnEndpointTypeChangeProperty =
DependencyProperty.Register(
"OnEndpointTypeChange",
typeof(ICommand),
typeof(DeviceInfoUserControl), new PropertyMetadata(null));
public ICommand OnEndpointTypeChange
{
get { return (ICommand)GetValue(OnEndpointTypeChangeProperty); }
set { SetValue(OnEndpointTypeChangeProperty, value); }
}
public static readonly DependencyProperty ComponentProperty = DependencyProperty.Register("Component", typeof(DeviceManagementViewModel), typeof(DeviceInfoUserControl), new PropertyMetadata(null));
public DeviceManagementViewModel Component
{
get { return (DeviceManagementViewModel)GetValue(ComponentProperty); }
set { SetValue(ComponentProperty, value); }
}
}
I want to preserve Radio Button Selection across all screens. How should I achieve this?
You have to ensure that the same ViewModel instance is used for all control instance. The XAML way is always create new instance:
<Page.DataContext>
<vm:DetailPageViewModel x:Name="ViewModel" />
</Page.DataContext>
In the Template10's Bootstrapper class with the ResolveForPage method override, you can inject ViewModel's after the page navigation through a custom logic, or through dependency injection LINK
Don't know its better way or not but I have achieved this by making Singletone Viewmodel.
public class DeviceManagementViewModel : ViewModelBase
{
public static readonly DeviceManagementViewModel _instance = new DeviceManagementViewModel ();
private DeviceManagementViewModel ()
{
}
/*Properties and Methods */
}
In Parent Screen ViewModel I have created following property
private DeviceManagementViewModel _deviceManagementViewModel;
public DeviceManagementViewModel DeviceManagementViewModel1
{
get { return _deviceManagementViewModel; }
set { Set(ref _deviceManagementViewModel, value); }
}
I have Instantiated property in Constructor:
public ConfigurationViewModel()
{
DeviceManagementViewModel1 = DeviceManagementViewModel._instance;
}
And on User Control:
<my:DeviceInfoUserControl OnEndpointTypeChange="{Binding OnEndpointTypeChangeCommand}" Component="{Binding DeviceManagementViewModel1,Mode=TwoWay}"></my:DeviceInfoUserControl>

dapper with autofac and repository pattern

I am using dapper with the repository pattern in a WebApi Application and I have the following problem.
The Repository Class is as follows
public class Repository : DataConnection, IRepository
{
public Repository(IDbConnection connection)
: base(connection)
{
}
public T GetFirst<T>(object filters) where T : new()
{
//Creates the sql generator
var sqlGenerator = new MicroOrm.Pocos.SqlGenerator.SqlGenerator<T>();
//Creates the query
var query = sqlGenerator.GetSelect(filters);
//Execute the query
return Connection.Query<T>(query, filters).FirstOrDefault();
}
The IRepository Interface has only one method, the GetFirst. A Controller that uses this repository is as follows
public class UsersController : ApiController
{
private IRepository Repository;
public UsersController(IRepository repository)
{
Repository = repository;
}
public User Get(int id)
{
return Repository.GetFirst<User>(new { id });
}
}
I use autofac as DI and in the Application_Start method in Global.asax I use the following code
string connString = ConfigurationManager.ConnectionStrings["DapperDemo"].ConnectionString;
SqlConnection connnection = new SqlConnection(connString);
var builder = new ContainerBuilder();
builder.RegisterType<Repository>().As<IRepository>();
builder.RegisterType<UsersController>().InstancePerRequest();
var container = builder.Build();
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
But it seems that I am missing something cause I get the following error:
An error occurred when trying to create a controller of type 'UsersController'. Make sure that the controller has a parameterless public constructor.
You need to overwrite default controller activator, because it has no knowledge of your DI container.
Add a service class:
public class ServiceActivator : IHttpControllerActivator
{
public ServiceActivator(HttpConfiguration configuration) { }
public IHttpController Create(HttpRequestMessage request
, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = ObjectFactory.GetInstance(controllerType) as IHttpController;
return controller;
}
}
Then on Application_Start():
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new ServiceActivator(GlobalConfiguration.Configuration));
I'm using structure map in this example, so just replace it with which ever container you are using.

Parameterless implementation for Ninject mappings

i am creating custom membership provider using ninject for binding to sql classes.
my class in looks like
public MyMembershipProvider(IUsersRepository userRepository)
{
this.userRepository = userRepository;
}
How can i create from this parameterless constructor?
kernel.Bind<IUsersRepository>().To<UsersRepository>();
kernel.Bind<MembershipProvider>().To<MyMembershipProvider>();
and then:
public class AccountController : Controller
{
private readonly MembershipProvider _membershipProvider;
public AccountController(MembershipProvider membershipProvider)
{
_membershipProvider = membershipProvider;
}
public ActionResult Foo()
{
// TODO: Use the membership provider to do some processing
return View();
}
}