Declare Properties to Ignore in Entities Interface (Ef Core)

Declare properties to ignore in entities interface (EF Core)

Currently EF Core does not provide a way to define custom conventions, but you can add the following to your OnModelCreating override (after all entity types are discovered) to iterate all entity types implementing the interface and call Ignore fluent API for each property:

var propertyNames = typeof(INotPersistingProperties).GetProperties()
.Select(p => p.Name)
.ToList();
var entityTypes = modelBuilder.Model.GetEntityTypes()
.Where(t => typeof(INotPersistingProperties).IsAssignableFrom(t.ClrType));
foreach (var entityType in entityTypes)
{
var entityTypeBuilder = modelBuilder.Entity(entityType.ClrType);
foreach (var propertyName in propertyNames)
entityTypeBuilder.Ignore(propertyName);
}

EF Core Reflect interface properties and ignore

You could use ModelBuilder.Entity(Type entityType) method to get the EntityTypeBuilder instance and then use its Ignore(string propertyName) method:

foreach (var prop in props)
{
modelBuilder.Entity(prop.DeclaringEntityType.ClrType).Ignore(prop.Name);
}

Ignore base class / interfaces completely in entity framework 5 code first

If you mark your base class(es) as abstract and use table per concrete type approach this may work. Something like;

context.Entity<User>().Map(p =>
{
p.MapInheritedProperties();
p.ToTable("Users");
});

refer to this.

Ignoring all implemented interface properties in EF CodeFirst

You can use reflection to find out which properties to ignore, then ignore them with Fluent API in DbContext.OnModelCreating method:

foreach(var property in typeof(IDomainEntity).GetProperties())
modelBuilder.Types().Configure(m => m.Ignore(property.Name));

Entity Framework generic expression to selectively hide property

Normally you might write something like this to hide a property

var entities = context.Services.Select(x => 
new Service { Id = Id, Description = Description, LargeXMLData = "" })

If you can do that manually, it should be doable automatically using the exact same concept, with little reflection and Expression APIs.

But note that this woult work only for EF Core, since EF6 does not support projecting to entity types, like new Service { ... } here, and projecting to dynamic types at runtime is not trivial and also will break the DTO mapping.

With that being said, following is a sample implementation of the aforementioned concept:

public static partial class QueryableExtensions
{
public static IQueryable<T> Excluding<T>(this IQueryable<T> source, params Expression<Func<T, object>>[] excludeProperties)
{
var excludeMembers = excludeProperties
.Select(p => ExtractMember(p.Body).Name)
.ToList();
if (excludeMembers.Count == 0) return source;
// Build selector like (T e) => new T { Prop1 = e.Prop1, Prop2 = e.Prop2, ... }
// for each public property of T not included in the excludeMembers list,
// which then will be used as argument for LINQ Select
var parameter = Expression.Parameter(typeof(T), "e");
var bindings = typeof(T).GetProperties()
.Where(p => p.CanWrite && !excludeMembers.Contains(p.Name))
.Select(p => Expression.Bind(p, Expression.MakeMemberAccess(parameter, p)));
var body = Expression.MemberInit(Expression.New(typeof(T)), bindings);
var selector = Expression.Lambda<Func<T, T>>(body, parameter);
return source.Select(selector);
}

static MemberInfo ExtractMember(Expression source)
{
// Remove Convert if present (for value type properties cast to object)
if (source.NodeType == ExpressionType.Convert)
source = ((UnaryExpression)source).Operand;
return ((MemberExpression)source).Member;
}
}

The usage would be exactly as desired:

var entities = context.Services.Excluding(x => x.LargeXMLData).ToArray();

The problem with this though is that it will automatically "include" navigation properties and/or unmapped properties.

So it would be better to use EF model metadata instead of reflection. The problem is that currently EF Core does not provide a good public way of plugging into their infrastructure, or to get access to DbContext (thus Model) from IQueryble, so it has to be passed as argument to the custom method:

public static IQueryable<T> Excluding<T>(this IQueryable<T> source, DbContext context, params Expression<Func<T, object>>[] excludeProperties)
{
var excludeMembers = excludeProperties
.Select(p => ExtractMember(p.Body).Name)
.ToList();
if (excludeMembers.Count == 0) return source;
// Build selector like (T e) => new T { Prop1 = e.Prop1, Prop2 = e.Prop2, ... }
// for each property of T not included in the excludeMembers list,
// which then will be used as argument for LINQ Select
var parameter = Expression.Parameter(typeof(T), "e");
var bindings = context.Model.FindEntityType(typeof(T)).GetProperties()
.Where(p => p.PropertyInfo != null && !excludeMembers.Contains(p.Name))
.Select(p => Expression.Bind(p.PropertyInfo, Expression.MakeMemberAccess(parameter, p.PropertyInfo)));
var body = Expression.MemberInit(Expression.New(typeof(T)), bindings);
var selector = Expression.Lambda<Func<T, T>>(body, parameter);
return source.Select(selector);
}

which makes the usage not so elegant (but doing the job):

var entities = context.Services.Excluding(context, x => x.LargeXMLData).ToArray();

Now the only remaining potential problem are shadow properties, but they cannot be handled with projection, so this technique simply cannot be used for entities with shadow properties.

Finally, the pure EF Core alternative of the above is to put the LargeXMLData into separate single property "entity" and use table splitting to map it to the same table. Then you can use the regular Include method to include it where needed (by default it would be excluded).

Have specific property configuration for all types that implement an interface except for one

I did find a solution, however it's a not so nice one. I did it by modifying the line of the type configuration to

modelBuilder.Types<IEventReportElement>().Where(x=>x.Name!="Position").Configure(x=>x.Property(y=>y.IdEventReport).IsRequired());

CodeFirst EF Core - Implementing interfaces possible?

I think it would be better if you create a base class

public class EntityModel:IEntityModel
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EntityId { get; set; }
publlic PostalAddress PrincipalAddress { get; set; }
}

CompanyEntity

public class CompanyEntity : EntityModel
{
public string CompanyName { get; set; }
}

CaseEntity

public class CaseEntity

{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CaseEntityId { get; set; }


public int CaseId { get; set; }
public CaseModel Case { get; set; }

public int EntityId { get; set; }
public virtual EntityModel EntityModel { get; set; }
}


Related Topics



Leave a reply



Submit