i'm following Autofac guide to migrate to .net 3.1
According to their guide, I need to add (among other things) this function:
public void ConfigureContainer(ContainerBuilder builder)
And this will be called automatically
The problem is that the code inside it that is registering the services is conditional for our app so I need to pass a boolean to the function
for example:
public void ConfigureContainer(ContainerBuilder builder)
{
if (enableTokenAutoRefresh)
{
builder.RegisterType<AuthenticationWrapper>()
.As<IApiProxy>()
}
else
{
builder.RegisterType<ApiProxy>()
.As<IApiProxy>()
}
}
Can I just add a boolean to the ConfigureContainer method?
seems this will break the calling for it?
and if not - how to pass data to it ?
please help
ConfigureContainer, provided by the ASP.NET Core framework, only takes the container builder type of your DI framework (in this case, an Autofac ContainerBuilder). To get additional data in there, you'd need to set a variable on the Startup class somewhere earlier in the startup pipeline and use it.
// NOT a complete Startup, but gives you the idea.
public class Startup
{
public Startup(IConfiguration config)
{
// appSettings.json has the value you want
this.EnableTokenAutoRefresh = config.GetValue<bool>("path:to:key");
}
public bool EnableTokenAutoRefresh { get; set; }
public void ConfigureContainer(ContainerBuilder builder)
{
if(this.EnableTokenAutoRefresh)
{
// Do what you need to based on config
}
}
}
Historically, when I have wanted to add a column or columns on to the AspNetUsers table, the workflow I would follow is as per the following:
1.Create an ApplicationUser class, inherit IdentityUser from it.
2.Add the new properties to the application user class
3:Update the applicationDbContext to inherit from: IdentityDbContext
4:Alter any references to IdentityUser in startup code eg:Startup.cs / Global etc
5:Add-Migration MigrationName
6: Update-Database
this would generate the Up/Down scripts for the new column and add the column to my database.
However, I have spun up a new Blazor serverside web-application and done the steps above to no avail.
Can anyone see anything i'm missing here, I have done this enough times in the past that I find it weird that I may have missed something, but anything is possible. hopefully someone can help, please see below for the code I have altered trying to achieve this.
Application DB context code:
namespace ExtendingBlazorIdentity.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
}
Application User Class:
namespace ExtendingBlazorIdentity.Data
{
public class ApplicationUser : IdentityUser
{
string NickName { get; set; }
}
}
Startup.cs
namespace ExtendingBlazorIdentity
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<ApplicationUser>>();
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSingleton<WeatherForecastService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
Your property is not public.
public class ApplicationUser : IdentityUser
{
public string NickName { get; set; }
}
I have a simple Typed Hub setup through an interface. I can successfully connect to the SignalR Hub.
namespace POC.WebSocket
{
public class ApiResultHub : Hub<IApiResultHubClient>
{
public async Task SendMessage(string user, string message)
{
await Clients.All.ReceiveMessage(user, message);
}
public override Task OnConnectedAsync()
{
Clients.Caller.ReceiveMessage("newUser", $"{Context.ConnectionId}");
return base.OnConnectedAsync();
}
}
}
However, when I try invoke a client side hub method it throws the following exception:
Here are my other class files:
Startup.cs
namespace POC.WebSocket
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("https://localhost:44324", "https://localhost:44326")
.AllowCredentials();
}));
services.AddSignalR();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<ApiResultHub>("/apiResult");
});
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
IApiResultHubClient`T.cs
namespace POC.Common
{
public interface IApiResultHubClient
{
Task ReceiveMessage(string user, string message);
Task SendApiResponse<T>(ApiResult<T> data);
}
}
Has anyone else seen this issue? I saw few similar issues with being able to connect to the hub itself. However, as mentioned I am able to successfully connect to the hub, just not able to resolve Clients.All, Clients.Caller or Clients.Others.
All libraries updated today:
.Net Core version 2.1.4
.Net SignalR Core version 1.0.3
Edits:
Found details of the exception:
Method 'SendApiResponse' in type
'Microsoft.AspNetCore.SignalR.TypedClientBuilder.IApiResultHubClientImpl'
from assembly 'Microsoft.AspNetCore.SignalR.TypedClientBuilder,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have
an implementation.
I am using Entity Framework 7 on the nightly build channel (right now I'm using version EntityFramework.7.0.0-beta2-11524) and I'm trying to log the queries that EF generates just out of curiosity.
I'm writing a simple console program, I tried using the same logging technic that EF6 uses, but DbContext.Database.Logis not available on Entity Framework 7. Is there a way to log or just take a peek at the SQL generated by EF7?
For those using EF7 none of the above worked for me. But this is how i got it working. (from #avi cherry's comment)
In your Startup.cs you proably have a Configure method with a bunch of configurations in it. It should look like below (in addition to your stuff).
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
//this is the magic line
loggerFactory.AddDebug(LogLevel.Debug); // formerly LogLevel.Verbose
//your other stuff
}
You can log to the console using this code, I am sure it will be wrapped in a simpler api later:
using System;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Utilities;
using Microsoft.Framework.Logging;
public static class SqlCeDbContextExtensions
{
public static void LogToConsole(this DbContext context)
{
var loggerFactory = ((IAccessor<IServiceProvider>)context).GetService<ILoggerFactory>();
loggerFactory.AddProvider(new DbLoggerProvider());
}
}
And the DbLoggerProvider is implemented here: https://github.com/ErikEJ/EntityFramework7.SqlServerCompact/tree/master/src/Provider40/Extensions/Logging
If you are using MS SQL Server, one way I have used in the past is to make use of the SQL Server Profiler and capture all interaction with the SQL Server, this captures the exact SQL submitted and can be cut n pasted into the SQL Server Management Studio for further review/analysis.
I know this does not directly answer your question on Entity Framework, but I have found this generic approach very useful for any language/tools.
One tip is in the Trace Properties when setting up a new trace, I have found it useful to adjust the default selection of events in the Events Selection tab. Mostly I turn off the Audit Login/Logout unless specifically tracking such an issue.
I struggled with all the above answers as the EF bits kept changing, so the code wouldn't compile. As of today (19Feb2016) with EF7.0.0-rc1-final (Prerelease) and SQLite, here's what works for me:
From the EF7 documentation:
using System;
using System.IO;
using Microsoft.Extensions.Logging;
namespace EFLogging
{
public class EFLoggerProvider : ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
return new EFLogger();
}
public void Dispose()
{
// N/A
}
private class EFLogger : ILogger
{
public IDisposable BeginScopeImpl(object state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
{
File.AppendAllText(#".\EF.LOG", formatter(state, exception));
Console.WriteLine(formatter(state, exception));
}
}
}
}
Using some ideas above and the EF7 Docs:
using System;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Extensions.DependencyInjection; // Add this to EF7 docs code
using Microsoft.Extensions.Logging;
namespace DataAccessLayer
{
public static class DbContextExtensions
{
public static void LogToConsole(this DbContext context)
{
var serviceProvider = context.GetInfrastructure<IServiceProvider>();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
loggerFactory.AddProvider(new EFLoggerProvider(logLevel));
}
}
}
EDIT: #jnm2 pointed out if you add "using Microsoft.Extensions.DependencyInjection", the EF7 docs ARE correct. Thanks!
And finally, in my App.OnStartup method:
using (var db = new MyDbContext())
{
db.LogToConsole();
}
This code will create a log file and also output logging info to the Visual Studio output window. I hope this helps -- I'm sure in a few weeks, the bits will change again.
With the latest version of EF7-beta8, Anthony's answer need a little tweaking. Here's what I did to get it to work.
internal static class DbContextExtensions
{
public static void LogToConsole(this DbContext context)
{
var loggerFactory = context.GetService<ILoggerFactory>();
loggerFactory.AddConsole(LogLevel.Verbose);
}
}
I think I figured this out. With the current EF7 bits, ILoggerFactory is registered with the dependency injection container which EF is using. You can get a reference to the container, which is an IServiceProvider, via the ScopedServiceProvider property of DbContext when it is cast to IDbContextServices. From there, you can get the ILoggerFactory and configure it using the AddToConsole extension method from the Microsoft.Framework.Logging.Console NuGet package.
public static void LogToConsole(this DbContext context)
{
// IServiceProvider represents registered DI container
IServiceProvider contextServices = ((IDbContextServices)context).ScopedServiceProvider;
// Get the registered ILoggerFactory from the DI container
var loggerFactory = contextServices.GetRequiredService<ILoggerFactory>();
// Add a logging provider with a console trace listener
loggerFactory.AddConsole(LogLevel.Verbose);
}
Here is a gist I created for this snippet: https://gist.github.com/tonysneed/4cac4f4dae2b22e45ec4
This worked for me with EF7 rc2-16485:
"EntityFramework.MicrosoftSqlServer": "7.0.0-rc2-16485",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc2-15888",
public static class DbContextExtensions
{
public static void LogToConsole(this DbContext context)
{
var contextServices = ((IInfrastructure<IServiceProvider>) context).Instance;
var loggerFactory = contextServices.GetRequiredService<ILoggerFactory>();
loggerFactory.AddConsole(LogLevel.Verbose);
}
}
As an alternative to the above answers, I found this answer by far the easiest solution for me to reason about:
private readonly ILoggerFactory loggerFactory;
// Using dependency injection
public FooContext(ILoggerFactory loggerFactor) {
this.loggerFactory = loggerFactory;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseLoggerFactory(loggerFactory); // Register logger in context
}
With ASP.NET Core 2.0 you get SQL logging automatically. No need to do anything extra.
For those who just want SQL queries to be logged (using Entity Framework Core with .NET Core 2.0 or above), use the following code in your DbContext class:
public static readonly LoggerFactory MyLoggerFactory
= new LoggerFactory(new[]
{
new ConsoleLoggerProvider((category, level)
=> category == DbLoggerCategory.Database.Command.Name
&& level == LogLevel.Information, true)
});
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseLoggerFactory(MyLoggerFactory) // Warning: Do not create a new ILoggerFactory instance each time
.UseSqlServer(
#"Server=(localdb)\mssqllocaldb;Database=EFLogging;Trusted_Connection=True;ConnectRetryCount=0");
Reference: https://learn.microsoft.com/en-us/ef/core/miscellaneous/logging
Using MVC and trying to use dependency injection for controllers, but when I try to call a method on a controller that takes a dependency, I get the "no parameterless constructor" error. Here's my setup:
ProductRepository : IProductRepository
ProductService : IProductService {
public ProductService(IProductRepository repository) {} }
ProductController {
public ProductController(IProductService service) {} }
In Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
InitializeServiceLocator();
RegisterRoutes(RouteTable.Routes);
}
protected virtual void InitializeServiceLocator()
{
IWindsorContainer container = new WindsorContainer();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
container.RegisterControllers(typeof(HomeController).Assembly);
ComponentRegistrar.AddComponentsTo(container);
foreach (var handler in container.Kernel.GetAssignableHandlers(typeof(object)))
{
System.Diagnostics.Debug.WriteLine(String.Format("{0} {1}",
handler.ComponentModel.Service,
handler.ComponentModel.Implementation));
}
ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(container));
}
ComponentRegistrar:
public static void AddComponentsTo(IWindsorContainer container)
{
AddCustomRepositoriesTo(container);
AddApplicationServicesTo(container);
}
When InitializeServiceLocator completes, I can see that all Controllers, Services and Repositories are registered.
Any help greatly appreciated.
I'd still like to know the problem, but I've worked around it by creating my own WindsorControllerFactory per this post (with modification to avoid any web.config action): http://mvcsharp.wordpress.com/2010/01/09/setting-up-ioc-in-asp-net-mvc-using-castle-windsor/
The WindsorControllerFactory in the previous code was the MvcContrib.Castle.WindsorControllerFactory. Anyone using MvcContrib version successfully?
Thanks.