BuilderHasIndexExtensions.cs 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. using System;
  2. using System.Linq;
  3. using System.Linq.Expressions;
  4. using System.Reflection;
  5. using Microsoft.EntityFrameworkCore;
  6. using Microsoft.EntityFrameworkCore.Metadata.Builders;
  7. using Microsoft.EntityFrameworkCore.Metadata.Internal;
  8. using Microsoft.EntityFrameworkCore.Query;
  9. using System.Collections.ObjectModel;
  10. namespace MTWorkHR.Infrastructure.Data
  11. {
  12. public static class BuilderHasIndexExtensions
  13. {
  14. static readonly MethodInfo HasIndexMethod = typeof(BuilderHasIndexExtensions)
  15. .GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
  16. .Single(t => t.IsGenericMethod && t.Name == nameof(SetIndex));
  17. public static void HasIndexOnAllEntities<TEntityInterface>(
  18. this ModelBuilder builder,
  19. Expression<Func<TEntityInterface, object>> filterExpression)
  20. {
  21. foreach (var type in builder.Model.GetEntityTypes()
  22. .Where(t => t.BaseType == null)
  23. .Select(t => t.ClrType)
  24. .Where(t => typeof(TEntityInterface).IsAssignableFrom(t)))
  25. {
  26. builder.SetEntityIndex(
  27. type,
  28. filterExpression);
  29. }
  30. }
  31. static void SetEntityIndex<TEntityInterface>(
  32. this ModelBuilder builder,
  33. Type entityType,
  34. Expression<Func<TEntityInterface, object>> filterExpression)
  35. {
  36. HasIndexMethod
  37. .MakeGenericMethod(entityType, typeof(TEntityInterface))
  38. .Invoke(null, new object[] { builder, filterExpression });
  39. }
  40. static void SetIndex<TEntity, TEntityInterface>(
  41. this ModelBuilder builder,
  42. Expression<Func<TEntityInterface, object>> filterExpression)
  43. where TEntityInterface : class
  44. where TEntity : class, TEntityInterface
  45. {
  46. var concreteExpression = filterExpression
  47. .Convert<TEntityInterface, TEntity>();
  48. builder.Entity<TEntity>()
  49. .HasIndex(concreteExpression);
  50. }
  51. }
  52. public static class HasIndexExpressionExtensions
  53. {
  54. // This magic is courtesy of this StackOverflow post.
  55. // https://stackoverflow.com/questions/38316519/replace-parameter-type-in-lambda-expression
  56. // I made some tweaks to adapt it to our needs - @haacked
  57. public static Expression<Func<TTarget, object>> Convert<TSource, TTarget>(
  58. this Expression<Func<TSource, object>> root)
  59. {
  60. var visitor = new ParameterTypeVisitor<TSource, TTarget>();
  61. return (Expression<Func<TTarget, object>>)visitor.Visit(root);
  62. }
  63. class ParameterTypeVisitor<TSource, TTarget> : ExpressionVisitor
  64. {
  65. private ReadOnlyCollection<ParameterExpression> _parameters;
  66. protected override Expression VisitParameter(ParameterExpression node)
  67. {
  68. return _parameters?.FirstOrDefault(p => p.Name == node.Name)
  69. ?? (node.Type == typeof(TSource) ? Expression.Parameter(typeof(TTarget), node.Name) : node);
  70. }
  71. protected override Expression VisitLambda<T>(Expression<T> node)
  72. {
  73. _parameters = VisitAndConvert(node.Parameters, "VisitLambda");
  74. return Expression.Lambda(Visit(node.Body), _parameters);
  75. }
  76. }
  77. }
  78. }