Skip to content

Instantly share code, notes, and snippets.

@NickCraver
Last active August 22, 2018 19:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NickCraver/b51144cfc6bff5f5d9a378f940bfbe3e to your computer and use it in GitHub Desktop.
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
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&lt;IModelSource, OptInRelationalModelSource&gt;();
/// }
/// </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