Skip to content

Instantly share code, notes, and snippets.

@hidegh
Created March 7, 2017 08:57
Show Gist options
  • Save hidegh/36d92380c720804dee043fde8a863ecb to your computer and use it in GitHub Desktop.
Save hidegh/36d92380c720804dee043fde8a863ecb to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Reflection;
namespace EF6x_code_first_persistence.Mappings.Core
{
/// <summary>
/// Allows us to register conventions with a single call and also to pass in custom NamespaceToDbSchemaConvertor in ctor if given custom IConvention supports it.
/// </summary>
public static class MappingExtensions
{
public delegate bool MappingFilterDelegate(Type mappingClass, Type entity);
public static void RegisterMappingsFrom<TAssembly>(this DbModelBuilder modelBuilder, MappingFilterDelegate filter = null)
{
var mappingsAssembly = typeof(TAssembly);
var entityTypeConfigurationTypesToRegister = mappingsAssembly.Assembly
.GetTypes()
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>))
.Where(type =>
{
var entity = type.BaseType.GetGenericArguments()[0];
var mappingClass = type;
if (filter == null)
return true;
return filter(mappingClass, entity);
})
.ToList();
foreach (var entityTypeconfigurationType in entityTypeConfigurationTypesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(entityTypeconfigurationType);
modelBuilder.Configurations.Add(configurationInstance);
}
}
public static void RegisterMappingsFromInsideAssemblyAndNamespaceOf<TMapMarker>(this DbModelBuilder modelBuilder, MappingFilterDelegate filter = null)
{
var mappingMarker = typeof(TMapMarker);
var mappingMarkerNS = mappingMarker.Namespace;
RegisterMappingsFrom<TMapMarker>(modelBuilder, (mappingClass, entity) =>
{
// make sure we're inside the desired namespace
if (!mappingClass.FullName.StartsWith(mappingMarkerNS + "."))
return false;
// do additional custom filter logic
if (filter == null)
return true;
return filter(mappingClass, entity);
});
}
public static void IgnoreNonMappedProperties<T>(this EntityTypeConfiguration<T> mapper) where T : class
{
var entityType = typeof(T);
var entityProperties = entityType
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(c => c.GetMethod != null && c.GetMethod.IsPrivate == false)
.ToArray();
var mapperType = mapper.GetType();
var configuration = mapperType
.GetProperty("Configuration", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(mapper);
var configurationType = configuration.GetType();
var configuredProperties = ((IEnumerable<PropertyInfo>)
configurationType
.GetProperty("ConfiguredProperties", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(configuration)
)
.ToArray();
var configurationIgnoreMethod = configurationType.GetMethod("Ignore");
foreach (var p in entityProperties)
{
// skip if property is mapped...
if (configuredProperties.FirstOrDefault(c => c.Name == p.Name) != null)
continue;
// ignore properties that aren't mapped manually...
configurationIgnoreMethod.Invoke(configuration, new object[] { p });
}
}
}
}
/*
* https://github.com/aspnet/EntityFramework/issues/2805
*
* https://daveaglick.com/posts/custom-entity-type-configurations-in-entity-framework-code-first-part-1
* https://daveaglick.com/posts/custom-entity-type-configurations-in-entity-framework-code-first-part-2
*/
@hidegh
Copy link
Author

hidegh commented Mar 7, 2017

Here's a code snippet, to make sure EF6 will use only properties you map manually. Usage is simple, inside mapping class call the IgnoreNonMappedProperties extension method after configuring your properties.

public class EntryMap : EntityTypeConfiguration<Entry>
{
    public EntryMap()
    {
        HasKey(e => e.EntryId);
        Property(e => e.Description);
        this.IgnoreNonMappedProperties();
    }
}

@hidegh
Copy link
Author

hidegh commented Apr 4, 2017

FAIL:
This seems to work fine, but messes up bidirectional 1:N relations, where parent does it's configuration for both of the side, so configuring (N:1) from the child side won't be possible (EF won't allow it) and thus the back-ref. property will be ignored, even in case where we wan't to use it...

As a desperate measure to avoid un-mapping of custom properties on my entities I decided to use get/set functions...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment