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

RevEng: Use ScaffoldingTypeMapper #8626

Merged
merged 1 commit into from
May 31, 2017

Conversation

smitpatel
Copy link
Contributor

No description provided.

@@ -9,11 +9,11 @@ namespace Microsoft.EntityFrameworkCore.Scaffolding
{
public class TypeScaffoldingInfo
{
public TypeScaffoldingInfo([NotNull] Type clrType, bool inferred, bool? scaffoldUnicode, int? scaffoldMaxLength)
public TypeScaffoldingInfo([NotNull] Type clrType, bool isInferred, bool? scaffoldUnicode, int? scaffoldMaxLength)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is prefixes are discouraged on parameters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git inability to detect file move & broken resharper. I changed it to something else and forgot original name. I will remove is in parameter.


var dataType = underlyingDataType ?? (columnModel.DataType + (columnModel.MaxLength.HasValue ? $"({columnModel.MaxLength.Value})" : ""));

var typeScaffoldingInfo = _scaffoldingTypeMapper.FindMapping(dataType, keyOrIndex: false, rowVersion: columnModel.DataType == "timestamp");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this to be correct we have to know whether the property is part of a key.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At present, ReverseEngineer does not flow enough information to put here directly. This is not correct. (same goes for keyOrIndex being passed as false always). I can set this one as false too for now. We can decide later how to flow the information about key/index/rowversion from database model & make this call accurate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with postponing it. Please don't close the issue until it is fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to false.

@@ -457,8 +450,7 @@ namespace E2ETest.Namespace

modelBuilder.Entity<OneToOneSeparateFkprincipal>(entity =>
{
entity.HasKey(e => new { e.OneToOneSeparateFkprincipalId1, e.OneToOneSeparateFkprincipalId2 })
.HasName("PK_OneToOneSeparateFKPrincipal");
entity.HasKey(e => new { e.OneToOneSeparateFkprincipalId1, e.OneToOneSeparateFkprincipalId2 });

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has the HasName() call disappeared? Where is the name being stored now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is same as default name. It was a bug to scaffold them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, but I would like to have at least one test where we do generate it because it is different from the default. Can we arrange that somehow (e.g. rename a PK or two in the E2E.sql file)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment #8594 (comment)

/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public class DbContextScaffolder
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the name of this class confusing as, if I understand correctly, it is responsible for scaffolding all the code, not just the DbContext?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am happy with better name if there is any. This class is an orchestrator to our scaffolding.
I have aligned the name with our command names. While this class is scaffolding all the code not just the DbContext, same way all the code is scaffolded even though we call the command Scaffold-DbContext

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about ScaffoldingOrchestrator or ScaffoldingController then? (I didn't like Scaffold-DbContext for the same reason).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bricelam @ajcvickers - Name suggestions? I am fine with any name. as long as it conveys meaning.
Afaik, migrations have MigrationScaffolder.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My two cents:

  • Consider the word "model" to convey both the DbContext and the types
  • Not sure this is comparable to MigrationsScaffolder. At first glance it looks like that one is doing more of the work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MigrationScaffolder adds a new migration. (actual runner of Add-Migration) is it something different @bricelam ?

ModelScaffolder sounds good to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ModelScaffolder sounds good.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add-Migration code basically looks like this:

// Generate the code
var migraton = migrationsScaffolder.ScaffoldMigration(name, namespace)

// Write the files
return migrationsScaffolder.Save(migration, directory)

+1 for ModelScaffolder here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

</data>
<data name="RootNamespaceRequired" xml:space="preserve">
<value>Root namespace of the project is required to generate code.</value>
</data>
<data name="MigrationsAssemblyMismatch" xml:space="preserve">
<value>Your target project '{assembly}' doesn't match your migrations assembly '{migrationsAssembly}'. Either change your target project or change your migrations assembly.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re the deletions above, what error message do users see if they fail to set these things (or e.g. set them to non-existent paths) now? We spent a bit of time making sure the error messages were useful...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were removed in other PR which was already approved.
As explanation,
In current codebase, above messages are thrown from ReverseEngineerConfiguration.CheckValidity() (I am talking about namespace/project dir/connectionstring).

We create RevEngConfig object in this class https://github.com/aspnet/EntityFramework/blob/dev/src/EFCore.Design/Design/Internal/DatabaseOperations.cs

Based on the annotations in the class, none of those properties will ever be null. If they will be then we will throw long before we reach at validation check.

The only way to get those exception is user passing invalid Config object in ReverseEngineerGenerator.GenerateAsync function. But we have removed the config object and added those properties as parameter. So for our normal UX from PMC/cmd, the values will be proper. The only way exceptions will arise is when user explicitly pass null to parameters marked as not-null and I believe it is fine to just throw argument null exception in that case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Have a check what happens if you fail to enter/enter bad info at the command-line. If the error you get back is sufficient to help the user figure out what's wrong and fix it then so be it. But if they just get some generic error that's along the lines of "something went wrong" then we really tried to be more useful than that with the error messages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked codebase.
RootNameSpace -> we take from command line if passed else it will be same as AssemblyFileName of the assembly
ProjectDirectory -> Use command line arg or take the current directory
So we will never have incorrect values for above unless passed wrong by user.
@bricelam can tell what would we throw because it is not specific to scaffolding but rather any EF command.

ConnectionString is required arg in scaffold command, if user don't provider it then we show error as it is required arg missing. which is self-explanatory.

@smitpatel smitpatel closed this May 30, 2017
@smitpatel smitpatel changed the base branch from feature/removerelationaldesign to feature/HardToRebase May 30, 2017 23:12
@smitpatel smitpatel reopened this May 30, 2017
@smitpatel
Copy link
Contributor Author

Updated PR after going through rebase hell. The real commit is this PR is much smaller. (thanks to nice design of ScaffoldingTypeMapper by @ajcvickers ).

It is not accurate in terms of scaffolded code yet. But this defines a layer what ScaffoldingTypeMapper is supposed to do & how it will work.

  • Provider implements IScaffoldingHelper.GetTypeScaffoldingInfo function. It takes ColumnModel so provider should have access to all the metadata about column before making judgement on what to scaffold about type mapping of the column.
  • The function returns TypeScaffoldingInfo which has difference facets (3 for now, explained below) which associate with fluent api calls to scaffold.

TypeScaffoldingInfo contains

  • IsInferred - indicates if the type mapping can be inferred i.e. no HasColumnType call needed.
  • ScaffoldUnicode - indicates that it has non-default Unicode and we need to call IsUnicode
  • ScaffoldMaxLengh - indicates that it has non-default length and we need to call HasMaxLength

@smitpatel smitpatel force-pushed the scaffoldingtypemapper branch 3 times, most recently from eb3d7ab to 44dc5f4 Compare May 30, 2017 23:42
@@ -23,6 +23,7 @@ public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollec
=> serviceCollection
.AddSingleton<IRelationalTypeMapper, SqliteTypeMapper>()
.AddSingleton<IDatabaseModelFactory, SqliteDatabaseModelFactory>()
.AddSingleton<IScaffoldingHelper, SqliteScaffoldingHelper>();
.AddSingleton<IScaffoldingHelper, SqliteScaffoldingHelper>()
.AddSingleton<ScaffoldingTypeMapper>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should only need to replace "core" design-time services in the provider, not re-add them.

@smitpatel smitpatel merged commit 2b689bb into feature/HardToRebase May 31, 2017
@smitpatel smitpatel deleted the scaffoldingtypemapper branch May 31, 2017 21:06
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

Successfully merging this pull request may close these issues.

6 participants