Last active
August 22, 2018 19:31
-
-
Save NickCraver/b51144cfc6bff5f5d9a378f940bfbe3e to your computer and use it in GitHub Desktop.
OptInRelationalModelSource.cs - Making EF Core opt-in instead of opt-out for properties
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel.DataAnnotations; | |
using System.ComponentModel.DataAnnotations.Schema; | |
using System.Linq; | |
using System.Reflection; | |
using Microsoft.EntityFrameworkCore.Infrastructure; | |
using Microsoft.EntityFrameworkCore.Infrastructure.Internal; | |
using Microsoft.EntityFrameworkCore.Metadata.Conventions; | |
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; | |
using Microsoft.EntityFrameworkCore.Metadata.Internal; | |
namespace MyNamespace | |
{ | |
/// <summary> | |
/// <para>This data source is a conservative opt-in approach to model building.</para> | |
/// | |
/// <para>It only discovers properties marked with one or more of:</para> | |
/// <para>- <see cref="KeyAttribute"/></para> | |
/// <para>- <see cref="ColumnAttribute"/></para> | |
/// <para>- <see cref="ForeignKeyAttribute"/></para> | |
/// <para>- <see cref="InversePropertyAttribute"/></para> | |
/// <para>Properties not attributed are ignored.</para> | |
/// <code> | |
/// protected override void OnConfiguring(DbContextOptionsBuilder builder) | |
/// { | |
/// builder.ReplaceService<IModelSource, OptInRelationalModelSource>(); | |
/// } | |
/// </code> | |
/// </summary> | |
public class OptInRelationalModelSource : RelationalModelSource | |
{ | |
/// <summary> | |
/// This creates a new <see cref="OptInRelationalModelSource"/>. | |
/// </summary> | |
/// <param name="dependencies">The <see cref="ModelSourceDependencies"/> to use.</param> | |
public OptInRelationalModelSource(ModelSourceDependencies dependencies) : base(dependencies) { } | |
protected override ConventionSet CreateConventionSet(IConventionSetBuilder conventionSetBuilder) | |
{ | |
var sets = base.CreateConventionSet(conventionSetBuilder); | |
sets.EntityTypeAddedConventions.Add(new NotOptedInConvention()); | |
return sets; | |
} | |
} | |
public class NotOptedInConvention : IEntityTypeAddedConvention | |
{ | |
private static readonly HashSet<Type> OptedInAttributeTypes = new HashSet<Type>() | |
{ | |
typeof(KeyAttribute), | |
typeof(ColumnAttribute), | |
typeof(ForeignKeyAttribute), | |
typeof(InversePropertyAttribute) | |
}; | |
public InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder) | |
{ | |
var entityType = entityTypeBuilder.Metadata; | |
if (!entityType.HasClrType()) | |
{ | |
return entityTypeBuilder; | |
} | |
foreach (var member in entityType.GetRuntimeProperties().Values.Cast<MemberInfo>().Concat(entityType.GetRuntimeFields().Values)) | |
{ | |
bool optedIn = false; | |
foreach (var attribute in member.GetCustomAttributes()) | |
{ | |
if (OptedInAttributeTypes.Contains(attribute.GetType())) | |
{ | |
optedIn = true; | |
break; | |
} | |
} | |
if (!optedIn) | |
{ | |
entityTypeBuilder.Ignore(member.Name, ConfigurationSource.DataAnnotation); | |
} | |
} | |
return entityTypeBuilder; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment