Home > Enterprise >  Calling generic method recursively with type change in c#
Calling generic method recursively with type change in c#

Time:11-11

I have the following generic class

 public class EFBatchOperation<TContext, T> : IEFBatchOperationBase<TContext, T>, IEFBatchOperationFiltered<TContext, T>
    where T : class
    where TContext : DbContext{
      private ObjectContext context;
    private DbContext dbContext;
    private IDbSet<T> set;
    private Expression<Func<T, bool>> predicate;

    public EFBatchOperation(TContext context, IDbSet<T> set)
    {
        this.dbContext = context;
        this.context = (context as IObjectContextAdapter).ObjectContext;
        this.set = set;
    }

    public static IEFBatchOperationBase<TContext, T> For<TContext, T>(TContext context, IDbSet<T> set)
        where TContext : DbContext
        where T : class
    {
        return new EFBatchOperation<TContext, T>(context, set);
    }
    public BatchOperationResult InsertAll<TEntity>(IEnumerable<TEntity> items, DbConnection connection = null, int? batchSize = null) where TEntity : class, T
    {
       // the problem is here I want to call the current function 'InsertAll' but after changing the type of the function. passing a different type to the function. I tried the following but its not working  
         var objectContext = ((IObjectContextAdapter)this.dbContext).ObjectContext;
            var os = objectContext.CreateObjectSet<TEntity>();
            var foreignKeyProperties = os.EntitySet.ElementType.NavigationProperties.Where(x => x.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);
            Type entityType = typeof(TEntity);
            foreach (var foreignKeyProperty in foreignKeyProperties)
            {
                var childProperty = foreignKeyProperty.ToEndMember.GetEntityType();

                foreach (var item in items)
                {
                    var childValue = entityType.GetProperty(foreignKeyProperty.Name).GetValue(item);
                    Type childValueType = childProperty.GetType();

                    
                    //MethodInfo method = typeof(EFBatchOperation).GetMethod("InsertAll");
                    MethodInfo method = typeof(EFBatchOperation<TContext, T>).GetMethod("InsertAll");
                    var newMethod = method.MakeGenericMethod(new[] { childValueType.DeclaringType });
                    newMethod.Invoke(this, new object[] { childValue });
                    // InsertAll<>(childValue, connection, batchSize);
                }
            }
    }
   }

I am calling InsertAll function as follows:

 BatchOperationResult batchOperationResult = EFBatchOperation.For(context, dbSet).InsertAll(collectionOfEntitiesToInsert);

the problem is here I want to call the current function 'InsertAll' but after changing the type of the function. passing a different type to the function.

I have tried to call the function using reflection but its not working using the following code

   MethodInfo method = typeof(EFBatchOperation<TContext, T>).GetMethod("InsertAll");
   var newMethod = method.MakeGenericMethod(new[] { childValueType });
   newMethod.Invoke(this, new object[] { childValue });

and I got the following error

GenericArguments [0], "System.Data.Entity.Core.Metadata.Edm.EntityType" for "EntityFramework.Utilities.BatchOperationResult InsertAll [TEntity] (System.Collections.Generic.IEnumerable1 [TEntity], System.Data.Common .DbConnection, System.Nullable1 [System.Int32]) "exceeds the" TEntity "type constraint.

Update:

  • the idea here is to insert child-related properties because the original code just inserted the main entity, not the child elements.
  • Also Updated the code with more code to clarify what i am trying to do here

CodePudding user response:

Your method has where TEntity : class, T restriction. Even when calling it via reflection via MakeGenericMethod, you still cannot pass any arbitrary type - you have to pass a type that satisfies that restriction. This is what the error tells you: you have passed some type that does not satisfy either class, or T restriction.

If I read the exception message well, the TEntity was at this point an int?, which surely isn't a class *). I don't know how about T.

May try writing a version of this method that does not need those restrictions, and call it instead?

*) nullable<int>, or int? - yeah, it's nullable and can hold a 'null' value, but still it's a struct, and doesn't fit class generic constraint. If you need a method with class constraint, make another method that handles similar struct constraint, and make your code detect and pick one of them as needed.

EDIT: also, since you start with .InsertAll(collectionOfEntitiesToInsert), it seems to handle objects, it seems pretty strange to then try recursing it to handle inserting a set of int? values.. I have no idea what mechanism you are trying to devise, but usually a-batch-of-objects (usually 1object=1row in a table) is processed a little bit different than a-batch-of-scalars (a column = set of values projected from multiple rows; alternatively, packed multi-valued attribute in a row).. anyways, I wouldn't expect those to have the same common generic implementation.. really, odd

CodePudding user response:

I'm assuming that type T is some base class that all of your model entities extend? Including this childValueType?

From the error message, System.Data.Entity.Core.Metadata.Edm.EntityType does not qualify for the constraints on TEntity.

EntityType is the EF Core implementation of IEntityType. Though you have not included in your example where childValueType is defined, I believe you have assigned childValueType = [IEntityType].GetType(), where you intended childValueType = [IEntityType].ClrType.

  • Related