Как я могу передать лямбда-выражение в метод, который вызывается с помощью отражения - PullRequest
0 голосов
/ 25 февраля 2020

Я работаю над еще одной версией Onion Architecture, использующей EF в базовой среде. У меня есть какое-то сопоставление, которое я хочу, чтобы оно применялось почти ко всем таблицам, которые у меня есть, и, поскольку сопоставление каким-то образом логично в моем сценарии, я хочу устранить пробел в уме и у коллег, уменьшив дальнейшее повреждение данных.

Так что я не прошел шаг за шагом «Отражение», пытаясь достичь следующего сценария:

for (all registered model in context) {
  if(model has IXyz interface) {
    * builder apply mapping on entity
  }
}

Знак зодиака (): * Отображение, которое я хочу настроить в точке * следующим образом:

builder.Entity<Sample>().HasQueryFilter(m => EF.Property<bool>(m, nameof(IDeletableEntity.IsDeleted)) == false);

Я пробовал много вещей, но ни одна не работала; Я перепрыгивал с ошибки на ошибку, как во время выполнения, так и во время компиляции:

1.

System.ArgumentException: для метода Stati c требуется нулевой экземпляр, не статический * Метод 1041 * требует ненулевого экземпляра. '

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]] 'нельзя вызвать с экземпляром типа' Template.Data.Configurations.ApplicationDbContext ''

3.

Ошибка CS1660 Невозможно преобразовать лямбда-выражение в тип 'Выражение', поскольку оно не является делегатом

Последнее состояние моего кода:

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; }
    }
}

Примечание для Отладка Этот код необходимо вызвать «Update-Database» в «Консоли диспетчера пакетов».

Спасибо, Хассан.

...