I am using Entity Framework Core tools to create Entities and DBContext from the existing database.
Scaffold-DbContext "Server=XXXXXX;Database=MyDB;User ID=xxxx;Password=xxxxxxx" Microsoft.EntityFrameworkCore.SqlServer -ContextDir .-OutputDir Entities -Force
This working. But is there any way to scaffold all the entities with known interface? So i have interface IEntityBase and i want all the entities to have this interface
Note
This question is specific to EF Core 2+ and scaffolding
Update 1
So as per the SO Suggesion i have created CSharpEntityTypeGenerator and IDesignTimeServices The accepted answer is not valid for EF Core > 2.* so i am using the suggession from #Chris Peacock
public class MyEntityTypeGenerator: CSharpEntityTypeGenerator
{
public MyEntityTypeGenerator(ICSharpHelper cSharpHelper)
: base(cSharpHelper)
{
}
public override string WriteCode(IEntityType entityType, string #namespace, bool useDataAnnotations)
{
string code = base.WriteCode(entityType, #namespace, useDataAnnotations);
var oldString = "public partial class " + entityType.Name;
var newString = "public partial class " + entityType.Name + " : EntityBase";
return code.Replace(oldString, newString);
}
}
public class MyDesignTimeServices: IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<ICSharpEntityTypeGenerator, MyEntityTypeGenerator>();
}
}
There is one change i had to make. CSharpEntityTypeGenerator constructor takes ICSharpHelper as parameter instead of ICSharpUtilities
These two classes are in Data assembly which is not a startup project.
At package manager console i executed the scaffolding command again. However i do not see generated entities have base class.
How scaffolding framework would know to use my custom generator? I am adding generator in serviceCollection but looks like the code never get executed
Am i missing something?
You can use EF Core Power Tools with Handlebars templates to achieve this.
https://github.com/ErikEJ/EFCorePowerTools/wiki/Reverse-Engineering#customize-code-using-handlebars
I think these options should have been provided as as a part of EF Scaffolding tool by default.
But here is my code. I am using Microsoft.EntityFrameworkCore.Design 2.2.4 and Microsoft.EntityFrameworkCore.SqlServer 2.2.4 and based on my experience with .NET Core this may change in future version
CSharpEntityTypeGenerator
public class MyEntityTypeGenerator: CSharpEntityTypeGenerator
{
public MyEntityTypeGenerator(ICSharpHelper cSharpUtilities)
: base(cSharpUtilities)
{
}
public override string WriteCode(IEntityType entityType, string #namespace, bool useDataAnnotations)
{
Console.WriteLine(entityType.Name);
string code = base.WriteCode(entityType, #namespace, useDataAnnotations);
var oldString = "public partial class " + entityType.Name;
var newString = "public partial class " + entityType.Name + " : EntityBase";
return code.Replace(oldString, newString);
}
}
By default scaffolding generates classes with the same name as Table name. In our case the table names are Pluralize, but we want class name Singularize. So you have to implement Microsoft.EntityFrameworkCore.Design.IPluralizer. I used Inflator utility. However, note that i could not add Inflector using Nuget in .Net Core project. Looks like it does not support .NET Core yet. So i have added the single code file instead in my project.
IPluralizer
public class MyPluralizer : IPluralizer
{
public string Pluralize(string identifier)
{
return Inflector.Pluralize(identifier) ?? identifier;
}
public string Singularize(string identifier)
{
return Inflector.Singularize(identifier) ?? identifier;
}
}
IDesignTimeServices
You need to add this class in startup project. Initially, i had this class in the same project as other classes but that did not work. I moved this class in startup project, (In my case that is ASP.NET Core project) and it worked
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
{
// Start debugger
System.Diagnostics.Debugger.Launch();
serviceCollection.AddSingleton<ICSharpEntityTypeGenerator, MyEntityTypeGenerator>();
serviceCollection.AddSingleton<IPluralizer, MyPluralizer>();
}
}
Related
I have an Azure function running on .NET Core 3.1. I have a .NET Standard 2.1 library that contains an EF Core 3.1 DbContext.
I'm trying to add migrations from Visual Studio and I'm getting the following errors:
If I run PM> Add-Migration Initial selecting as default the function project I get the error 'No DbContext was found in assembly 'SimonApp' (this is my function project). Ensure that you're using the correct assembly and that the type is neither abstract nor generic.'
If I run the same command against the library where EF Core is installed, I get No parameterless constructor defined for type 'SimonApp.Core.Data.ClinikoEntitiesContext'.
I have found lots of posts and questions that are similar on SO but none of them fix my problem.
I have tried creating a parameterless constructor on the context without luck, I get the same errors. My context looks like this:
public class ClinikoEntitiesContext : DbContext
{
public ClinikoEntitiesContext()
{}
public ClinikoEntitiesContext(DbContextOptions<ClinikoEntitiesContext> options)
: base(options)
{ }
My Startup.cs looks like:
class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var configuration = builder.Services.BuildServiceProvider().GetService<IConfiguration>();
var IsDevelopment = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT")?.Equals("Development", StringComparison.OrdinalIgnoreCase);
var connString = configuration.GetConnectionString("SqlCliniko");
builder.Services.AddDbContext<ClinikoEntitiesContext>(options => options.UseSqlServer(connString)
.UseLazyLoadingProxies()
.EnableSensitiveDataLogging(IsDevelopment.HasValue && IsDevelopment.Value == true));
builder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddConsole()
.AddFilter(DbLoggerCategory.Database.Command.Name, LogLevel.Warning);
});
}
}
I'm setting up Entity Framework Core in a new API to deploy to an existing SQL Server database that is used by Entity Framework 4.6 applications. There is one Migration History table that is shared by other applications, and has 2 fields in it that need to be populated for each entry: ContextKey, and Model. Entity Framework Core does not have a Context Key, and does not save the Model to the Migration History table.
I've already created a HistoryRepository : SqlServerHistoryRepository and configured Entity Framework Core to use it, but the ConfigureTable method only allows you to create additional columns, but not actually populate each record as it gets inserted with custom data. Providing a default value to the column is not a solution.
public class HistoryRepository : SqlServerHistoryRepository
{
public HistoryRepository(HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
}
protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history)
{
base.ConfigureTable(history);
history.Property<string>("ContextKey")
.HasMaxLength(300);
history.Property<byte[]>("Model");
}
}
services.AddDbContext<MDSContext>(options =>
options.UseLazyLoadingProxies()
.UseSqlServer(
connectionString,
x => x.MigrationsHistoryTable("__MigrationHistory")).ReplaceService<Microsoft.EntityFrameworkCore.Migrations.IHistoryRepository, Burkhart.CoreServices.IncomingOrders.Core.Models.Base.HistoryRepository>()
);
I should be able to provide a custom value for ContextKey and Model dynamically
I looked all over for solutions, but they all show you how to add a column and set a default value, but not how to set a value dynamically. I ended up digging into the ASP.NET Entity Framework Core source code at GitHub for the solution, so that I would share it with everyone else, as I know there are others that are looking for this information:
Just override the GetInsertScript method on the HistoryRepository and insert your custom values. Here is the full solution:
public class HistoryRepository : SqlServerHistoryRepository
{
public HistoryRepository(HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
}
protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history)
{
base.ConfigureTable(history);
history.Property<string>("ContextKey")
.HasMaxLength(300);
history.Property<byte[]>("Model");
}
public override string GetInsertScript(HistoryRow row)
{
var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));
return new StringBuilder().Append("INSERT INTO ")
.Append(SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema))
.Append(" (")
.Append(SqlGenerationHelper.DelimitIdentifier(MigrationIdColumnName))
.Append(", ")
.Append(SqlGenerationHelper.DelimitIdentifier(ProductVersionColumnName))
.Append(", [ContextKey], [Model])")
.Append("VALUES (")
.Append(stringTypeMapping.GenerateSqlLiteral(row.MigrationId))
.Append(", ")
.Append(stringTypeMapping.GenerateSqlLiteral(row.ProductVersion))
.Append($", '{ContextConstants.ContextName}.{ContextConstants.ContextSchemaName}', 0x)")
.AppendLine(SqlGenerationHelper.StatementTerminator)
.ToString();
}
}
Here is a link to the source code on github:
https://github.com/aspnet/EntityFrameworkCore/blob/master/src/EFCore.Relational/Migrations/HistoryRepository.cs
Ok, I want to recreate a project that I created using EF 4.1 to EF 5.0, simple enough or at least I thought. One of the things in my old project is that I was able to change the database connection string at runtime in EF 4.1:
using (var myContext = new MyEntities(ConnectionString))
{
}
Easy-peasy but in EF 5.0 you have to do this differently:
string connectionString = "data source=LocalHost;initial catalog=MyDatabase;user id=MyUserName;password=MyPassword;multipleactiveresultsets=True;App=EntityFramework";
using (var myContext = new MyEntities())
{
myContext.Database.Connection.ConnectionString = connectionString;
}
Now, this took me a better part of two hours to figure out, so I guess my question is this the proper way of changing the connection string at runtime or not? If it is why did they make this change?
I did find this Link but it didn't work. I received the error as detailed in the first comment of the first answer by Ladislav Mrnka. I later found this Link which seems to work fine.
UPDATE
I re-read the first link I posted and I found another solution, I simply created a partial class:
public partial class MyEntities : DbContext
{
public MyEntities(string connectionString) : base(connectionString)
{
Database.Connection.ConnectionString = connectionString;
}
}
Use the context constructor overload that takes the connection string as a parameter.
Create a class with the same name as the Target ContextClass class next to the main class
like this :
public CustomerContext( string connectionString)
: base(connectionString)
{
}
For using :
using (var context = new CustomerContext("connectionString"))
{
}
Or
var customerContext=new CustomerContext("yorConnectionString");
var customer=CustomerContext.customer.FirstOrDefault(x=>x.id==1).FirstName;
Have a look at other link Setup Entity Framework For Dynamic Connection String.
It says - " you can do it by creating another partial class as the Entities class is declared partial"
I am trying to use mvc-mini-profiler for db-first EF, but it is not working properly.
(note that I'm using objectcontext, not dbcontext)
Here is the list of stackoverflows I've tried:
Setup of mvc-mini-profiler for EF-db- first
How to get MVC-mini-profiler working on EF 4.1 Database First
versions:
Entity Framework: 4.3.1
MiniProfiler: 2.0.2
MiniProfiler.ef: 2.0.3
This is how I setup miniprofiler:
I've added the following stuff in Global.asax
protected void Application_BeginRequest(
{
MiniProfiler.Start();
}
protected void Application_EndRequest()
{
MiniProfiler.Stop();
}
protected void Application_Start()
{
...
MiniProfilerEF.Initialize_EF42();
}
Then configure an objectcontext,
var entityConnection = new EntityConnection(ConnectionString);
var profiledDbConnection = new EFProfiledDbConnection(entityConnection, MiniProfiler.Current);
var context = profiledDbConnection.CreateObjectContext<MyContext>();
var list = context.MyEntities.ToList();
If I execute this, the following exception occurs when running "context.MyEntities.ToList()"
[System.Data.EntityCommandCompliationException]
the message in the inner exception says:
EntityClient cannot be used to create a command definition from a store command tree.
Have I configured wrong? Any help?
thanks,
I use MiniProfiler and database first Entity Framework and it does work well. You may need to turn off the database initialization strategy inside of your database context as per this related answer: https://stackoverflow.com/a/9762989/325727
public class EmployeeContext : DbContext
{
static EmployeeContext() { Database.SetInitializer<EmployeeContext>(null); }
public IDbSet<Employee> Employees { get; set; }
}
The parameter null turns off database initialization by making sure that there is no initializer available.
The default constructor in a generated Entity Framework Entities file is like this:
public ProjectEntities() : base("name=ProjectEntities", "ProjectEntities")
{
this.OnContextCreated();
}
I want to change it to:
public ProjectEntities() : base(UtilClass.GetEnvDependantConnectionStringName(), "ProjectEntities")
{
this.OnContextCreated();
}
This is because I want to have a different connection string for all the dev environments and the production environment, and have no chance they are mixed up (which is what my custom method checks).
How do I do that? This code is thrown away every time the designer file is regenerated.
You need to create another file alongside the auto-created ProjectEntities.Designer.cs, say ProjectEntities.cs. In that you use partial to extend the functionality of your entities class like this:
public partial class ProjectEntities : ObjectContext
{
partial void OnContextCreated()
{
this.Connection.ConnectionString = UtilClass.GetEnvDependantConnectionString();
}
}
The file won't then get changed when you regenerate the .Designer.cs file. You'll have to fetch the connection string yourself...
We fixed it by calling our entities ProjectEntitiesPrivate, and what was partial class ProjectEntities before, is now a non partial class ProjectEntities : ProjectEntitiesPrivate, with the constructor I need:
public class ProjectEntities: ProjectEntitiesPrivate
{
public ProjectEntities():base(UtilClass.GetEnvDependantConnectionStringName())
{
}
....