Skip to content

Commit

Permalink
Add validation for unique column names
Browse files Browse the repository at this point in the history
smitpatel committed Oct 8, 2015
1 parent f3c7726 commit e6e9449
Showing 21 changed files with 184 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@
</Compile>
<Compile Include="Infrastructure\SqlServerDbContextOptionsBuilder.cs" />
<Compile Include="Internal\SqlServerModelSource.cs" />
<Compile Include="Internal\SqlServerModelValidator.cs" />
<Compile Include="Internal\SqlServerOptionsExtension.cs" />
<Compile Include="Metadata\Conventions\Internal\SqlServerConventionSetBuilder.cs" />
<Compile Include="Metadata\Conventions\Internal\SqlServerValueGenerationStrategyConvention.cs" />
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ public static EntityFrameworkServicesBuilder AddSqlServer([NotNull] this EntityF
.AddSingleton<SqlServerModelSource>()
.AddSingleton<SqlServerAnnotationProvider>()
.AddSingleton<SqlServerMigrationsAnnotationProvider>()
.AddScoped<SqlServerModelValidator>()
.AddScoped<ISqlServerUpdateSqlGenerator, SqlServerUpdateSqlGenerator>()
.AddScoped<ISqlServerSequenceValueGeneratorFactory, SqlServerSequenceValueGeneratorFactory>()
.AddScoped<SqlServerModificationCommandBatchFactory>()
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using JetBrains.Annotations;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Extensions.Logging;

namespace Microsoft.Data.Entity.Internal
{
public class SqlServerModelValidator : RelationalModelValidator
{
public SqlServerModelValidator([NotNull] ILogger<RelationalModelValidator> loggerFactory, [NotNull] IRelationalAnnotationProvider relationalExtensions)
: base(loggerFactory, relationalExtensions)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -53,5 +53,6 @@ public SqlServerDatabaseProviderServices([NotNull] IServiceProvider services)
public override ISqlQueryGeneratorFactory SqlQueryGeneratorFactory => GetService<SqlServerQuerySqlGeneratorFactory>();
public override IEntityQueryModelVisitorFactory EntityQueryModelVisitorFactory => GetService<SqlServerQueryModelVisitorFactory>();
public override ICompiledQueryCacheKeyGenerator CompiledQueryCacheKeyGenerator => GetService<SqlServerCompiledQueryCacheKeyGenerator>();
public override IModelValidator ModelValidator => GetService<SqlServerModelValidator>();
}
}
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ public override void Validate(IModel model)
base.Validate(model);

EnsureDistinctTableNames(model);
EnsureDistinctColumnNames(model);
ValidateInheritanceMapping(model);
}

@@ -46,6 +47,22 @@ protected virtual void EnsureDistinctTableNames([NotNull] IModel model)
}
}

protected virtual void EnsureDistinctColumnNames([NotNull] IModel model)
{
foreach (var entityType in model.EntityTypes)
{
var columns = new HashSet<string>();
foreach (var property in entityType.GetProperties())
{
var name = _relationalExtensions.For(property).ColumnName;
if (!columns.Add(name))
{
ShowError(RelationalStrings.DuplicateColumnName(name, entityType.Name, property.Name));
}
}
}
}

protected virtual void ValidateInheritanceMapping([NotNull] IModel model)
{
var roots = new HashSet<IEntityType>();

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -295,4 +295,7 @@
<data name="RelationalLoggerExecutingCommand" xml:space="preserve">
<value>Executing DbCommand: [Parameters=[{parameters}], CommandType='{commandType}', CommandTimeout='{commandTimeout}']{newLine}{newLine}{commandText}{newLine}</value>
</data>
<data name="DuplicateColumnName" xml:space="preserve">
<value>Cannot use column name '{columnName}' in entity '{entityType}' for property '{property}' since it is being used for another property.</value>
</data>
</root>
1 change: 1 addition & 0 deletions src/EntityFramework.Sqlite/EntityFramework.Sqlite.csproj
Original file line number Diff line number Diff line change
@@ -59,6 +59,7 @@
<Link>Extensions\SharedTypeExtensions.cs</Link>
</Compile>
<Compile Include="Infrastructure\Internal\SqliteModelSource.cs" />
<Compile Include="Infrastructure\Internal\SqliteModelValidator.cs" />
<Compile Include="Infrastructure\Internal\SqliteOptionsExtension.cs" />
<Compile Include="Metadata\Builders\SqliteEntityTypeBuilderExtensions.cs" />
<Compile Include="Metadata\Builders\SqliteReferenceCollectionBuilderExtensions.cs" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using JetBrains.Annotations;
using Microsoft.Data.Entity.Internal;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Extensions.Logging;

namespace Microsoft.Data.Entity.Infrastructure.Internal
{
public class SqliteModelValidator : RelationalModelValidator
{
public SqliteModelValidator([NotNull] ILogger<RelationalModelValidator> loggerFactory, [NotNull] IRelationalAnnotationProvider relationalExtensions)
: base(loggerFactory, relationalExtensions)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ public static EntityFrameworkServicesBuilder AddSqlite([NotNull] this EntityFram
.AddSingleton<SqliteModelSource>()
.AddSingleton<SqliteMigrationsAnnotationProvider>()
.AddSingleton<SqliteConventionSetBuilder>()
.AddScoped<SqliteModelValidator>()
.AddScoped<SqliteUpdateSqlGenerator>()
.AddScoped<SqliteModificationCommandBatchFactory>()
.AddScoped<SqliteDatabaseProviderServices>()
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
using JetBrains.Annotations;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Infrastructure.Internal;
using Microsoft.Data.Entity.Internal;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Metadata.Conventions.Internal;
using Microsoft.Data.Entity.Metadata.Internal;
@@ -47,5 +48,6 @@ public SqliteDatabaseProviderServices([NotNull] IServiceProvider services)
public override IMemberTranslator CompositeMemberTranslator => GetService<SqliteCompositeMemberTranslator>();
public override IMigrationsAnnotationProvider MigrationsAnnotationProvider => GetService<SqliteMigrationsAnnotationProvider>();
public override ISqlQueryGeneratorFactory SqlQueryGeneratorFactory => GetService<SqliteQuerySqlGeneratorFactory>();
public override IModelValidator ModelValidator => GetService<SqliteModelValidator>();
}
}
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@
<Compile Include="Migrations\SqlServerModelDifferTest.cs" />
<Compile Include="SqlServerDatabaseCreatorTest.cs" />
<Compile Include="SqlServerDbContextOptionsExtensionsTest.cs" />
<Compile Include="SqlServerModelValidatorTest.cs" />
<Compile Include="SqlServerOptionsExtensionTest.cs" />
<Compile Include="SqlServerConnectionTest.cs" />
<Compile Include="SqlServerDatabaseSourceTest.cs" />
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ public override void Services_wire_up_correctly()
VerifyScoped<SqlServerHistoryRepository>();
VerifyScoped<SqlServerCompositeMethodCallTranslator>();
VerifyScoped<SqlServerCompositeMemberTranslator>();
VerifyScoped<SqlServerModelValidator>();
}

public SqlServerEntityFrameworkServicesBuilderExtensionsTest()
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.Data.Entity.Internal;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Metadata.Conventions.Internal;
using Microsoft.Data.Entity.Tests;
using Microsoft.Data.Entity.Tests.TestUtilities;
using Microsoft.Extensions.Logging;

namespace Microsoft.Data.Entity.SqlServer.Tests
{
public class SqlServerModelValidatorTest : RelationalModelValidatorTest
{
public override void Detects_duplicate_column_names()
{
var modelBuilder = new ModelBuilder(new CoreConventionSetBuilder().CreateConventionSet());
modelBuilder.Entity<Product>();
modelBuilder.Entity<Product>().Property(b => b.Name).ForSqlServerHasColumnName("Id");

VerifyError(RelationalStrings.DuplicateColumnName("Id", typeof(Product).FullName, "Name"), modelBuilder.Model);
}

private class Product
{
public int Id { get; set; }
public string Name { get; set; }
}

protected override ModelValidator CreateModelValidator()
=> new SqlServerModelValidator(
new Logger<SqlServerModelValidator>(
new ListLoggerFactory(Log, l => l == typeof(SqlServerModelValidator).FullName)),
new TestSqlServerAnnotationProvider());
}

public class TestSqlServerAnnotationProvider : TestAnnotationProvider
{
public override IRelationalPropertyAnnotations For(IProperty property) => new SqlServerPropertyAnnotations(property);
}
}
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.Data.Entity.Internal;
using Microsoft.Data.Entity.Metadata.Conventions;
using Microsoft.Data.Entity.Metadata.Conventions.Internal;
using Microsoft.Data.Entity.Tests.Infrastructure;
using Microsoft.Data.Entity.Tests.TestUtilities;
using Microsoft.Extensions.Logging;
@@ -74,6 +76,16 @@ public virtual void Does_not_detect_duplicate_table_names_for_inherited_entities
CreateModelValidator().Validate(model);
}

[Fact]
public virtual void Detects_duplicate_column_names()
{
var modelBuilder = new ModelBuilder(new CoreConventionSetBuilder().CreateConventionSet());
modelBuilder.Entity<Product>();
modelBuilder.Entity<Product>().Property(b => b.Name).HasColumnName("Id");

VerifyError(RelationalStrings.DuplicateColumnName("Id", typeof(Product).FullName, "Name"), modelBuilder.Model);
}

[Fact]
public virtual void Passes_for_non_hierarchical_model()
{
@@ -152,6 +164,12 @@ protected class Generic<T> : Abstract
{
}

private class Product
{
public int Id { get; set; }
public string Name { get; set; }
}

protected override ModelValidator CreateModelValidator()
=> new RelationalModelValidator(
new Logger<RelationalModelValidator>(
12 changes: 6 additions & 6 deletions test/EntityFramework.Relational.Tests/TestAnnotationProvider.cs
Original file line number Diff line number Diff line change
@@ -7,11 +7,11 @@ namespace Microsoft.Data.Entity.Tests
{
public class TestAnnotationProvider : IRelationalAnnotationProvider
{
public IRelationalEntityTypeAnnotations For(IEntityType entityType) => entityType.TestProvider();
public IRelationalForeignKeyAnnotations For(IForeignKey foreignKey) => foreignKey.TestProvider();
public IRelationalIndexAnnotations For(IIndex index) => index.TestProvider();
public IRelationalKeyAnnotations For(IKey key) => key.TestProvider();
public IRelationalModelAnnotations For(IModel model) => model.TestProvider();
public IRelationalPropertyAnnotations For(IProperty property) => property.TestProvider();
public virtual IRelationalEntityTypeAnnotations For(IEntityType entityType) => entityType.TestProvider();
public virtual IRelationalForeignKeyAnnotations For(IForeignKey foreignKey) => foreignKey.TestProvider();
public virtual IRelationalIndexAnnotations For(IIndex index) => index.TestProvider();
public virtual IRelationalKeyAnnotations For(IKey key) => key.TestProvider();
public virtual IRelationalModelAnnotations For(IModel model) => model.TestProvider();
public virtual IRelationalPropertyAnnotations For(IProperty property) => property.TestProvider();
}
}
2 changes: 1 addition & 1 deletion test/EntityFramework.Sqlite.Tests/ApiConsistencyTest.cs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
using System.Reflection;
using Microsoft.Data.Entity.Storage.Internal;

namespace Microsoft.Data.Entity.Sqlite
namespace Microsoft.Data.Entity.Sqlite.Tests
{
public class ApiConsistencyTest : ApiConsistencyTestBase
{
Original file line number Diff line number Diff line change
@@ -44,6 +44,7 @@
<Compile Include="Migrations\SqliteMigrationSqlGeneratorTest.cs" />
<Compile Include="Migrations\SqliteHistoryRepositoryTest.cs" />
<Compile Include="Infrastructure\SqliteEntityFrameworkServicesBuilderExtensionsTest.cs" />
<Compile Include="SqliteModelValidatorTest.cs" />
<Compile Include="Storage\SqliteSqlGeneratorTest.cs" />
<Compile Include="Update\SqliteUpdateSqlGeneratorTest.cs" />
<Compile Include="Storage\SqliteTypeMappingTest.cs" />
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ public override void Services_wire_up_correctly()
VerifyScoped<SqliteHistoryRepository>();
VerifyScoped<SqliteCompositeMethodCallTranslator>();
VerifyScoped<SqliteCompositeMemberTranslator>();
VerifyScoped<SqliteModelValidator>();
}

public SqliteEntityFrameworkServicesBuilderExtensionsTest()
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
using Microsoft.Data.Sqlite;
using Xunit;

namespace Microsoft.Data.Entity
namespace Microsoft.Data.Entity.Sqlite.Tests
{
public class SqliteDbContextOptionsBuilderExtensionsTest
{
43 changes: 43 additions & 0 deletions test/EntityFramework.Sqlite.Tests/SqliteModelValidatorTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.Data.Entity.Infrastructure.Internal;
using Microsoft.Data.Entity.Internal;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Metadata.Conventions.Internal;
using Microsoft.Data.Entity.Metadata.Internal;
using Microsoft.Data.Entity.Tests;
using Microsoft.Data.Entity.Tests.TestUtilities;
using Microsoft.Extensions.Logging;

namespace Microsoft.Data.Entity.Sqlite.Tests
{
public class SqliteModelValidatorTest : RelationalModelValidatorTest
{
public override void Detects_duplicate_column_names()
{
var modelBuilder = new ModelBuilder(new CoreConventionSetBuilder().CreateConventionSet());
modelBuilder.Entity<Product>();
modelBuilder.Entity<Product>().Property(b => b.Name).ForSqliteHasColumnName("Id");

VerifyError(RelationalStrings.DuplicateColumnName("Id", typeof(Product).FullName, "Name"), modelBuilder.Model);
}

private class Product
{
public int Id { get; set; }
public string Name { get; set; }
}

protected override ModelValidator CreateModelValidator()
=> new SqliteModelValidator(
new Logger<SqliteModelValidator>(
new ListLoggerFactory(Log, l => l == typeof(SqliteModelValidator).FullName)),
new TestSqliteAnnotationProvider());
}

public class TestSqliteAnnotationProvider : TestAnnotationProvider
{
public override IRelationalPropertyAnnotations For(IProperty property) => new RelationalPropertyAnnotations(property, SqliteAnnotationNames.Prefix);
}
}

0 comments on commit e6e9449

Please sign in to comment.