Using Autofac with webapi and mvc5.1 not working for webapi - autofac

I have a project using both mvc and webapi.
It's a Membership Reboot application so I have taken the example single application project and have slightly modified it to suit.
The DI works ok for controllers however when I try to call a webapi controller I keep getting an error
Make sure that the controller has a parameterless public constructor.
Is there something else I need to do for using autofac with webapi?
This is the code from my startup.cs
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "External",
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive
});
ConfigureMembershipReboot(app);
}
private static void ConfigureMembershipReboot(IAppBuilder app)
{
System.Data.Entity.Database.SetInitializer(new System.Data.Entity.MigrateDatabaseToLatestVersion<DefaultMembershipRebootDatabase, BrockAllen.MembershipReboot.Ef.Migrations.Configuration>());
//System.Data.Entity.Database.SetInitializer(new System.Data.Entity.CreateDatabaseIfNotExists<DefaultMembershipRebootDatabase>());
var cookieOptions = new CookieAuthenticationOptions
{
AuthenticationType = MembershipRebootOwinConstants.AuthenticationType
};
BuildAutofacContainer(app, cookieOptions.AuthenticationType);
app.UseMembershipReboot(cookieOptions);
}
private static void BuildAutofacContainer(IAppBuilder app, string authType)
{
var builder = new ContainerBuilder();
var config = CreateMembershipRebootConfiguration(app);
builder.RegisterInstance(config).As<MembershipRebootConfiguration>();
builder.RegisterType<DefaultUserAccountRepository>()
.As<IUserAccountRepository>()
.As<IUserAccountQuery>()
.InstancePerLifetimeScope();
builder.RegisterType<UserAccountService>().OnActivating(e =>
{
var owin = e.Context.Resolve<IOwinContext>();
var debugging = false;
#if DEBUG
debugging = true;
#endif
e.Instance.ConfigureTwoFactorAuthenticationCookies(owin.Environment, debugging);
})
.AsSelf()
.InstancePerLifetimeScope();
builder.Register(ctx =>
{
var owin = ctx.Resolve<IOwinContext>();
return new OwinAuthenticationService(authType, ctx.Resolve<UserAccountService>(), owin.Environment);
})
.As<AuthenticationService>()
.InstancePerLifetimeScope();
builder.Register(ctx=>HttpContext.Current.GetOwinContext()).As<IOwinContext>();
builder.RegisterControllers(typeof(Startup).Assembly);
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}

It was a 1 liner :)
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);

Related

Change EF connection string when user logs in with Identity

My question is about extending this previous post using identity to calculate the connection string for each user: ASP.NET Core change EF connection string when user logs in
I tried the following approach :
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
var c = new SqlConnectionStringBuilder
{
-- the connection string to the users repository --
};
services.AddDbContextFactory<MasterDBContext>(options =>
options.UseSqlServer(c.ConnectionString));
services.AddScoped<MasterDBContext>(p => p.GetRequiredService<IDbContextFactory<MasterDBContext>>().CreateDbContext());
services.AddDefaultIdentity<MyUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<MasterDBContext>();
services.AddTransient<IMasterUserService, MasterUserService>();
services.AddDbContextFactory<UserDbContext>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
UserDbContext:
public MyContext(IServiceProvider provider)
{
_provider = provider;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var haccess = (IHttpContextAccessor)_provider.GetService(typeof(IHttpContextAccessor));
var scopefactory = haccess.HttpContext.RequestServices.GetService<IServiceScopeFactory>();
using (var scope = scopefactory.CreateScope())
{
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<MyUser>>();
var user = userManager.GetUserAsync(haccess.HttpContext.User).Result;
var userServ = scope.ServiceProvider.GetRequiredService<IMasterUserService>();
optionsBuilder.UseSqlServer(userServ.GetConnectionString(user).Result);
}
base.OnConfiguring(optionsBuilder);
}
But, even in a scope, no way to get access to UserManager service (usermanager injection works fine from others services and controllers). I get an "invalid operation exception" at the usermanager connection point.
What is wrong with that code ?
Thanks in advance
I found the solution at the end... My code in MyContext.OnConfiguring is correct if you add services.TryAddScoped<UserManager>(); in the ConfigureServices function of statup.cs.
All together, I'm able to get a connection string depending of the current user from any service :
public class MyService : IMyService
{
private IDbContextFactory _dbfactory;
public MyService(IDbContextFactory<MyContext> dbfactory)
{
_dbfactory = dbfactory;
}
public async Task AnyAsync()
{
using (var dbtf = _dbfactory.CreateDbContext())
{
... your code ...
}
}
}

Autofac Performance Tuning

We have implmeneted the following Autofac code using SingleInstance in c# code. We found its taking time around 3 to 4 seconds for intiating the constructors for all the services for every time when we call the particular services.
Please find below the following code implementation.
private static void RegisterDependency(HttpConfiguration config)
{
Log.Trace("Registering dependencies");
var builder = new ContainerBuilder();
HttpContextBase httpContextBase = new HttpContextWrapper(HttpContext.Current);
var configService = new ConfigurationService(httpContextBase, new CacheWrapper());
var stubModeValue = configService.GetResourcePreferenceValueByKeyName("stubMode", "N");
var isStubMode = "Y".Equals(stubModeValue, StringComparison.OrdinalIgnoreCase);
builder.RegisterAssemblyTypes(typeof(ClientService).Assembly).Where(x => x.isClass && x.isPublic && !x.isAbstract)
.Where(x => isStubMode = x.Name.EndsWith("stub").AsImplementedInterface();
RegisterServiceProxy<ExternalService1>(builder);
RegisterServiceProxy<ExternalService2>(builder);
RegisterServiceProxy<ExternalService3>(builder);
builder.Register<IUserProfile>(x => OwnerIdentityAndRoleProfile.Current).InstancePerLifeTimeScope();
builder.Register<IConfigurationService>(c => new ConfigurationService(httpContextBase, new CacheWrapper()).InstancePerLifeTimeScope;
builder.RegisterType<ServiceProxyProvider>().SingleInstance();
builder.RegisterType<wsProfileWrapper>() As <wsProfileWrapper>();
var container = builder.build();
ServiceProxyProvider = container.Resolve<serviceProxyProvider>();
config.DependencyResolver = new AutofacWebAPIDependencyResolver(container);
Log.Trace("Completed Registering dependencies");
}
static void RegisterServiceProxy<T> (ContainerBuilder builder) where T:class
{
builder.Register(_ => _serviceProxyProvider.CreateServiceProxyInstance<T>()).InstancePerRequest()
.OnRelease(x => _serviceProxyProvider.Dispose(x));
}
Thanks.

Xamarin.Auth fails to complete with Trakt

I'm building an app as Trakt client using Xamarin. To authenticate users, I use Xamarin.Auth because its cross-platform. However, after the authentication succeeds, it doesn't call Completed event handler. The event is only called once I click on the Back button but it returns a null Account object and false IsAuthenticated.
I'm wondering if its because the redirect uri is invalid.
Please see my code below.
[assembly: ExportRenderer(typeof(LoginView), typeof(LoginViewRenderer))]
namespace ShowsCalendar.Droid.ViewRenderer
{
public class LoginViewRenderer : PageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
var context = Forms.Context;
var baseAddress = ConfigHelper.TraktAPIURL;
var auth = new OAuth2Authenticator(
clientId: ConfigHelper.ClientID,
redirectUrl: new Uri("urn:ietf:wg:oauth:2.0:oob"),
scope: "",
authorizeUrl: new Uri(baseAddress + "/oauth/authorize?response_type=code")
);
auth.AllowCancel = true;
auth.Completed += AuthenticateCompleted;
var intent = auth.GetUI(context);
context.StartActivity(intent);
}
private void AuthenticateCompleted(object sender, AuthenticatorCompletedEventArgs e)
{
if (!e.IsAuthenticated)
{
return;
}
App.AccessToken = e.Account.Properties["access_token"].ToString();
AccountStore.Create().Save(e.Account, "Trakt");
}
}
}

Web API HttpClient PutAsync returning Http 404

Im trying to send a PUT to my Web API and am struggling a bit as to how I should construct the actual Http request. Below is an integration test sample. It works fine using HttpMessageInvoker to call the Web API Put, but I want to use HttpClient in test also since that is what I'll be using in the business layer.
[TestMethod]
public void Verify_UpdateBudgetData_Http_PUT()
{
int budgetId = 1;
string appId = "DummyApp";
string userId = "Dummy";
string value = "400";
string filterJSON =
"{dimensionFilter:{\"Demo_Konto\":[\"3000\"],\"Demo_AO\":[\"200\"]},valueSpreadType:{\"Value1\":0}}";
HttpConfiguration config = new HttpConfiguration();
Konstrukt.SL.AggregationEngine.WebApiConfig.Register(config, new SL.AggregationEngine.AutofacStandardModule());
HttpServer server = new HttpServer(config);
/*this works*/
using (HttpMessageInvoker client = new HttpMessageInvoker(server))
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put,
String.Format("http://localhost/AggregationEngine/UpdateBudgetData/{0}/{1}/{2}/{3}/{4}",
budgetId, appId, userId, value, filterJSON)))
using (HttpResponseMessage response = client.SendAsync(request, CancellationToken.None).Result)
{
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Wrong http status returned");
}
};
/*this does not work*/
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri("http://localhost");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseMessage =
client.PutAsync(
String.Format("http://localhost/AggregationEngine/UpdateBudgetData/{0}/{1}/{2}/{3}/{4}",
budgetId, appId, userId, value, filterJSON), new StringContent("")).Result;
Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode, "Wrong http status returned");
}
}
Here is my WebApiConfig-class
public static class WebApiConfig
{
public static void Register(HttpConfiguration config, Autofac.Module moduleToAppend)
{
config.Routes.MapHttpRoute(
name: "UpdateBudgetData",
routeTemplate: "AggregationEngine/{controller}/{budgetId}/{appId}/{userId}/{value}/{filterJSON}",
defaults: new { filter = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "GetBudgetAndRefData",
routeTemplate: "AggregationEngine/{controller}/{budgetId}/{userId}/{filterJSON}",
defaults: new { filter = RouteParameter.Optional }
);
config.EnableCors();
config.EnableSystemDiagnosticsTracing();
// Autofac container
// if not configured here you'll not have dependencies provided to your WebApiControllers when called
var builder = new ContainerBuilder(); // yes, it is a different container here
builder.RegisterAssemblyTypes( // register Web API Controllers
Assembly.GetExecutingAssembly())
.Where(t =>
!t.IsAbstract && typeof(ApiController).IsAssignableFrom(t))
.InstancePerLifetimeScope();
// register your graph - shared
builder.RegisterModule(
new AutofacStandardModule()); // same as with ASP.NET MVC Controllers
if (moduleToAppend != null)
{
builder.RegisterModule(moduleToAppend);
}
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(
container);
}
public static void Register(HttpConfiguration config)
{
Register(config, null);
}
}
How can I fix the HttpClient call to PutAsync? Should I embed the FilterJSON parameter in the body? If so, how to do that? I've tried that but then the FromBody parametger was null...
I got it working by using the FromBody tag in the controller and then wrapping that parameter in the http request body. An important note to is to prefix the parameter with an "=" sign to make sure it was interpreted correctly by the controller. Also I removed the same parameter from the route config. Finally to make the client to server request work I had to replace HttpServer Class with httpselfhostserver

HTTP self-hosting and unit tests

I am working on a set of unit tests, which include testing of HTTP client/server functionality, with a self hosted server. But I can't get even the simplest test to work. HEre is my code
UnitTest1.cs
using System;
using System.Net.Http;
using System.Web.Http.SelfHost;
using NUnit.Framework;
using SomeWebService;
namespace UnitTestProject1
{
[TestFixture]
public class UnitTest1
{
[Test]
public void TestMethod1()
{
var baseAddress = new Uri("http://localhost:9876");
var config = new HttpSelfHostConfiguration(baseAddress);
new Bootstrap().Configure(config);
var server = new HttpSelfHostServer(config);
using (var client = new HttpClient(server))
{
client.BaseAddress = baseAddress;
var response = client.GetAsync("").Result;
Assert.True(response.IsSuccessStatusCode, "Actual status code: " + response.StatusCode);
}
}
}
}
Bootstrap.cs
using System.Web.Http;
namespace SomeWebService
{
public class Bootstrap
{
public void Configure(HttpConfiguration config)
{
config.Routes.MapHttpRoute(name: "API Default", routeTemplate: "{controller}/{id}", defaults: new
{
controller = "Home",
id = RouteParameter.Optional
});
}
}
}
and the HomeController.cs
using System.Net.Http;
using System.Web.Http;
namespace SomeWebService
{
class HomeController:ApiController
{
public HttpResponseMessage Get()
{
return this.Request.CreateResponse();
}
}
}
The test results in:
Actual status code: NotFound
Expected: True
But was: False
What am I doing wrong?
Packages installed
Install-Package Microsoft.Net.Http -version 2.0.20710
Install-Package Microsoft.AspNet.WebApi.SelfHost -version 4.0.20918
Install-Package Microsoft.AspNet.WebApi.Core -version 4.0.20710
If you want your tests to run even faster, you can avoid the whole TCP/IP stack by using a purely in-memory host,
[Test]
public void TestMethod1()
{
var config = new HttpConfiguration();
new Bootstrap().Configure(config);
var server = new HttpServer(config);
using (var client = new HttpClient(server))
{
client.BaseAddress = baseAddress;
var response = client.GetAsync("").Result;
Assert.True(response.IsSuccessStatusCode, "Actual status code: " + response.StatusCode);
}
}
HomeController is private, because you haven't explicitly declared it public. Try making it public:
public class HomeController:ApiController
{
public HttpResponseMessage Get()
{
return this.Request.CreateResponse();
}
}