To run migrations at the moment, we have to run the cli tool which runs in a seperate process and calls back into the project (building the project in the process) to then reflect on it and find various things before producing the migrations.
This model suffers from various issues like:
Can I suggest an alternative model:
Using the above approach:
Consider at the moment, we are able to add ef to a netstandard1.5 library project. But good luck getting the dotnet-ef tool to run to generate migrations for that. You end up having to create shim project just to be able to have the tooling run!
using (var db = new MyDbContext())
{
db.Database.Migrate();
}
using (var db = new MyDbContext())
{
var reporter = new OperationReporter(
new OperationReportHandler(
m => Console.WriteLine(" error: " + m),
m => Console.WriteLine(" warn: " + m),
m => Console.WriteLine(" info: " + m),
m => Console.WriteLine("verbose: " + m)));
var designTimeServices = new ServiceCollection()
.AddSingleton(db.GetService<IHistoryRepository>())
.AddSingleton(db.GetService<IMigrationsIdGenerator>())
.AddSingleton(db.GetService<IMigrationsModelDiffer>())
.AddSingleton(db.GetService<IMigrationsAssembly>())
.AddSingleton(db.Model)
.AddSingleton(db.GetService<ICurrentDbContext>())
.AddSingleton(db.GetService<IDatabaseProvider>())
.AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<ICSharpHelper, CSharpHelper>()
.AddSingleton<CSharpMigrationOperationGeneratorDependencies>()
.AddSingleton<ICSharpMigrationOperationGenerator, CSharpMigrationOperationGenerator>()
.AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<ICSharpSnapshotGenerator, CSharpSnapshotGenerator>()
.AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<IMigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton<IOperationReporter>(reporter)
.AddSingleton<MigrationsScaffolderDependencies>()
.AddSingleton<MigrationsScaffolder>()
.BuildServiceProvider();
var scaffolder = designTimeServices.GetRequiredService<MigrationsScaffolder>();
var migration = scaffolder.ScaffoldMigration(
"MyMigration",
"MyApp.Data");
File.WriteAllText(
migration.MigrationId + migration.FileExtension,
migration.MigrationCode);
File.WriteAllText(
migration.MigrationId + ".Designer" + migration.FileExtension,
migration.MetadataCode);
File.WriteAllText(migration.SnapshotName + migration.FileExtension,
migration.SnapshotCode);
}
Would be nice with some sugar on top of the second one
Thanks @bricelam - I can use that to get up and running.
Agree with @ErikEJ - would be brilliant to see a fluent api for that longer term!
I agree building the design-time service provider should be simplified. However, we don't typically add design-time APIs to DbContext
, so the sugar would likely stop there.
Could you also provide / point to example of running a scaffold
to programtically generate model from a set of tables? (i.e db first generation)
@ErikEJ - much obliged!
damn.. I actually need this for 1.0.x (sorry) - the code on this issue is all for 1.1.0 I believe. Upgrading to 1.1.0 isn't something I can do very easily at present :-(
The code is actually for 2.0.0. It shouldn't be too bad to backport. Try removing some of the I
s from the services and just remove the non-existent ones.
I gave it a go. I can't seem to find the following service (in 1.0.x):
var scaffolder = designTimeServices.GetRequiredService<MigrationsScaffolder>();
Is there an alternative for MigrationsScaffolder
or perhaps I am missing a package reference or using
directive?
I also have commented out (hopefully they aren't essential):
OperationReporter
and OperationReportHandler
and:
.AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<ICSharpHelper, CSharpHelper>()
.AddSingleton<CSharpMigrationOperationGeneratorDependencies>()
.AddSingleton<ICSharpMigrationOperationGenerator, CSharpMigrationOperationGenerator>()
.AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<ICSharpSnapshotGenerator, CSharpSnapshotGenerator>()
.AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<IMigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton<IOperationReporter>(reporter)
.AddSingleton<MigrationsScaffolderDependencies>()
.AddSingleton<MigrationsScaffolder>()
Ah found them. I had a problem with the reference to: Microsoft.EntityFrameworkCore.Design
So, after some tinkering, here is a 1.0.x equivalent:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore.Migrations.Design;
using Reach.GCv3.Data.EF7.Models;
using System;
using System.IO;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Storage.Internal;
namespace Scaffolder
{
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddLogging();
var connString = "your conn string";
services.AddDbContext<YourDbContext>((optionsBuilder) =>
{
optionsBuilder.UseSqlServer(connString);
});
var serviceProvider = services.BuildServiceProvider();
var scopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
using (var db = scope.ServiceProvider.GetRequiredService<YourDbContext>())
{
var reporter = new OperationReporter(
new OperationReportHandler(
m => Console.WriteLine(" error: " + m),
m => Console.WriteLine(" warn: " + m),
m => Console.WriteLine(" info: " + m),
m => Console.WriteLine("verbose: " + m)));
var designTimeServices = new ServiceCollection()
.AddLogging()
.AddSingleton(db.GetService<IHistoryRepository>())
.AddSingleton(db.GetService<IMigrationsIdGenerator>())
.AddSingleton(db.GetService<IMigrationsModelDiffer>())
.AddSingleton(db.GetService<IMigrationsAssembly>())
.AddSingleton(db.Model)
.AddSingleton(db.GetService<ICurrentDbContext>())
.AddSingleton(db.GetService<IDatabaseProvider>())
// .AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<CSharpHelper>()
// .AddSingleton<CSharpMigrationOperationGeneratorDependencies>()
.AddSingleton<CSharpMigrationOperationGenerator>()
// .AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<CSharpSnapshotGenerator>()
//.AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<MigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton<IOperationReporter>(reporter)
// .AddSingleton<MigrationsScaffolderDependencies>()
.AddSingleton<MigrationsScaffolder>()
.AddSingleton<IDatabaseProviderServices, SqlServerDatabaseProviderServices>()
.AddSingleton<IRelationalDatabaseProviderServices, SqlServerDatabaseProviderServices>()
.BuildServiceProvider();
var scaffolder = designTimeServices.GetRequiredService<MigrationsScaffolder>();
var migration = scaffolder.ScaffoldMigration(
"MyMigration",
"MyApp.Data");
File.WriteAllText(
migration.MigrationId + migration.FileExtension,
migration.MigrationCode);
File.WriteAllText(
migration.MigrationId + ".Designer" + migration.FileExtension,
migration.MetadataCode);
File.WriteAllText(migration.SnapshotName + migration.FileExtension,
migration.SnapshotCode);
}
}
}
}
}
I had to add Logging
and IDatabaseProviderServices
registrations to the designer services collection.
Ok so I can successfully generate migrations, and now I am trying to reverse engineer like @ErikEJ 's code shows!
Problem I have is I am not sure how to register: .ReverseEngineeringGenerator
in 1.0.x?
At the moment if I just add it like this:
.AddSingleton<ReverseEngineeringGenerator>()
Then when I resolve it I get an exception that IScaffoldingModelFactory
can't be resolved. I can't find an implementation of IScaffoldingModelFactory
anywhere though to register.
Right so I am close.. but I have been thwarted.
After figuring out where things are in 1.0.x. and adding a bunch of additional service registrations, I can now resolve the ReverseEngineeringGenerator
. The problem is, when I try to use it I get this exception:
I have no idea what that exception message means.
Here are my services:
var services = new ServiceCollection();
services.AddLogging();
var connString = "MY REAL SQL SERVER CONN STRING IS HERE";
services.AddDbContext<MyDbContext>((optionsBuilder) =>
{
optionsBuilder.UseSqlServer(connString);
});
var serviceProvider = services.BuildServiceProvider();
var scopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
using (var db = scope.ServiceProvider.GetRequiredService<MyDbContext>())
{
var reporter = new OperationReporter(
new OperationReportHandler(
m => Console.WriteLine(" error: " + m),
m => Console.WriteLine(" warn: " + m),
m => Console.WriteLine(" info: " + m),
m => Console.WriteLine("verbose: " + m)));
var designTimeServices = new ServiceCollection()
.AddLogging()
.AddSingleton(db.GetService<IHistoryRepository>())
.AddSingleton(db.GetService<IMigrationsIdGenerator>())
.AddSingleton(db.GetService<IMigrationsModelDiffer>())
.AddSingleton(db.GetService<IMigrationsAssembly>())
.AddSingleton(db.Model)
.AddSingleton(db.GetService<ICurrentDbContext>())
.AddSingleton(db.GetService<IDatabaseProvider>())
// .AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<CSharpHelper>()
// .AddSingleton<CSharpMigrationOperationGeneratorDependencies>()
.AddSingleton<CSharpMigrationOperationGenerator>()
// .AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<CSharpSnapshotGenerator>()
// .AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<MigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton<IOperationReporter>(reporter)
// .AddSingleton<MigrationsScaffolderDependencies>()
.AddSingleton<MigrationsScaffolder>()
.AddSingleton<IDatabaseProviderServices, SqlServerDatabaseProviderServices>()
.AddSingleton<IRelationalDatabaseProviderServices, SqlServerDatabaseProviderServices>()
.AddSingleton<ReverseEngineeringGenerator>()
.AddSingleton<IScaffoldingModelFactory, SqlServerScaffoldingModelFactory>()
.AddSingleton<IRelationalTypeMapper, SqlServerTypeMapper>() //RelationalTypeMapper
.AddSingleton<IDatabaseModelFactory, SqlServerDatabaseModelFactory>()
// .AddSingleton<AnnotationCodeGeneratorDependencies>()
.AddSingleton<IFileService, FileSystemFileService>()
// .AddSingleton<RelationalTypeMapperDependencies>()
// .AddSingleton<IModelScaffolder, ModelScaffolder>()
.AddSingleton<CandidateNamingService>()
// .AddSingleton<IPluralizer, NullPluralizer>()
.AddSingleton<CSharpUtilities>()
// .AddSingleton<ICSharpDbContextGenerator, CSharpDbContextGenerator>()
// .AddSingleton<ICSharpEntityTypeGenerator, CSharpEntityTypeGenerator>()
.AddSingleton<ScaffoldingTypeMapper>()
.AddSingleton<ConfigurationFactory>()
// .AddSingleton<IScaffoldingCodeGenerator, CSharpScaffoldingGenerator>()
.AddSingleton<IScaffoldingModelFactory, RelationalScaffoldingModelFactory>()
.AddSingleton<IRelationalAnnotationProvider, SqlServerAnnotationProvider>()
.AddSingleton<CodeWriter, StringBuilderCodeWriter>()
.AddSingleton<DbContextWriter>()
.AddSingleton<EntityTypeWriter>()
.AddSingleton<ScaffoldingUtilities>()
.BuildServiceProvider();
var currentPath = Environment.CurrentDirectory;
var outputPath = Path.Combine(currentPath, "output");
var tables = new string[] {
"Foo",
"Bar"
};
var tableSelection = new Microsoft.EntityFrameworkCore.Scaffolding.TableSelectionSet(tables);
var options = new ReverseEngineeringConfiguration
{
ConnectionString = connString,
ProjectPath = currentPath,
OutputPath = outputPath,
ProjectRootNamespace = "My.Namespace",
OverwriteFiles = true,
UseFluentApiOnly = true,
ContextClassName = "MyDbContext",
TableSelectionSet = tableSelection
};
var generator = designTimeServices.GetService<ReverseEngineeringGenerator>();
var model = generator.GetMetadataModel(options);
// ALERT: Following line throws!
var filePaths = generator.GenerateAsync(options).GetAwaiter().GetResult();
var errors = model.Scaffolding().EntityTypeErrors;
var contextFilePath = filePaths.ContextFile;
var entityTypeFilePaths = filePaths.EntityTypeFiles;
}
}
Found the culprit. This registration:
.AddSingleton<IScaffoldingModelFactory, RelationalScaffoldingModelFactory>()
Should have been:
.AddSingleton<IScaffoldingModelFactory, SqlServerScaffoldingModelFactory>()
and I was also registering it twice :-(
Here is the working code for 1.0.x the service registrations are enough to scaffold migrations and also reverse engineer - using sql server:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore.Migrations.Design;
using System;
using System.IO;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.EntityFrameworkCore.Scaffolding.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Scaffolding;
namespace Scaffolder
{
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddLogging();
var connString = "YOUR CONN STRING";
services.AddDbContext<YourDbContext>((optionsBuilder) =>
{
optionsBuilder.UseSqlServer(connString);
});
var serviceProvider = services.BuildServiceProvider();
var scopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
using (var db = scope.ServiceProvider.GetRequiredService<YourDbContext>())
{
var reporter = new OperationReporter(
new OperationReportHandler(
m => Console.WriteLine(" error: " + m),
m => Console.WriteLine(" warn: " + m),
m => Console.WriteLine(" info: " + m),
m => Console.WriteLine("verbose: " + m)));
var designTimeServices = new ServiceCollection()
.AddLogging()
.AddSingleton(db.GetService<IHistoryRepository>())
.AddSingleton(db.GetService<IMigrationsIdGenerator>())
.AddSingleton(db.GetService<IMigrationsModelDiffer>())
.AddSingleton(db.GetService<IMigrationsAssembly>())
.AddSingleton(db.Model)
.AddSingleton(db.GetService<ICurrentDbContext>())
.AddSingleton(db.GetService<IDatabaseProvider>())
.AddSingleton<CSharpHelper>()
.AddSingleton<CSharpMigrationOperationGenerator>()
.AddSingleton<CSharpSnapshotGenerator>()
.AddSingleton<MigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton<IOperationReporter>(reporter)
.AddSingleton<MigrationsScaffolder>()
.AddSingleton<IDatabaseProviderServices, SqlServerDatabaseProviderServices>()
.AddSingleton<IRelationalDatabaseProviderServices, SqlServerDatabaseProviderServices>()
.AddSingleton<ReverseEngineeringGenerator>()
.AddSingleton<IScaffoldingModelFactory, SqlServerScaffoldingModelFactory>()
.AddSingleton<IRelationalTypeMapper, SqlServerTypeMapper>() //RelationalTypeMapper
.AddSingleton<IDatabaseModelFactory, SqlServerDatabaseModelFactory>()
.AddSingleton<IFileService, FileSystemFileService>()
.AddSingleton<CandidateNamingService>()
.AddSingleton<CSharpUtilities>()
.AddSingleton<ScaffoldingTypeMapper>()
.AddSingleton<ConfigurationFactory>()
.AddSingleton<IRelationalAnnotationProvider, SqlServerAnnotationProvider>()
.AddSingleton<CodeWriter, StringBuilderCodeWriter>()
.AddSingleton<DbContextWriter>()
.AddSingleton<EntityTypeWriter>()
.AddSingleton<ScaffoldingUtilities>()
.BuildServiceProvider();
var currentPath = Environment.CurrentDirectory;
var outputPath = Path.Combine(currentPath, "output");
var tables = new string[] {
"Foo",
"Bar"
};
var tableSelection = new Microsoft.EntityFrameworkCore.Scaffolding.TableSelectionSet(tables);
var options = new ReverseEngineeringConfiguration
{
ConnectionString = connString,
ProjectPath = currentPath,
OutputPath = outputPath,
ProjectRootNamespace = "Your.Namespace",
OverwriteFiles = true,
UseFluentApiOnly = true,
ContextClassName = "YourDbContext",
TableSelectionSet = tableSelection
};
// Reverse engineer:
var generator = designTimeServices.GetService<ReverseEngineeringGenerator>();
var model = generator.GetMetadataModel(options);
var filePaths = generator.GenerateAsync(options).GetAwaiter().GetResult();
var errors = model.Scaffolding().EntityTypeErrors;
var contextFilePath = filePaths.ContextFile;
var entityTypeFilePaths = filePaths.EntityTypeFiles;
// Scaffold a migration
var scaffolder = designTimeServices.GetRequiredService<MigrationsScaffolder>();
var migration = scaffolder.ScaffoldMigration( "MyMigration", "MyApp.Data");
//File.WriteAllText(
// migration.MigrationId + migration.FileExtension,
// migration.MigrationCode);
//File.WriteAllText(
// migration.MigrationId + ".Designer" + migration.FileExtension,
// migration.MetadataCode);
//File.WriteAllText(migration.SnapshotName + migration.FileExtension,
// migration.SnapshotCode);
}
}
}
}
}
I had to added .AddSingleton<ISnapshotModelProcessor, SnapshotModelProcessor>()
to go it work
Dazinator.
Somebody tested this solution with Postgresql Database ???
FYI this has changed for 2.1, lots more services need to be injected now
I don't think so.
I use this
var designTimeServiceCollection = new ServiceCollection()
.AddEntityFrameworkDesignTimeServices()
//.AddSingleton<MigrationsScaffolder>()
.AddDbContextDesignTimeServices(db);
new SqlServerDesignTimeServices().ConfigureDesignTimeServices(designTimeServiceCollection);
return designTimeServiceCollection.BuildServiceProvider();
Before 2.1 I used this
return new ServiceCollection()
.AddSingleton(db.GetService<IHistoryRepository>())
.AddSingleton(db.GetService<IMigrationsIdGenerator>())
.AddSingleton(db.GetService<IMigrationsModelDiffer>())
.AddSingleton(db.Model)
.AddSingleton(db.GetService<ICurrentDbContext>())
.AddSingleton(db.GetService<IDatabaseProvider>())
.AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<ICSharpHelper, CSharpHelper>()
.AddSingleton<CSharpMigrationOperationGeneratorDependencies>()
.AddSingleton<ICSharpMigrationOperationGenerator, CSharpMigrationOperationGenerator>()
.AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<ICSharpSnapshotGenerator, CSharpSnapshotGenerator>()
.AddSingleton<ISnapshotModelProcessor, SnapshotModelProcessor>()
.AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<IMigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton<IMigrationsCodeGeneratorSelector, MigrationsCodeGeneratorSelector>()
.AddSingleton<IRelationalConnection, SqlServerConnection>()
.AddSingleton<IDbContextOptions, DbContextOptions<Sql>>()
.AddSingleton<IMigrator, Migrator>()
.AddSingleton<IDatabaseCreator, SqlServerDatabaseCreator>()
.AddSingleton<IOperationReporter>(reporter)
.AddSingleton<MigrationsScaffolderDependencies>()
.AddSingleton<RelationalConnectionDependencies>()
.AddSingleton<RelationalDatabaseCreatorDependencies>()
.AddSingleton<MigrationsScaffolder>()
.AddSingleton(db.GetService<IMigrationsAssembly>())
.AddSingleton(db.GetService<IMigrationsAssembly>())
.BuildServiceProvider();
@bricelam could you provide this code for Razor Class Library ( NetStandard.Library 2.0 ) or is it possible for Razor Class Library ?
@oOAhmedKingOo What's the question? You shouldn't have to do anything different for a Razor Class Library...
@bricelam my Admin area is a Razor Class Library and i want to generate migration files in that area. if still not clear for you i will explain more. best regards
Still not clear. What have you tried? What error are you seeing?
@bricelam
thanx for your response ,
simply if i copy your above code to generate migration files then i will get
( type or namespace MigrationsCodeGeneratorDependencies
could not be found )
this error is show for all below (.AddSingleton<>()
)
var designTimeServices = new ServiceCollection()
.AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<ICSharpHelper, CSharpHelper>()
.AddSingleton<CSharpMigrationOperationGeneratorDependencies>()
.AddSingleton<ICSharpMigrationOperationGenerator, CSharpMigrationOperationGenerator>()
.AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<ICSharpSnapshotGenerator, CSharpSnapshotGenerator>()
.AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<IMigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton<IOperationReporter>(reporter)
.AddSingleton<MigrationsScaffolderDependencies>()
.AddSingleton<MigrationsScaffolder>()
.BuildServiceProvider();
_I don't have problem with regular Asp.Net Core 2.1 App and works as expected_
Best Regards
For EF Core 2.1+, use Anderman's code:
var designTimeServiceCollection = new ServiceCollection()
.AddEntityFrameworkDesignTimeServices()
.AddDbContextDesignTimeServices(db);
new SqlServerDesignTimeServices().ConfigureDesignTimeServices(designTimeServiceCollection);
var designTimeServices = designTimeServiceCollection.BuildServiceProvider();
@bricelam thanx for your help,
sorry for bothering you !
but i want the generated files to be in different assembly ( project )
in this line var migration = scaffolder.ScaffoldMigration("MyMigration", "MyApp.Data");
i set MyApp.Data
to different namespace ( MyDatabaseModels.Data
which is not the current namespace (Admin project
)) , and always the generated files are placed in the Main Asp.net Core app (SMCore.Web
) not the MyDatabaseModels.Data
project
:)
if its not clear i will show you my solution tree ( which contains 3 project )
Just change the path you write the files to:
var scaffolder = designTimeServices.GetRequiredService<MigrationsScaffolder>();
var migration = scaffolder.ScaffoldMigration("MyMigration", "MyDatabaseModels.Data");
var projectDir = @"..\MyDatabaseModels\Data\";
File.WriteAllText(
projectDir + migration.MigrationId + migration.FileExtension,
migration.MigrationCode);
File.WriteAllText(
projectDir + migration.MigrationId + ".Designer" + migration.FileExtension,
migration.MetadataCode);
File.WriteAllText(
projectDir + migration.SnapshotName + migration.FileExtension,
migration.SnapshotCode);
sorry for late replay.
thank you for your help .
you saved my day 馃
Best Regard
Ahmed :)
one last question .
how do i check that the generated files are not empty , i mean the Method ( Up
and Down
) are not empty , means there is database changes.
another last question
this line await context.Database.MigrateAsync()
how do i know the database is already up to date
thank you man
Regards
:(
how do i check that the generated files are not empty
See this line in the database error page.
how do i know the database is already up to date
You can use this:
!context.Database.GetPendingMigrations().Any()
But, MigrateAsync()
will just no-op if it's up to date.
@bricelam thank you so much .
i appreciate your help 馃 .
:) and sorry for bothering you too much.
thanx again
Regards
@bricelam I was previoulsy using ReverseEngineeringGenerator
to scaffold a set of models for a set of tables:
var tableSelection = new Microsoft.EntityFrameworkCore.Scaffolding.TableSelectionSet(tables);
var options = new ReverseEngineeringConfiguration
{
ConnectionString = connString,
ProjectPath = currentPath,
OutputPath = outputPath,
ProjectRootNamespace = "Your.Namespace",
OverwriteFiles = true,
UseFluentApiOnly = true,
ContextClassName = "YourDbContext",
TableSelectionSet = tableSelection
};
// Reverse engineer:
var generator = designTimeServices.GetService<ReverseEngineeringGenerator>();
var model = generator.GetMetadataModel(options);
var filePaths = generator.GenerateAsync(options).GetAwaiter().GetResult();
var errors = model.Scaffolding().EntityTypeErrors;
var contextFilePath = filePaths.ContextFile;
var entityTypeFilePaths = filePaths.EntityTypeFiles;
However in 2.2 I see TableSelectionSet
is marked as obsolete, and I can't find ReverseEngineeringGenerator
class anywhere.. What's the new way?
@dazinator look at the source in EF Core Power Tools
We were using code below to modify entity names.
With EFCore 2.2, DbContextWriter can not be found. Is there an alternative way to accomplish this?
Thanks.
services.AddSingleton
...
public class CustomDbContextWriter : DbContextWriter
{
public CustomDbContextWriter(
ScaffoldingUtilities scaffoldingUtilities,
CSharpUtilities cSharpUtilities)
: base(scaffoldingUtilities, cSharpUtilities)
{ }
public override string WriteCode(ModelConfiguration modelConfiguration)
{
// There is no good way to override the DbSet naming, as it uses
// an internal StringBuilder. This means we can't override
// AddDbSetProperties without re-implementing the entire class.
// Therefore, we have to get the code and then do string manipulation
// to replace the DbSet property code
var code = base.WriteCode(modelConfiguration);
foreach (var entityConfig in modelConfiguration.EntityConfigurations)
{
var entityName = entityConfig.EntityType.Name;
//var setName = Inflector.Inflector.Pluralize(entityName) ?? entityName;
// We will always have original names as is
var setName = entityName;
code = code.Replace(
$"DbSet<{entityName}> {entityName}",
$"DbSet<{entityName}> {setName}");
}
return code;
}
@AlansButler Just replace the IPluralizer
service instead?
Thanks for the suggestion bricelam.
Could you point me to an example?
services.AddSingleton<IPluralizer, CustomPluralizer>();
public class CustomPluarlizer : IPluralizer
{
public override string Pluralize(string identifier)
=> Inflector.Inflector.Pluralize(identifier);
public override string Singularize(string identifier)
=> Inflector.Inflector.Singularize(identifier);
}
Thanks. But what we'd like to do is prepend the schema name. I'm not sure how to access the schema name from within Pluralize.
public string Pluralize(string name)
{
return schema +'_'+ name;
}
Well, that's nothing like the original code you showed... 馃檪 We don't have an easy hook for that. Start in CSharpModelGenerator to find a new hack. But also recognize that using internal API will likely break again in the future.
I got the code running with EF Core 2.2.4. The code generates the migration code, but how do I get it integrated (compiled) at runtime?
Ok, found a solution.
optionsBuilder.UseSqlServer("Data Source=localhost;Initial Catalog=Test;Integrated Security=true", m => m.MigrationsAssembly(assemblyName));
```
Brice,
Thanks for the suggestion.
I have this mostly working except for below:
When building the models this is what I want for example:
public partial class X837_DTP
{
public long DTPID { get; set; }
public virtual X837_Claim X837_Claim { get; set; }
}
Where the simple types like DTPID property names don't contain the schema,
but property types that are classes do. Where X837 is the schema and Claim is the table.
By Overridding RelationalScaffoldingModelFactory
protected override string GetPropertyName(DatabaseColumn column)
I am able to get all the properties to contain the schema. This is close as I can get:
public partial class X837_DTP
{
public long X837_DTPID { get; set; } //Don't want schema name here in property name.
public virtual X837_Claim X837_Claim { get; set; }
}
If I don't use override GetPropertyName I get below model with no schemas in name.
public partial class X837_DTP
{
public long DTPID { get; set; }
public virtual X837_Claim Claim { get; set; }
}
Now, I can override Overridding CSharpEntityTypeGenerator.WriteCode
and do a replace, but, this doesn't seem to change the name internally.
The DBContext still uses the tablename only. Like Claim.
example:
entity.HasOne(d => d.Claim) //HasOne Still using table not Schama_Table
...
I tried to override VisitColumn:
protected override PropertyBuilder VisitColumn(EntityTypeBuilder builder, DatabaseColumn column)
so I could tell the type of the property, but even the properties with class types come back as
simple types in column.storeType.
Any suggestions would be appreciated.
Code:
public class RelationalScaffoldingModelFactoryGaf : RelationalScaffoldingModelFactory
{
public static string fSchema = string.Empty;
public static Dictionary
public RelationalScaffoldingModelFactoryGaf(
IOperationReporter reporter,
ICandidateNamingService candidateNamingService,
IPluralizer pluralizer,
ICSharpUtilities cSharpUtilities,
IScaffoldingTypeMapper scaffoldingTypeMapper)
: base(
reporter,
candidateNamingService,
pluralizer,
cSharpUtilities,
scaffoldingTypeMapper)
{
rememberColumns.Clear();
}
/// <summary>
/// Changes the property names in the models.
/// public int x { get; set; }
/// This converts too many properties. For example in X837_DTP.
/// This we want for example: public virtual X837_Claim X837_Claim { get; set; }
/// This we do not: public DateTime? X837_DateTo { get; set; }
/// </summary>
/// <param name="column"></param>
/// <returns></returns>
protected override string GetPropertyName(DatabaseColumn column)
{
return column.Table.Schema + "_" + column.Name;
}
protected override PropertyBuilder VisitColumn(EntityTypeBuilder builder, DatabaseColumn column)
{
PropertyBuilder pb = base.VisitColumn(builder, column);
Console.WriteLine("RelationalScaffoldingModelFactoryGaf-VisitColumn");
Console.WriteLine(" columnName " + column.Name);
Console.WriteLine(" pb.MetaDataCLRType: " + pb.Metadata.ClrType);
Console.WriteLine(" Column.StoreType: " + column.StoreType);
//var typeScaffoldingInfo = GetTypeScaffoldingInfo(column);
//var clrType = typeScaffoldingInfo.ClrType;
//var propertyBuilder = builder.Property(clrType, GetPropertyName(column));
return pb;
}
}
public class CSharpEntityTypeGeneratorGaf : CSharpEntityTypeGenerator
{
public CSharpEntityTypeGeneratorGaf(
ICSharpHelper cSharpHelper)
: base(cSharpHelper)
{ }
public override string WriteCode(IEntityType entityType, string @namespace, bool useDataAnnotations)
{
string code = base.WriteCode(entityType, @namespace, useDataAnnotations);
string[] modelLines = code.Split(Environment.NewLine);
foreach (string line in modelLines)
{
List<string> modifiedModelLines = new List<string>();
//Some code here to get the schemaName and tableName from the current model line.
modifLine = line.Replace(schemaName + "_" + tableName + " " + tableName,
schemaName + "_" + tableName + " " + schemaName +"_"+ tableName);
modifiedModelLines.Add(modifLine);
}
code = string.Join(Environment.NewLine, modifiedModelLines.ToArray());
return code;
}
}
Is there anything I need to change to do this correctly in 3.0?
@kierenj I don't recall any major changes in this area. There is one breaking change to the way you have to reference EFCore.Design. Other than that, I think it will just work.
When upgrading to 3.0 I need to add the compile asset to Microsoft.EntityFrameworkCore.Design 3.0
package. Is there a reason for this?
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive;compile</IncludeAssets>
</PackageReference>
Is there a reason for this?
Yes, cited in the breaking change note
@bricelam I'm very happy to see your answer, I've been troubled by a related problem for the last few days. I am having the same problem as @DarkGraySun , your answer doesn't have a section on how to compile and integrate the generated code at runtime, but roslyn doesn't work for me. I have two ideas:
I would like to have some sample code, thank you very much.
Most helpful comment
dotnet ef migrations apply
dotnet ef migrations add