You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从字典动态调用不同类型集合?EF Core实践疑难

问题描述

需要实现的功能:通过字典存储DbSet(作为键,对应数据库实体)和其他集合(作为值,为IEnumerable或List类型的模型)。遍历字典时,先判断DbSet是否包含数据,若为空则将模型集合中的数据转换为实体后添加到DbSet,最后通过EntityFrameworkCore保存更改。但运行时无法访问这些DbSet或集合,尝试使用dynamic类型的字典后遇到RuntimeBinderException异常。

涉及类型说明

字典的键(DbSet)类型

  • configContext!.Clients:实际类型为 Duende.IdentityServer.EntityFramework.Entities.Client
  • configContext!.IdentityResources:实际类型为 Duende.IdentityServer.EntityFramework.Entities.IdentityResource
  • configContext!.ApiScopes:实际类型为 Duende.IdentityServer.EntityFramework.Entities.ApiScope
  • configContext!.ApiResources:实际类型为 Duende.IdentityServer.EntityFramework.Entities.ApiResource

字典的值(模型集合)类型

  • Config.Clients:实际类型为 Duende.IdentityServer.Models.Client
  • Config.IdentityResources:实际类型为 Duende.IdentityServer.Models.IdentityResource
  • Config.ApiScopes:实际类型为 Duende.IdentityServer.Models.ApiScope
  • Config.ApiResources:实际类型为 Duende.IdentityServer.Models.ApiResource

注:键(数据库实体)与值(模型)为一对一映射关系,可互相转换。

初始代码

引用部分

using Duende.IdentityServer.EntityFramework.DbContexts;
using Duende.IdentityServer.EntityFramework.Mappers;
using Duende.IdentityServer.Models;
using Microsoft.EntityFrameworkCore;
using DbClient = Duende.IdentityServer.EntityFramework.Entities.Client;
using DbIdentityResource = Duende.IdentityServer.EntityFramework.Entities.IdentityResource;
using DbApiScope = Duende.IdentityServer.EntityFramework.Entities.ApiScope;
using DbApiResource = Duende.IdentityServer.EntityFramework.Entities.ApiResource;

测试方法

private static void Test(this ConfigurationDbContext configContext)
{
    Dictionary<dynamic, dynamic> configCollections = new Dictionary<dynamic, dynamic>()
    {
        { configContext!.Clients, Config.Clients },
        { configContext!.IdentityResources, Config.IdentityResources },
        { configContext!.ApiScopes, Config.ApiScopes },
        { configContext!.ApiResources, Config.ApiResources },
    };

    foreach (var configCollection in configCollections)
    {
        var collection = configCollection!.Key.Any();

        if (collection == false)
        {
            foreach (var configValue in configCollection.Value)
            {
                configCollection!.Key.Add(configValue.ToEntity());
            }

            configContext.SaveChanges();
        }
    }
}

调用方式

configContext.Test();

初始报错

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: ''Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<Duende.IdentityServer.EntityFramework.Entities.Client>' does not contain a definition for 'Any''

尝试的解决方法(均未成功)

  • 泛型方法改造:将测试方法改为泛型方法,对集合进行Cast转换,调用时指定类型,但问题未解决。
  • 强制转换为DbSet:将 var collection = configCollection!.Key.Any(); 改为 ((DbSet<dynamic>)configCollection!.Key).Any();,报错无法转换类型。
  • 强制转换后再Cast:改为 ((DbSet<dynamic>) configCollection!.Key).Cast<dynamic>().Any();,仍报错无法转换类型。

新报错

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot convert type 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<Duende.IdentityServer.EntityFramework.Entities.Client>' to 'Microsoft.EntityFrameworkCore.DbSet''

最终解决方案

注:

  1. Duende.IdentityServer.EntityFramework.Mappers下的不同类型映射器位于更深层级
  2. 需在连接字符串中添加 MultipleActiveResultSets=true;
public static void TestExec()
{
    List<(IQueryable dbSet, IEnumerable<object> models)> configCollections = new() 
    {
        (configContext!.Clients, Config.Clients),
        (configContext!.IdentityResources, Config.IdentityResources),
        (configContext!.ApiScopes, Config.ApiScopes),
        (configContext!.ApiResources, Config.ApiResources),
    };

    foreach (var (dbSet, models) in configCollections)
    {
        if (dbSet.AsQueryable().GetEnumerator().MoveNext() == false)
        { 
            foreach (object configValue in models)
            {
                dynamic? test = null;

                switch (configValue.GetType().Name)
                {
                    case nameof(Client):
                        test = ClientMappers.ToEntity((dynamic)configValue);
                        break;
                    case nameof(IdentityResource):
                    case nameof(OpenId):
                    case nameof(Profile):
                    case nameof(Email):
                    case nameof(Phone):
                    case nameof(Address):
                        test = IdentityResourceMappers.ToEntity((dynamic)configValue);
                        break;
                    case nameof(ApiScope):
                        test = ScopeMappers.ToEntity((dynamic)configValue);
                        break;
                    case nameof(ApiResource):
                        test = ApiResourceMappers.ToEntity((dynamic)configValue);
                        break;
                    default:
                        break;
                }

                ((dynamic)dbSet).Add(test!);
                
            }

            configContext.SaveChanges();
        }
    }
}

内容的提问来源于stack exchange,提问作者user20291437

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 06:35:19