Scaffolding Entity Framework Controllers with Lamar - c#

I have Lamar set up in my .NET Core 2 project:
public class Program
{
public static void Main(string[] args)
{
IWebHost webhost = CreateWebHostBuilder(args).Build();
//((Container)webhost.Services).GetInstance<IStart>().Run();
webhost.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseLamar()
.UseStartup<Startup>();
}
...
public class Startup
{
...
public void ConfigureContainer(ServiceRegistry services)
{
services.Configure<Configuration.Auth>("auth", Configuration);
...
services.Scan(s =>
{
s.TheCallingAssembly();
s.WithDefaultConventions();
});
services.AddCors();
services.AddMvc()
.AddJsonOptions(o =>
{
o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
o.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<Context>(options => options.UseSqlServer(Configuration.GetConnectionString("defaultConnection")));
}
}
However, when attempting to use Scaffold API Controller with actions, using Entity Framework I run into the following error:
There was an error running the selected code generator: 'No parameterless constructor defined for this object.'
Looking up this https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/?view=aspnetcore-2.2#update-main-method-in-programcs suggested that this can show up in ASP.NET Core 2 projects that attempt to still use the .NET 1.x structure.
I found a hacky work-around that I'll post below, which suggests that the scaffolding code generation may have an issue with Lamar. However, is there a better solution? Can you set up Lamar to be able to handle Entity Framework Code generation?

Considering EF was failing in the generate code section, I wondered if perhaps the issue was not the parameterless constructor (I'm pretty sure that whatever unnamed object it was referring to actually has one) but the issue with how the WebHost gets built when using Lamar.
The note in the Lamar documentation on integrating with ASP.NET Core states
Note! The Startup.ConfigureServices(ServiceRegistry) convention does not work as of ASP.Net Core 2.1. Use ConfigureContainer(ServiceRegistry) instead.
I was using that Lamar function in my Startup; however, if I changed it back to ConfigureContainer(IServiceCollection services) (and commented out the Lamar-specific functions, such as Scan), I found that I was able to scaffold the EF controller again.
So, at the moment, my workaround is to comment out Lamar before scaffolding, and then uncomment it back once I'm done. I suspect there may be a better solution though...

Related

Entity Framework Core - No referenced design-time services were found

I am trying to run 'update-database -verbose' in the Package Manager Console but I am getting the following lines at the end of the output: (nothing is being generated in my SQL server)
Using context 'TutoringContext'.
Finding design-time services for provider 'Microsoft.EntityFrameworkCore.SqlServer'...
Using design-time services from provider 'Microsoft.EntityFrameworkCore.SqlServer'.
Finding design-time services referenced by assembly 'LakeTutoringWebsite'...
Finding design-time services referenced by assembly 'LakeTutoringWebsite'...
No referenced design-time services were found.
Finding IDesignTimeServices implementations in assembly 'LakeTutoringWebsite'...
No design-time services were found.
I see that I can create a DesignTimeDbContextFactory like this: https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli
But the constructor must be parameterless so I can't pass an IConfiguration object to get my connection string. How can I run 'update-database' without hard coding my connection string?
Based on the above link, I thought I would be able to run 'update-database' since I have added my DBContext to services.
I am using dependency injection for my DBContext in my project:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<TutoringContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LakeTutoringDatabase")));
}
}
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class TutoringContext : IdentityDbContext<IdentityUser>
{
public DbSet<Comment> Comments { get; set; }
public TutoringContext(DbContextOptions<TutoringContext> options) : base(options)
{
}
}
I am using ASP.NET Core 3.1
I installed System.Configuration.Configuration version 6.0.0 using NuGet.
Firstly, as well as creating an implementation of IDesignTimeDbContextFactory, your project will need to add a reference to package:
Microsoft.EntityFrameworkCore.Design
Normally, hard-coded strings are not so bad in this class because you are just working with a development database. Ideally, you would apply migrations to other databases (staging, beta, production) in code (instead of update-database in command line) at application startup which will use values from your appsettings.json file accessed thru IConfiguration.
However, if you do want to have the development connection string accessed from a json file, this link does a pretty good job of walking you thru it.

Add support for Entity Framework in Azure function

I have the following project structure in my Azure function:
Application.Function
Application.Domain
Application.Infrastructure
This works fine. All the dependencies are resolved without any errors.
However, when I setup Entity Framework in my Infrastructure layer, and I'm trying to run the application, I get the following error:
webjobsbuilderextensions.cs not found
When I remove all the Entity Framework related things and try to run the application, it works again.
In my Startup.cs I have the following code snippet:
builder.Services.AddDomain(connectionString);
Application.Domain.Injections:
public static class Injections
{
public static IServiceCollection AddDomain(this IServiceCollection services, string connectionString)
{
services.AddTransient<ISalesItemService, SalesItemService>();
services.AddPersistence(connectionString)
return services;
}
}
Application.Infrastructure.Injections:
public static class Injections
{
public static IServiceCollection AddPersistence(this IServiceCollection services, string connectionString)
{
services.AddTransient<ISalesItemDataService, SalesItemDataService>();
services.AddDbContext<IOnePlmSubContext, OnePlmSubContext>(
options => options.UseSqlServer(connectionString),
ServiceLifetime.Transient,
ServiceLifetime.Transient);
return services;
}
}
Has anyone else experienced the same issue that I have? Can't I have this kind of layered structure when working with Azure functions?
Summarize the comments for other communities reference:
Just downgrade version of EF Core to 3.1 and re-build the function, then re-rest the funciton, it works.

How to add Json Formatters to MvcCore?

I'm following the next tutorial of IdentityServer4 implementation for API , but I can't call the method AddJsonFormatters() to services.AddMvcCore().
I'm currently configuring the API from an empty template in ASP.NET Core 3.0.0
I have added NuGet package Microsoft.AspNetCore.Mvc.Formatters.Json with no results.
Also, I understand that using AddMvc() instead of AddMvcCore() would be a partial solution but I can't use AddAuthorization() on AddMvc()
//code extracted from the link
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
}
}
This is the error message I see above:
'IMvcCoreBuilder' does not contain a definition for
'AddJsonFormatters' and no accessible extension method
'AddJsonFormatters' accepting a first argument of type
'IMVCoreBuilder' could be found (are you using a missing directive or
an assembly reference?)
Is this the method? Should I send an MVCCoreBuilder? How do I do that? MvcJsonMvcCoreBuilderExtensions.AddJsonFormatters Method
When you call services.AddMvc() you get an IMvcBuilder.
if you want to add more output or input formatters, the IMvcBuilder has an extension method that you can call AddMvcOptions bellow you have an example of an XmlDataContractSerializerOutputFormatter that was added
mvcBuilder.AddMvcOptions(options =>
{
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter(options));
Mvc already has a JsonOutputFormatter ,so inside of the AddMvcOptions you can get it and also and add your own custom mediatypes if you need it.
var jsonOutputFormatter = options.OutputFormatters.OfType<JsonOutputFormatter>().FirstOrDefault();
if (jsonOutputFormatter != null)
{
jsonOutputFormatter.SupportedMediaTypes.Add(HttpMediaTypes.Vnd+json.all);
jsonOutputFormatter.SupportedMediaTypes.Add(HttpMediaTypes.ApplicationOctetStream);
}
As I understood, there is not class MvcJsonMvcCoreBuilderExtensions in .NET Core 3.0 yet.
Eventually I just added -f parameter when I was created the Api project:
dotnet new web -n Api -f netcoreapp2.2
instead of
dotnet new web -n Api
It makes the Api project for .NET Core 2.2 so you can read the tutorial.
I was having the same issue. I found that I was on the wrong tutorial for implementing it with the current versions of dotnet core. The comment made by -AnorZaken helped:
Check the tutorial again, it has been updated to NetCore3
Look at the top of the sidebar on the tutorial page. If it says "Release" under the IdentityServer4 title, that won't work.
There is a dropdown at the bottom of the sidebar where you can select "3.1" instead.
Use this:
If MVC
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver());
.....
.....
if it is API than use
services.AddControllersWithViews().AddNewton ......

Statuscode 406 (Not Acceptable) in ASP.NET Core

REST services should provide content negotiation. This means that clients send an Accept header that contains the desired content type of the response. If the service does not support this media type, it should respond with status code 406 (Not Acceptable).
I try to map this behavior to ASP.NET Core. ASP.NET core returns a JSON document, if it doesn't recognize the media type in the Accept header. In previous versions of the framework the behavior described above could be achieved by adding a special output formatter to the configuration:
public void ConfigureServices(IServiceCollection services) {
services.AddMvc(options => {
options.OutputFormatters.Insert(0, new HttpNotAcceptableOutputFormatter());
});
}
Unfortunately, HttpNotAcceptableOutputFormatter was removed from the ASP.NET Core framework after RC1. Is there any replacement for this class in the current version of the framework?
I had this before:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
Then I change it to AddMvcCore() instead of AddMvc()
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore();
}
Finally I had that issue with the Response 406 so what I did was to add .AddJsonFormatters() to services.AddMVCCore() and my API worked again.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters();
}
In such cases, it’s a good idea to find the commit that removed the functionality, to see what it likely got replaced with. In this case, HttpNotAcceptableOutputFormatter was removed with this commit to fix issue #4612:
Alter content negotiation algorithm so that it can be configured (via MvcOptions) to always respect an explicit Accept header.
What it was replaced with is MvcOptions.ReturnHttpNotAcceptable, which is a setting on the MvcOptions that you configure when adding MVC with AddMvc.
So your code should become like this:
services.AddMvc(options =>
{
options.ReturnHttpNotAcceptable = true;
});
You add this to the ConfigureService method in the Startup class.
services.AddMvc(options =>
{
options.ReturnHttpNotAcceptable = true;
// If you need to add support for XML
// options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
});
None of the above answer worked for me , finally this worked
Adding the following line in ConfigureServices of Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().AddJsonFormatters().AddApiExplorer();
}

Is there an in memory job storage package for Hangfire?

I have a console application to test HangFire. Here is the code:
using System;
using Hangfire;
namespace MyScheduler.ConsoleApp
{
internal static class Program
{
internal static void Main(string[] args)
{
MyMethod();
Console.WriteLine("[Finished]");
Console.ReadKey();
}
private static void MyMethod()
{
RecurringJob.AddOrUpdate(() => Console.Write("Easy!"), Cron.Minutely);
}
}
}
But it throws an exception on runtime:
Additional information: JobStorage.Current property value has not been
initialized. You must set it before using Hangfire Client or Server
API.
So I need a job storage to run this. But all examples in SQL storage etc. Is there any way to run this example with some kind of memory storage?
JobStorage.Current = new SqlServerStorage("ConnectionStringName", options);
// to
JobStorage.Current = new MemoryDbStorage(string.Empty, options);
You can use Hangfire.MemoryStorage for this.
Simply add this nuget package.
And then you can use it like -
GlobalConfiguration.Configuration.UseMemoryStorage();
For NET Core (web application):
Just to make it very obvious because it wasn't obvious to me.
Install following nuget packages:
Hangfire.AspNetCore (v1.6.17 atow)
Hangfire.MemoryStorage.Core (v1.4.0 atow)
In Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// other registered services
...
services.AddHangfire(c => c.UseMemoryStorage());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// other pipeline configuration
...
app.UseHangfireServer();
app.UseMvc();
}
Anything less than above and my enqueued method did not fire.
As Yogi said, you can use Hangfire.MemoryStorage or Hangfire.MemoryStorage.Core (for .Net Core).
What is missing in that answer is the complete code (for .Net Core) that needs to be put inside ConfigureServices(..) :
var inMemory = GlobalConfiguration.Configuration.UseMemoryStorage();
services.AddHangfire(x => x.UseStorage(inMemory));
Just for the sake of completeness the author of the Hangfire library has added a new package titled Hangfire.InMemory the version of which is available on Nuget. The repository readme positions it as targeting production use. A quote github repo URL is as follows "..an efficient transactional in-memory storage for Hangfire with data structures close to their optimal representation. The result of this attempt should enable production-ready usage of this storage implementation and handle particular properties of in-memory processing.."
The familiar configuration concept applies here as well:
GlobalConfiguration.Configuration.UseInMemoryStorage();
I personally added it as follows:
services.AddHangfire(configuration => { configuration.UseInMemoryStorage(); });

Categories

Resources