Skip to content

feat(Migrations): SQL Server Filterable indexes. - #6866

Merged
AndriySvyryd merged 1 commit into
dotnet:feature/1.2.0from
laskoviymishka:feat/FiltrableIndexes
Oct 29, 2016
Merged

feat(Migrations): SQL Server Filterable indexes.#6866
AndriySvyryd merged 1 commit into
dotnet:feature/1.2.0from
laskoviymishka:feat/FiltrableIndexes

Conversation

@laskoviymishka

Copy link
Copy Markdown
Contributor

Please check if the PR fulfills these requirements

  • The code builds and tests pass (verified by our automated build checks)
  • Commit messages follow this format
 Implement ability to add filter to index
- Extension for index builder
- Index creation operation
- Corresponding SQL generator for migration
- Corresponding SQL reverse engeniering code for scaffolding
- Corresponding C# reverse engeniering code for scaffolding
Fixes #5817

Please review the guidelines for CONTRIBUTING.md for more details.
Fixed#5817

@AndriySvyrydAndriySvyryd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In addition to the below comments please rebase on feature/1.2.0 and run build.cmd

public virtual string Name { get; [param: NotNull] set; }
public virtual ICollection<IndexColumnModel> IndexColumns { get; [param: NotNull] set; } = new List<IndexColumnModel>();
public virtual bool IsUnique { get; [param: NotNull] set; }
public virtual string WhereClauses { get; set; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add [param: CanBeNull]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Any reason this is plural? Am not familiar with SQL Server here but in PostgreSQL it's just a standard single WHERE clause

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

This should be renamed in Filter. Just forgot this entry.

[CanBeNull] string schema = null,
bool unique = false)
bool unique = false,
string filter = null)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add [CanBeNull]

[CanBeNull] string schema = null,
bool unique = false)
bool unique = false,
string filter = null)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add [CanBeNull]


/// <summary>
/// Gets the properties that this index is defined on.
/// </summary>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Filtered indexes are a SQL Server feature, so it should be stored as an annotation using extension methods. See how .IsClustered is implemented.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

On a second though, other relational providers support it so it can go in .Relational

Sql);
}

public override void CreateIndexOperation_with_whrere_clauses()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Typo

IsUnique = false
});

[Fact]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Typo

Name = "IX_People_Name",
Table = "People",
Columns = new[] { "Name" },
IsUnique = false,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is SQLServer-specific SQL in a provider-independent test. However, it's not terribly important as this is only a unit test (i.e. the SQL never gets sent/evaluated), so no real harm.

Sql);
}

[Fact]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't this test simply override CreateIndexOperation_with_where_clauses, defined in MigrationSqlGeneratorTestBase.cs?

Sql);
}

[Fact]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should probably be defined in MigrationSqlGeneratorTestBase.cs and overridden here (useful for other providers)

@laskoviymishka
laskoviymishka changed the base branch from dev to feature/1.2.0October 27, 2016 12:26
@laskoviymishka

Copy link
Copy Markdown
ContributorAuthor

@AndriySvyryd please review latest check-in. I moved it into relational annotation but not completely sure that this proper way to use realational attributes (i tried to mimic HasName behaivior).

@AndriySvyrydAndriySvyryd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You also need to add a method to RelationalIndexBuilderAnnotations so filters can be set by conventions, as well as in SqlServerIndexBuilderAnnotations that would allow to set a filter just for SQL Server in case multiple providers are used with the same model.

/// <summary>
/// Gets a value indicating whre clauses to specific index.
/// </summary>
string Filter { get; set; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The properties on this interface should be read-only.
Also the fix typos in the comment or just remove it.

&& s.Properties.Select(diffContext.FindTarget).SequenceEqual(t.Properties),
// ReSharper disable once ImplicitlyCapturedClosure
(s, t) => s.IsUnique == t.IsUnique
&& s.Relational().Filter == t.Relational().Filter

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You need to use Annotations.For(t).Filter in all of the migrations code, this allows it to read the provider-specific value

Table = Annotations.For(index.DeclaringEntityType).TableName,
Columns = index.Properties.Select(p => Annotations.For(p).ColumnName).ToArray()
Columns = index.Properties.Select(p => Annotations.For(p).ColumnName).ToArray(),
Filter = index.Relational().Filter

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use Annotations.For

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You missed this one :)

@laskoviymishka

Copy link
Copy Markdown
ContributorAuthor

@AndriySvyryd done. Please review

<ProjectReference Include="..\..\src\Microsoft.EntityFrameworkCore.Relational\Microsoft.EntityFrameworkCore.Relational.csproj">
<Project>{6A25DF99-2615-46D8-9532-821764647EE1}</Project>
<Name>Microsoft.EntityFrameworkCore.Relational</Name>
</ProjectReference>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This project shouldn't reference .Relational



[Fact]
public void Can_write_index_builder_extension_with_where_clauses()

@AndriySvyrydAndriySvyrydOct 27, 2016

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Move this to RelationalBuilderExtensionsTest and add a similar test to RelationalMetadataExtensionsTest

@divega

Copy link
Copy Markdown
Contributor

General question about this PR: currently by default we add the filter WHERE x IS NOT NULL for unique indexes in SQL Server (not sure what we do for SQLite). Can that become a convention that takes advantage of this feature?

@AndriySvyryd

Copy link
Copy Markdown
Member

@divega Sure, I can do it after this is merged

Annotations.For(t).Name,
StringComparison.OrdinalIgnoreCase)
&& s.IsUnique == t.IsUnique
&& Annotations.For(s).Filter == Annotations.For(t).Filter

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

@AndriySvyryd May be HasDifferences can handle this comparing? It looks like i can remove this lines

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

HasDifferences diffs migrations annotations, while they have the same API they are different from model annotations, the added conditions are still needed.

@laskoviymishka

Copy link
Copy Markdown
ContributorAuthor

@AndriySvyryd done, please review

@AndriySvyrydAndriySvyryd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Almost there.
After making the change below rebase again on the latest feature/1.2.0 and squash the commits

Annotations.For(t).Name,
StringComparison.OrdinalIgnoreCase)
&& s.IsUnique == t.IsUnique
&& Annotations.For(s).Filter == Annotations.For(t).Filter

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

HasDifferences diffs migrations annotations, while they have the same API they are different from model annotations, the added conditions are still needed.

/// <summary>
/// Gets a value indicating whre clauses to specific index.
/// </summary>
public string Filter

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This needs to be virtual. Also correct spelling in the comment or just remove it.


if (Index.Relational().Filter != null)
{
lines.Add("." + nameof(RelationalIndexBuilderExtensions.HasFilter) + "(\"" + Index.Relational().Filter + "\")");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

String interpolation?

/// </summary>
public new virtual bool Name([CanBeNull] string value) => SetName(value);

/// <summary>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should be HasFilter?

protected abstract IMigrationsSqlGenerator SqlGenerator { get; }

protected virtual string Sql { get; set; }
[Fact]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Missing empty line

@laskoviymishka

Copy link
Copy Markdown
ContributorAuthor

@AndriySvyryd@roji done, please review.

@roji

roji commented Oct 29, 2016

Copy link
Copy Markdown
Member

LGTM

@AndriySvyryd
AndriySvyryd merged commit 2e0498a into dotnet:feature/1.2.0Oct 29, 2016
@AndriySvyryd

Copy link
Copy Markdown
Member

Thank you for your contribution.

Sign up for freeto 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.

5 participants

@laskoviymishka@divega@AndriySvyryd@roji@dnfclas