Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example code without BuildWebHost breaks Entity Framework 2.0 #10

Closed
janpieterz opened this issue Oct 16, 2017 · 7 comments
Closed

Example code without BuildWebHost breaks Entity Framework 2.0 #10

janpieterz opened this issue Oct 16, 2017 · 7 comments

Comments

@janpieterz
Copy link

janpieterz commented Oct 16, 2017

Took me half a morning before I figured out what was exactly going on.
My Identity Server project used the provided way here to create the host, but Entity Framework relies on the BuildWebHost method to analyse it for contexts.

Let me know if you'd like me to change the sample and docs here to below, I believe it contains all that's needed for a valid Serilog + AspNetCore flow.

Program.cs:

public class Program
    {
        public static IConfiguration Configuration { get; set; }
        public static int Main(string[] args)
        {
            Configuration = CreateConfiguration();

            Logging.Config(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development");

            try
            {
                Log.Information("Starting up application");

                var host = BuildWebHost(args);

                host.Run();

                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        private static IConfiguration CreateConfiguration()
        {
            return new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .Build();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseConfiguration(CreateConfiguration())
                .UseSerilog()
                .UseStartup<Startup>()
                .Build();
    }
@nblumhardt
Copy link
Member

Hi Jan-Pieter, thanks for the report.

Do you mean that a method called BuildWebHost needs to exist? I think I might be misunderstanding what you mean here, as that seems slightly strange... :-) Is there any way of narrowing the change down to a single line of code?

Thanks again!

@janpieterz
Copy link
Author

Hi Nick!

Yep, the BuildWebHost needs to exists, that's what it ended up as. The unbelievable bit was what made it cost so much time, I never thought this was the issue and compared a working sample and a not-working app, and brought them closer and closer until this was the only difference.

For reference:

dotnet/efcore#9415 (comment)
and the linked article (bottom of the section)
https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#update-main-method-in-programcs

Single LOC change might be difficult as the Configuration also needs to be callable from that static bit as well as the normal flow.
Currently I create it twice, which certainly isn't pretty, just to be able to (in other apps) have the configuration for Seq.

@nblumhardt
Copy link
Member

Wow, thanks for the links, that's frankly one of the strangest design points I've seen in an ORM to date. Would love to know the backstory. I'll take a look at the sample again 👍

@nblumhardt
Copy link
Member

@janpieterz I've updated the example in the main README. I'm keen to update the sample repository, but I'd love to come up with a way to do it that doesn't double up on loading the configuration... May be a bit hopeful on my part... Any ideas?

@janpieterz
Copy link
Author

Nice. Yeah, I'd be curious what the MS team has to say about this.

We could look to set a private static variable that a GetConfiguration method returns?
Not the prettiest.

private static IConfiguration _cachedConfig;

private static IConfiguration GetConfiguration()
{
    if (_cachedConfig == null)    
    {
        _cachedConfig = new ConfigurationBuilder()        
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .Build();
    }
    return _cachedConfig;
}

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseConfiguration(GetConfiguration())
        .UseSerilog()
        .UseStartup<Startup>()
        .Build();

@nblumhardt nblumhardt changed the title Recommended way breaks Entity Framework 2.0 Example code without BuildWebHost breaks Entity Framework 2.0 Oct 24, 2017
@RehanSaeed
Copy link

RehanSaeed commented Dec 12, 2017

I also wasted a couple of hours on this one. The fact that the method needs to be called BuildWebHost needs to be called out in giant red letters in the ReadMe. One additional thing to note is that if you are initializing your database in BuildWebHost, that logic also needs to be moved out and placed after the Log object is initialized like so:

public static class Program
{
    public static int Main(string[] args)
    {
        var webHost = BuildWebHost(args);
        Log.Logger = webHost.GetLogger();
        webHost.InitializeDatabase(); // THIS MUST GO HERE TO GET DATABASE LOGS

        try
        {
            Log.Information("Starting application");
            webHost.Run();
            Log.Information("Stopped application");
            return 0;
        }
        catch (Exception exception)
        {
            Log.Fatal(exception, "Application terminated unexpectedly");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

    public static IWebHost BuildWebHost(string[] args) =>
        new WebHostBuilder()
            .UseKestrel(options => options.AddServerHeader = false)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .ConfigureAppConfiguration((hostingContext, config) =>
                config.AddBridgeDefaultConfiguration(hostingContext.HostingEnvironment, args))
            .UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build())
            .UseIISIntegration()
            .UseDefaultServiceProvider((context, options) =>
                options.ValidateScopes = context.HostingEnvironment.IsDevelopment())
            .UseStartup<Startup>()
            .UseSerilog()
            .Build();
}

If you intitialize your database before the Log object is initialized, you will get zero EF Core logs and will be questioning your life choices.

@nblumhardt
Copy link
Member

Thanks for jumping in, @RehanSaeed 👍 .. The sample is now updated ( #17 ) so I'll close this - I think it'd be a great topic for a blog post, which might be a better candidate for top Google result for "serilog entity framework core" than we can achieve with the README. Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants