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
Related
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
}
}
}
I'm following guidelines in how to setup EF Core to work safely in Blazor & .NET Core 3.1.
The MS documentation is here: https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-3.1
In the instructions, advice is to create a DbContextFactory which is used to create a dbcontext in each service. All makes sense in the Blazor world, but the code won't compile as
AddDbContextFactory does not exist. If there's another way to do it in .Net Core 3.1/ EF Core 3 - I can't see it.
services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db")
.EnableSensitiveDataLogging());
I found this extension method that the Microsoft docs page is using in its sample github project:
public static IServiceCollection AddDbContextFactory<TContext>(
this IServiceCollection collection,
Action<DbContextOptionsBuilder> optionsAction = null,
ServiceLifetime contextAndOptionsLifetime = ServiceLifetime.Singleton)
where TContext : DbContext
{
// instantiate with the correctly scoped provider
collection.Add(new ServiceDescriptor(
typeof(IDbContextFactory<TContext>),
sp => new DbContextFactory<TContext>(sp),
contextAndOptionsLifetime));
// dynamically run the builder on each request
collection.Add(new ServiceDescriptor(
typeof(DbContextOptions<TContext>),
sp => GetOptions<TContext>(optionsAction, sp),
contextAndOptionsLifetime));
return collection;
}
And the factory class is here:
public class DbContextFactory<TContext>
: IDbContextFactory<TContext> where TContext : DbContext
{
private readonly IServiceProvider provider;
public DbContextFactory(IServiceProvider provider)
{
this.provider = provider;
}
public TContext CreateDbContext()
{
if (provider == null)
{
throw new InvalidOperationException(
$"You must configure an instance of IServiceProvider");
}
return ActivatorUtilities.CreateInstance<TContext>(provider);
}
}
GetOptions method:
private static DbContextOptions<TContext>
GetOptions<TContext>(Action<DbContextOptionsBuilder> action,
IServiceProvider sp = null) where TContext: DbContext
{
var optionsBuilder = new DbContextOptionsBuilder < TContext > ();
if (sp != null)
{
optionsBuilder.UseApplicationServiceProvider(sp);
}
action?.Invoke(optionsBuilder);
return optionsBuilder.Options;
}
AddDbContextFactory will be introduced in .NET Core 5. See here: https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-ef-core-5-0-preview-7/.
The page you linked to gives you the code (albeit in pieces) to roll your own.
You will have to add the AddDbContextFactory<TContext> class and the FactoryExtensions to your project. Download the sample app there to make it complete.
And when you upgrade to net5 just replace it with the library version.
With EF Core, DbContext is registered as Scoped by EF service extension. This is desirable because DbContext is not thread-safe and therefore it should be created per request.
ServiceStack IOC treats any Scoped registration in Startup as singleton, which contradicts with the point above.
One possible solution is to not use EF Core's service extension, but that seems to bring a lot of boilerplate code and reduce maintainability. Is there any better way?
--
UPDATE
I'd like to provide sample code for clarity
I added a private Guid to the DbContext class so that I can tell whether we have the new instance.
public class BloggingContext : DbContext
{
private readonly Guid _instance;
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{
_instance = Guid.NewGuid();
}
public DbSet<Blog> Blogs { get; set; }
}
With .NET Core MVC, the controller code looks like
public class BlogsController : Controller
{
private readonly BloggingContext _context;
public BlogsController(BloggingContext context)
{
_context = context;
}
// skip for readability
}
For each request hitting the controller, the _instance inside BloggingContext returns an unique value. However, when using within a ServiceStack service, _instance always returns the same value.
public class BlogService : ServiceStack.Service
{
private readonly BloggingContext _context;
public BlogService(BloggingContext context)
{
_context = context;
}
// skip for readability
}
This behaviour is consistent with ServiceStack documentation about .NET Core Container Adapter that scoped dependencies registered in .NET Core Startup is singleton within ServiceStack. However, it is not desirable because we want DbContext to be created per request.
My solution is that I move the DbContext registration into AppHost code as below
public override void Configure(Container container)
{
container.AddScoped(c =>
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connection);
return new BloggingContext(optionsBuilder.Options);
});
}
This code works as I expect. Every instance of BloggingContext injected into my BlogService is now unique. However, I find myself unable to use any service collection extension which is very handy in .Net Core Startup anymore. For example, I want to use Entity Framework Unit Of Work and I couldn't call
services
.AddUnitOfWork<BloggingContext>();
Instead, I have to wire up all dependencies of that library myself like
public override void Configure(Container container)
{
container.AddScoped(c =>
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connection);
return new BloggingContext(optionsBuilder.Options);
});
container.AddScoped<IRepositoryFactory, UnitOfWork<BloggingContext>>();
container.AddScoped<IUnitOfWork, UnitOfWork<BloggingContext>>();
container.AddScoped<IUnitOfWork<BloggingContext>, UnitOfWork<BloggingContext>>();
}
You should be able to register it in .NET Core's IOC like any .NET Core App:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BloggingContext>(options =>
options.UseSqlite("Data Source=blog.db"));
}
Then reference like a normal dependency in your ServiceStack Services:
public class MyServices : Service
{
public BloggingContext BloggingContext { get; set; }
}
Which uses ServiceStack's .NET Core Container Adapter to resolve any dependencies not in ServiceStack's IOC, in .NET Core's IOC.
I'm creating an webapi application in dotnet core with entity framework. When I gone through documentation. I can use DI to inject the dbcontext object in dotnet core. But when I'm doing this the whole application using one dbcontext object. How do I make the dbcontext as transient? If any example is there it will really help me.
Please find my existing code below.
This is the code I wrote in ConfigureService
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataAccess.XXXXContext>(options => options.UseMySQL(Configuration["ConnectionStrings:DefaultConnection"]),ServiceLifetime.Transient);
}
This is the code i wrote in DBcontext class
public partial class XXXXContext : DbContext
{
private readonly Logger logger = LogManager.GetCurrentClassLogger();
public XXXXContext(DbContextOptions<XXXXContext> options) :base(options)
{
logger.Debug("XXXXContext created");
}
}
If you see i have written already Transient in the AddDbContext method.So everytime if it create object. My constructor should call. But i'm getting call only once.
I`ve already done this using a separated statement.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataAccess.XXXXContext>(options => options.UseMySQL(Configuration["ConnectionStrings:DefaultConnection"]));
services.AddTransient<DataAccess.XXXXContext>();
}
I am trying to create a NUnit test for a project that uses Ninject. The Ninject was installed via Nuget, so the Configuration clas looks similar to this simplified version:
[assembly: WebActivator.PreApplicationStartMethod(typeof(NinjectMVC3), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(NinjectMVC3), "Stop")]
public static class NinjectMVC3
{
private static readonly Bootstrapper Bootstrapper = new Bootstrapper();
private static IKernel _kernel;
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestModule));
DynamicModuleUtility.RegisterModule(typeof(HttpApplicationInitializationModule));
Bootstrapper.Initialize(CreateKernel);
}
public static void Stop()
{
Bootstrapper.ShutDown();
}
}
I want those methods to be called in my startup test class. I tried:
[TestFixture]
public class TestBase
{
[SetUp]
public void Setup()
{
NinjectMVC3.Startup();
}
[TearDown]
public void TearDown()
{
NinjectMVC3.TearDown();
}
}
It will not work because I am trying to manually call methods that are managed by WebActivator. So I am looking for a way to instruct WebActivator to call those methods in a 'right time'. Let me remind you that there are two project that I am dealing with, one is a MVC Web Project (and it uses WebActivator for Ninject), and the other one is a Test project for my MVC Web Project. I tried to call WebActivator by changing implementation of my Setup method:
[SetUp]
public void Setup()
{
WebActivator.ActivationManager.Run();
}
It doesn't work. As far As I understand underneath this call WebActivator should do something similar to:
foreach (var assemblyFile in Directory.GetFiles(HttpRuntime.BinDirectory, "*.dll")) {
var assembly = Assembly.LoadFrom(assemblyFile);
foreach (PreApplicationStartMethodAttribute preStartAttrib in assembly.GetCustomAttributes(
typeof(PreApplicationStartMethodAttribute),
inherit: false)) {
preStartAttrib.InvokeMethod();
}
}
So I guess that it is unable to find an assembly. So the question is - how can I order WebActivator to scan thru some additional assembly and fire some methods in a 'right time'. Or maybe I am mislead here, and in order to test my Ninject project I should take a different approach?
I am able to test my solutions w/o WebActivator, but because it is widely used recently, I am keen to learn how to deal with it and force it to do things that I want.
I would avoid using WebActivator from your test project as it will not play well outside of asp.net.
If you want to test the setup of your Ninject kernel than i would make the CreateKernel() method public and call that from your Setup() method.
public static IKernel CreateKernel()
...
[SetUp]
public void Setup()
{
NinjectMVC3.CreateKernel();
}
Unfortunately by default WebActivator looks for a "*.dll" in a c:\tmp... directory, and due to that it is unable to find project libriaries that are included to the solution.
I ended up geting the source code and adding a following code to the ActivationManager class:
public static void AddAssembly(Assembly assembly)
{
if (_assemblies == null)
{
_assemblies = new List<Assembly>();
}
_assemblies.Add(assembly);
}
And in test class:
private const int PreStartInitStage_DuringPreStartInit = 1;
[SetUp]
public void Setup(){
WebActivator.ActivationManager.AddAssembly(Assembly.GetAssembly(typeof(NinjectMVC3)));
typeof(BuildManager).GetProperty("PreStartInitStage", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, PreStartInitStage_DuringPreStartInit, null);
WebActivator.ActivationManager.RunPreStartMethods();
Kernel = NinjectMVC3.GetKernel();
}
This is ugly code, and I hope to see one day a better approach.