My global.asax looks like below
private void BuildIocContainer()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new BootstrapModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); //Set the WebApi DependencyResolver
}
protected void Application_Start()
{
BuildIocContainer();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
I have built a bootstrap module for autofac like the one below
public class BootstrapModule : Autofac.Module
{
private Assembly AssemblyOf<T>()
{
return typeof(T).Assembly;
}
private Assembly WebAssembly
{
get { return Assembly.GetExecutingAssembly(); }
}
private void RegisterMvc(ContainerBuilder builder)
{
builder.RegisterType<AsyncControllerActionInvoker>()
.As<IActionInvoker>();
builder.RegisterControllers(WebAssembly)
.InjectActionInvoker();
builder.RegisterApiControllers(WebAssembly).InjectActionInvoker();
}
private void RegisterLogger(ContainerBuilder builder)
{
builder.Register(CreateLogger)
.SingleInstance();
builder.Register(_ => new NLogWrapper(LogManager.GetLogger("DefaultLogger")))
.As<Logging.ILogger>()
.SingleInstance();
}
private static System.Func<Type, Logging.ILogger> CreateLogger(IComponentContext componentContext)
{
return type => new NLogWrapper(LogManager.GetLogger(type.FullName));
}
protected override void Load(ContainerBuilder builder)
{
RegisterLogger(builder);
RegisterMvc(builder);
builder.RegisterAssemblyTypes(WebAssembly)
.AsImplementedInterfaces();
builder.RegisterType<UserService>()
.As<IUserService>()
.InstancePerRequest();
builder.RegisterAssemblyTypes(typeof(IUserService).Assembly)
.AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof(IUserRepository).Assembly)
.AsImplementedInterfaces();
builder.RegisterFilterProvider();
}
}
Now, when I try to hit account controller through postman client,
private IUserService _userService;
public AccountController(IUserService userService)
{
_userService = userService;
}
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public HttpStatusCode Register(User model)
{
if (!ModelState.IsValid)
{
return HttpStatusCode.BadRequest;
}
// TODO : Write mapper if needed
var user = new ToolUser()
{
FirstName = model.FirstName,
LastName = model.LastName,
EmailID = model.EmailID,
DOB = Convert.ToDateTime(model.DateOfBirth),
Gender = model.Gender.ToString(),
TenandID = model.TenantID,
Password = model.Password
};
HttpStatusCode result = _userService.RegisterUser(user);
return result;
}
I get the error
"An error occurred when trying to create a controller of type 'AccountController'. Make sure that the controller has a parameterless public constructor."
What am I doing wrong?
You should have something like
HttpConfiguration config = new HttpConfiguration();
somewhere, which you use to register your routes etc.
Pass that config to your BuildIocContainer() mehod and add the line
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
instead of
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); //Set the WebApi DependencyResolver
Hope that helps
Related
Having an issue getting a service instance in my controller. Followed the documentation from autofac's website but still getting an error. "None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MyAssembly.Controllers.HeartBeatController' can be invoked with the available services and parameters:\r\nCannot resolve parameter 'MyAssembly.IO.IConfig config' of constructor 'Void .ctor(MyAssembly.IO.IConfig)'."
Here is my Global.asax file
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var builder = new ContainerBuilder();
var config = GlobalConfiguration.Configuration;
WebApiConfig.Register(config);
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
BuildServices(builder);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
config.EnsureInitialized();
log4net.Config.XmlConfigurator.Configure(new FileInfo(Server.MapPath("~/Web.config")));
}
private void BuildServices(ContainerBuilder builder)
{
builder.RegisterType<Config>().As<IConfig>().InstancePerRequest();
}
}
And here is my interface definition and class. Which is defined in the same assembly.
public interface IConfig
{
string GetSetting(string key);
T GetSetting<T>(string key);
}
public class Config : IConfig
{
public string GetSetting(string key)
{
return ConfigurationManager.AppSettings[key];
}
public T GetSetting<T>(string key)
{
var setting = GetSetting(key);
return setting != null ? (T)System.Convert.ChangeType(setting, typeof(T)) : default(T);
}
}
This is so ridiculous but I failed to build the services before the container. Oversight on my part and the below works.
BuildServices(builder);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
config.EnsureInitialized();
I'm trying to build my first xamarin app, which I'm building using forms. One of the features of the app is sending users locations and have to do that even if the app is in the background. So I came across James Montemagno's GeolocatorPlugin, which promised to do just that.
As the documentation was not that clear on how to implement his plugin in the background I looked through the projects closed issues and found a guy which gave an example of a simple case of using the plugin with a service. (https://github.com/jamesmontemagno/GeolocatorPlugin/issues/272)
I've adopted the code and created the service. The service are using an interface to start the service and now my problem is how to make use of the interface to make the service run.
In my shared project I put the interface and the viewmodel and in xamarin.android project I put the service.
The interface - IGeolocationBackgroundService:
public interface IGeolocationBackgroundService {
void StartService();
void StartTracking();
}
The viewmodel - GeolocatorPageViewModel:
public class GeolocatorPageViewModel
{
public Position _currentUserPosition { get; set; }
public string CoordinatesString { get; set; }
public List<string> userPositions { get; set; }
public ICommand StartTrackingCommand => new Command(async () =>
{
if (CrossGeolocator.Current.IsListening)
{
await CrossGeolocator.Current.StopListeningAsync();
}
CrossGeolocator.Current.DesiredAccuracy = 25;
CrossGeolocator.Current.PositionChanged += Geolocator_PositionChanged;
await CrossGeolocator.Current.StartListeningAsync(
TimeSpan.FromSeconds(3), 5);
});
private void Geolocator_PositionChanged(object sender, PositionEventArgs e)
{
var position = e.Position;
_currentUserPosition = position;
var positionString = $"Latitude: {position.Latitude}, Longitude: {position.Longitude}";
CoordinatesString = positionString;
Device.BeginInvokeOnMainThread(() => CoordinatesString = positionString);
userPositions.Add(positionString);
Debug.WriteLine($"Position changed event. User position: {CoordinatesString}");
}
}
The service - GeolocationService:
[assembly: Xamarin.Forms.Dependency(typeof(GeolocationService))]
namespace MyApp.Droid.Services
{
[Service]
public class GeolocationService : Service, IGeolocationBackgroundService
{
Context context;
private static readonly string CHANNEL_ID = "geolocationServiceChannel";
public GeolocatorPageViewModel ViewModel { get; private set; }
public override IBinder OnBind(Intent intent)
{
return null;
}
public GeolocationService(Context context)
{
this.context = context;
CreateNotificationChannel();
}
private void CreateNotificationChannel()
{
NotificationChannel serviceChannel = new NotificationChannel(CHANNEL_ID,
"GeolocationService", Android.App.NotificationImportance.Default);
NotificationManager manager = context.GetSystemService(Context.NotificationService) as NotificationManager;
manager.CreateNotificationChannel(serviceChannel);
}
//[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
var newIntent = new Intent(this, typeof(MainActivity));
newIntent.AddFlags(ActivityFlags.ClearTop);
newIntent.AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, newIntent, 0);
var builder = new Notification.Builder(this, CHANNEL_ID);
var notification = builder.SetContentIntent(pendingIntent)
.SetSmallIcon(Resource.Drawable.ic_media_play_light)
.SetAutoCancel(false)
.SetTicker("Locator is recording")
.SetContentTitle("GeolocationService")
.SetContentText("Geolocator is recording for position changes.")
.Build();
StartForeground(112, notification);
//ViewModel = new GeolocatorPageViewModel();
return StartCommandResult.Sticky;
}
public void StartService()
=> context.StartService(new Intent(context, typeof(GeolocationService)));
public void StartTracking()
{
ViewModel = new GeolocatorPageViewModel();
ViewModel.StartTrackingCommand.Execute(null);
}
}
}
So be clear, I need to start the service and I'm not used to interfaces, so how do I call the interface?
use DependencyService to get a reference to your service and then start it
var svc = DependencyService.Get<IGeolocationBackgroundService>();
svc.StartService();
svc.StartTracking();
I've asked this question and marked as duplicated but there is a problem in dependency injection in custom AuthorizeAttribute in my design. I've tried
several codes from some same answers. Everything is working correctly except attributes, _authenticationService is always being null. My code:
public class AuthenticateAttribute : AuthorizeAttribute
{
[Dependency]
private IAuthenticationService _authenticationService { get; set; }
public AuthenticateAttribute()
{
//_authenticationService is null here, even tried this code too:
//
}
Test 1
UnityActionFilterProvider
public class UnityActionFilterProvider : IFilterProvider
{
private readonly IUnityContainer container;
public UnityActionFilterProvider(IUnityContainer container)
{
this.container = container;
}
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration,
HttpActionDescriptor actionDescriptor)
{
foreach (IActionFilter actionFilter in container.ResolveAll<IActionFilter>())
{
// TODO: Determine correct FilterScope
yield return new FilterInfo(actionFilter, FilterScope.Global);
}
}
}
RegisterComponents
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IAuthenticationService, AuthenticationService>(new HierarchicalLifetimeManager());
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
var providers = GlobalConfiguration.Configuration.Services.GetFilterProviders().ToList();
GlobalConfiguration.Configuration.Services.Add(typeof(IFilterProvider), new UnityActionFilterProvider(container));
var defaultprovider = providers.First(p => p is ActionDescriptorFilterProvider);
GlobalConfiguration.Configuration.Services.Remove(typeof(IFilterProvider), defaultprovider);
}
Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
UnityConfig.RegisterComponents();
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
Test 2
*UnityFilterAttributeFilterProvider *
public class UnityFilterAttributeFilterProvider : FilterAttributeFilterProvider
{
private IUnityContainer _container;
public UnityFilterAttributeFilterProvider(IUnityContainer container)
{
_container = container;
}
protected override IEnumerable<FilterAttribute> GetControllerAttributes(
ControllerContext controllerContext,
ActionDescriptor actionDescriptor)
{
var attributes = base.GetControllerAttributes(controllerContext,
actionDescriptor);
foreach (var attribute in attributes)
{
_container.BuildUp(attribute.GetType(), attribute);
}
return attributes;
}
protected override IEnumerable<FilterAttribute> GetActionAttributes(
ControllerContext controllerContext,
ActionDescriptor actionDescriptor)
{
var attributes = base.GetActionAttributes(controllerContext,
actionDescriptor);
foreach (var attribute in attributes)
{
_container.BuildUp(attribute.GetType(), attribute);
}
return attributes;
}
}
RegisterComponents
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IAuthenticationService, AuthenticationService>(new HierarchicalLifetimeManager());
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
var oldProvider = System.Web.Mvc.FilterProviders.Providers.Single(f => f is System.Web.Mvc.FilterAttributeFilterProvider);
System.Web.Mvc.FilterProviders.Providers.Remove(oldProvider);
var provider = new UnityFilterAttributeFilterProvider(container);
container.RegisterInstance<UnityFilterAttributeFilterProvider>(provider);
System.Web.Mvc.FilterProviders.Providers.Add(provider);
}
Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
UnityConfig.RegisterComponents();
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
When i set a breakpoint in the constructor of this controller, I'm seeing a System.ObjectDisposedException from userManager.Users . Why might .NET be giving me a disposed UserManager? I have other controllers that seem to be set up the same way and their UserManagers work fine (this one dies when it calls GetUserAsync)
[RequireHttps]
public class HomeController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RegionService _regionService;
private readonly ILogger _logger;
public HomeController(UserManager<ApplicationUser> userManager, RegionService regionService, ILoggerFactory loggerFactory)
{
_userManager = userManager;
_regionService = regionService;
_logger = loggerFactory.CreateLogger<HomeController>();
}
[HttpGet]
public async Task<IActionResult> Index()
{
var user = await _userManager.GetUserAsync(HttpContext.User);
...
}
...and i shouldn't be accessing a database in Home/Index; I'll change that afterward
Edit: This is how services are being configured:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.AddTransient<RegionService>();
// require SSL
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
}
The issue was in the constructor of a class I created called RegionService
public RegionService(IServiceProvider serviceProvider)
{
using (var context = serviceProvider.GetService<ApplicationDbContext>())
{
...
}
}
The 'using' statement causes 'context' to be disposed after it's done with that block of code. I should have just been making a new instance of the DbContext that can be disposed:
public RegionService(IServiceProvider serviceProvider)
{
using (var context = new ApplicationDbContext(
serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
...
}
}
I'm a beginner with Simple Injector and have a scenario where I need help to implement. I will try to simplify what I need to do.
I have a WebAPI where I need to authenticate users and based on the type of user choose an implementation.
Consider this structure
public interface ICarRepository {
void SaveCar(Car car);
}
//Some implementation for ICarRepository
public interface ICarLogic {
void CreateCar(Car car);
}
public class CarLogicStandard: ICarLogic {
private ICarRepository _carRepository;
public CarLogicStandard(ICarRepository carRepository) {
_carRepository = carRepository;
}
public void CreateCar(Car car) {
car.Color = "Blue";
_carRepository.SaveCar();
//Other stuff...
}
}
public class CarLogicPremium: ICarLogic {
private ICarRepository _carRepository;
public CarLogicPremium(ICarRepository carRepository) {
_carRepository = carRepository;
}
public void CreateCar(Car car) {
car.Color = "Red";
_carRepository.SaveCar();
//Other stuff 2...
}
}
And now I have a controller
public class CarController: ApiController {
private ICarLogic _carLogic;
public CarController(ICarLogic carLogic) {
_carLogic = carLogic;
}
public void Post(somePostData) {
//Identify the user based on post data
//....
Car car = somePostData.SomeCar();
_carLogic.CreateCar(car);
}
}
The code above will not work because in my request I need to identify the user. If it is a premium user the controller should use the CarLogicPremium and if it is a standard user the controller should use the CarLogicStandard.
I can configure the repository and others interfaces that don't need this logic on Global.asax however, since this case I need the request to decide which implementation should be used, I supose that I need to solve this in some other way.
There is a "Simple Injector" way to handle this? Or should I try another approach?
The simplest solution would be to configure the decision in the composition root, along with the rest of the container's configuration:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
var container = new Container();
container.Register<CarLogicStandard>();
container.Register<CarLogicPremium>();
container.RegisterPerWebRequest<ICarRepository, CarRepository>();
container.Register<ICarLogic>(
() =>
HttpContext.Current != null &&
HttpContext.Current.User != null &&
HttpContext.Current.User.IsInRole("Premium")
? (ICarLogic)container.GetInstance<CarLogicPremium>()
: (ICarLogic)container.GetInstance<CarLogicStandard>()
);
// This is an extension method from the integration package.
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
You could also create an abstraction over the current user and decorate standard features with premium features
public class CarLogicPremium : ICarLogic
{
private readonly ICarLogic decorated;
private readonly ICurrentUser currentUser;
private readonly ICarRepository carRepository;
public CarLogicPremium(
ICarLogic decorated,
ICurrentUser currentUser,
ICarRepository carRepository)
{
this.decorated = decorated;
this.currentUser = currentUser;
this.carRepository = carRepository;
}
public void CreateCar(Car car)
{
if (currentUser.IsPremiumMember)
{
car.Color = "Red";
this.carRepository.SaveCar(car);
//Other stuff 2...
}
else
{
this.decorated.CreateCar(car);
}
}
}
which would be configured a bit like this
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
var container = new Container();
container.Register<ICurrentUser, HttpCurrentUserProxy>();
container.RegisterPerWebRequest<ICarRepository, CarRepository>();
container.Register<ICarLogic, CarLogicStandard>();
container.RegisterDecorator(typeof(ICarLogic), typeof(CarLogicPremium));
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
But it really depends how many variations of services you will be creating over time. If you will be constantly adding new premium features you should look to implement a variation of the Try-X pattern. Let me know if one of the above works for you or if you need more info ...