实体框架 - 按名称获取实体 [duplicate]

Entity framework - get entity by name [duplicate]

提问人:user1209216 提问时间:9/17/2012 最后编辑:halferuser1209216 更新时间:8/26/2023 访问量:35067

问:

我有以下代码(示例):

public dynamic GetData(string name) 
{
    using(var ctx = GetObjectContext()) 
    {
        switch (name) 
        {
        case "entity1":
            return ctx.entity1.ToList();
        case "entity2":
            return ctx.entity2.ToList();
        ......
        default:
            return null;
        }
    }
}

我想避免在此示例中切换。如何按名称找到所需的实体类,调用 ToList() 方法并返回数据?我可以使用反射来做到这一点吗?

C# 实体框架 反射

评论

0赞 toy 2/26/2020
你能告诉我GetObjectContext()是什么吗?它位于哪个命名空间中?PS 使用 core v3.1
0赞 Gert Arnold 8/27/2023
作为重复关闭,虽然问题是关于 EF6(不是核心)的,但到目前为止,大多数登陆这里的人都会使用 EF 核心,这个问题已经开始吸引 EF 核心答案。
0赞 user1209216 8/27/2023
重复的问题有多旧?编辑这个以表明它与核心无关不是更好吗?
0赞 Gert Arnold 8/27/2023
当然,随着技术的发展,新问题成为重复目标的情况并不少见。这个问题可以编辑,但这不会阻止人们发布新的 EF 核心答案。(事实上,认真尝试回答这个问题的人应该立即认识到它不是 EF 核心)。
0赞 user1209216 8/28/2023
这实际上甚至与实体框架无关,它与反射有关。

答:

24赞 chiccodoro 9/17/2012 #1

您可以使用反射来执行此操作,但是您还需要使用泛型,因为 ToList() 方法返回的列表类型对于每个实体类型都不同。

您可以通过反射访问属性 getter,如下所示:

var enumerable = typeof([ClassNameOfContext]).GetProperty(name).GetValue(ctx, null);

Whereas 是 ctx 作为其实例的类的名称。这在您的代码中并不明显,但您知道:-)[ClassNameOfContext]

问题是这将是一个并且必须被强制转换为您正在访问的实体类型。换句话说,这取决于您传递的名称。如果使用泛型来确定类型,则将能够正确转换对象,而不必返回偶数。enumerableobjectIEnumerable<EntityType>EntityTypedynamic

public TEntity Get<TEntity>(string name)
{
    ...

并从上面转换线条:

var enumerable = (IEnumerable<TEntity>)(typeof([ClassNameOfContext]).GetProperty(name).GetValue(ctx, null));
return enumerable.ToList();

给你!

附录:可以想象,您也可以去掉字符串参数 - 应尽可能避免在字符串中使用类型或属性的名称,因为它不是类型安全的。编译器无法识别它,并且 IDE 功能(如重构)不会考虑它。这里的问题是,属性名称通常是实体类型名称的复数形式。但是,您可以使用反射来查找其类型与 TEntity 匹配的属性。我把它作为一个练习:-)

评论

0赞 user1209216 9/17/2012
如何使用反射来做到这一点?ToList 返回的类型无关紧要,因为 GetData 返回类型是动态的,因此它可以是任何类型
0赞 chiccodoro 9/17/2012
@user1209216 - 在我看来,您正在使用动态来解决一个问题。应谨慎使用关键字。它有其非常具体的用例,使用起来是合理的,但在你的情况下,你完全能够强键入你的返回类型。这是首选的出路。dynamic
0赞 user1209216 9/17/2012
对不起,我还是有点困惑。什么是“TEntity”,我应该如何声明它?我是这个小手,所以我需要问。
0赞 chiccodoro 9/17/2012
@user1209216 - 对不起,最重要的部分丢失了,将其添加到上面截取的代码中。它是 .这是泛型。当您调用该方法时,您需要在括号中添加所需的实体类型,例如 对于您有 Rabbit 实体的虚构案例。在方法内部,Visual Studio 将将其视为现有类型。在运行时,它被你用(Rabbit,例如)的任何称呼来替换(-ish) 如果您还不知道泛型,请在网络上阅读它。这是重要的基础知识。<TEntity>GetData<Rabbit>("Rabbits")TEntity
0赞 user1209216 9/17/2012
嗯,好的,我明白了。但是,问题是:我宁愿寻找具有“开关”等效物的方法。当我的代码调用它时,我不知道实体类型 - 只有名称是已知的。此方法将获取要在报表上打印的数据,并且报表名称与实体名称相同。因此,在这种情况下,调用方法可以返回动态 - 在这种情况下这是安全的。我需要将报告类型作为字符串传递。当然,为了避免切换,我可以使用 Dictionary,但我认为有更好的解决方案。
1赞 Oleg Polezky 10/31/2017 #2

你可以像这样使用代码

private IEnumerable<TEntity> GetList<TEntity>(string connectionString, Func<object, T> caster)
{
    using (var ctx = new DbContext(connectionString))
    {
        var setMethod = ctx.GetType().GetMethod("Set").MakeGenericMethod(typeof(T));

        var querable = ((DbSet<object>)setMethod
        .Invoke(this, null))
        .AsNoTracking()
        .AsQueryable();

        return querable
            .Select(x => caster(x))
            .ToList();
    }
}

像这样调用:

var branchList = GetList<Branch>("connectionStringName", x => (Branch)x);

您可以删除 .AsNoTracking() 并删除 .ToList(),那么你将得到纯 IQueryable,你可以进一步查询。

0赞 James.mikael 9/16/2022 #3

我创建了一种方法来包含所有相关实体,并借助@chiccodoro的出色答案。

使用具有 7 个导航属性的实体“Product”

public static IQueryable<T> IncludeAllEntities<T>(this DbSet<T> entity, DataContext context) where T : class
{
        var queryable = entity.AsQueryable();

        var type = typeof(T);
        var entityType= context.Model.FindEntityType(type);
        var navs = entityType?.GetNavigations();

        if (navs == null)
        {
            return null;
        }

        List<string> navNames = new List<string>();

        foreach (var nav in navs)
        {
            navNames.Add(nav.Name);
        }

        try
        {
            var agg = navNames.Aggregate(queryable, (acc, name) => acc.Include(name));
            return agg;
        }
        catch (Exception ex)
        {
            throw;
        }
}

我正在获取实体的类型以获取导航属性,然后将每个实体的名称添加到字符串列表中,然后聚合列表以包含每个实体。

然后我们可以像这样使用这个扩展方法:

var record = await _context.Products
                           .IncludeAllEntities(_context)
                           .FirstOrDefaultAsync(x => x.Id == key);