I'm working on just another version of Onion Architecture using EF in core environment. I have some mapping that i want it to be applied on almost every table i have, and since the mapping is somehow logical in my scenario, i want to close the gap in my and my co-workers mind, reducing further data corruption.
So i wen't through Reflection step by step, trying to achieve the following scenario:
for (all registered model in context) {
if(model has IXyz interface) {
* builder apply mapping on entity
}
}
The star sign ():* The mapping i want to configure at * point is as following:
builder.Entity<Sample>().HasQueryFilter(m => EF.Property<bool>(m, nameof(IDeletableEntity.IsDeleted)) == false);
I tried many things, but none worked; I jumped from error to error, at both run time & compile time:
1.
System.ArgumentException: 'Static method requires null instance, non-static method requires non-null instance.'
2.
System.ArgumentException: 'Method 'Boolean b__6_0(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder
1[Template.Domain.Models.Sample])' declared on type 'Template.Data.Configurations.ApplicationDbContext+<>c__6
1[Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1[Template.Domain.Models.Sample]]' cannot be called with instance of type 'Template.Data.Configurations.ApplicationDbContext''
3.
Error CS1660 Cannot convert lambda expression to type 'Expression' because it is not a delegate
The latest state of my code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Template.Domain.Interfaces;
using Template.Domain.Models;
namespace Template.Data.Configurations
{
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public virtual DbSet<Sample> Samples { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
Debugger.Launch();
var modelBuilderMethods = builder.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
MethodInfo desiredModelBuilderMethod = modelBuilderMethods.FirstOrDefault(modelBuilderMethod => modelBuilderMethod.Name == nameof(builder.Entity) && modelBuilderMethod.IsGenericMethod && modelBuilderMethod.IsGenericMethodDefinition && modelBuilderMethod.GetParameters().Length == 0); //Entity<T>() Method
//builder.Entity<Sample>().HasQueryFilter(m => EF.Property<bool>(m, nameof(IDeletableEntity.IsDeleted)) == false);
foreach (var entityType in builder.Model.GetEntityTypes())
{
if (entityType.ClrType.GetInterfaces().Any(w => w == typeof(IDeletableEntity)))
{
var entityGenericMethod = desiredModelBuilderMethod.MakeGenericMethod(entityType.ClrType);
var entity = entityGenericMethod.Invoke(builder, new object[0]);
var entityTypeBuilder = entity.GetType();
var entityTypeBuilderMethods = entityTypeBuilder.GetMethods(BindingFlags.Public | BindingFlags.Instance);
MethodInfo desiredEntityTypeBuilder = entityTypeBuilderMethods.FirstOrDefault(entityTypeBuilderMethod => entityTypeBuilderMethod.Name == nameof(EntityTypeBuilder.HasQueryFilter) && !entityTypeBuilderMethod.IsGenericMethod && !entityTypeBuilderMethod.IsGenericMethodDefinition); //HasQueryFilter(Expression<Func<T, bool>> filter) Method
var queryFilterMethod = this.GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.FirstOrDefault(w => w.Name == nameof(SetQueryFilters)
&& w.IsGenericMethod
&& w.IsGenericMethodDefinition
&& w.GetParameters().Length == 0)
?.MakeGenericMethod(entity.GetType());
var lambdaExpression = queryFilterMethod.Invoke(this, new object[0]);
desiredEntityTypeBuilder.Invoke(entity, new object[]
{
lambdaExpression
});
}
}
}
private Expression<Func<T, bool>> SetQueryFilters<T>()
{
var func = new Func<T, bool>(m=> EF.Property<bool>(m, nameof(IDeletableEntity.IsDeleted)) == false);
var exp = Expression.Default(typeof(ApplicationDbContext)); //Expression.Constant(this);
var methodCallExpression = Expression.Call(exp, func.Method);
var expression = Expression.Lambda<Func<T, bool>>(methodCallExpression);
return expression;
}
}
}
namespace Template.Domain.Interfaces
{
public interface IDeletableEntity
{
bool IsDeleted { get; set; }
}
}
using System.ComponentModel.DataAnnotations.Schema;
using Template.Domain.Interfaces;
namespace Template.Domain.Models
{
public class Sample : IDeletableEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
}
Note to Debug this code you need to call "Update-Database" in "Package Manager Console".
Thank you, Hassan.
Aucun commentaire:
Enregistrer un commentaire