DbSet.FirstOrDefault()?

本文关键字:FirstOrDefault DbSet | 更新日期: 2023-09-27 18:15:25

我试着这样做但是它说我不能使用FirstOrDefault

public static int GetId(this Context db, Type type, string name)
{
    return db.Set(type).FirstOrDefault(x => x.Name == name).Id;
}

错误是'System.Data.Entity。DbSet'不包含'FirstOrDefault'的定义,也没有扩展方法'FirstOrDefault'接受类型为'System.Data.Entity '的第一个参数。

可以找到DbSet'(您是否缺少using指令或汇编引用?)

我然后尝试了这个Cast方法,但给出了一个错误不能从非通用DbSet创建一个DbSet类型的对象'WindowStyle' (btw WindowStyle继承自DomainEntity下面),

var set = db.Set(type).Cast<DomainEntity>();
return set.FirstOrDefault(x => x.Name == name).Id;

这是类,

public class DomainEntity
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

DbSet.FirstOrDefault()?

也许你错过了

using System.Linq;

这个答案可能对你没有帮助,这取决于你调用这个方法的情况有多"动态",基本上如果你在编译时知道类型与否。如果你知道它,你可以写一个泛型方法:

public static class MyExtensions
{
    public static int? GetId<TEntity>(this Context db, string name)
        where TEntity : DomainEntity
    {
        return db.Set<TEntity>()
            .Where(x => x.Name == name)
            .Select(x => (int?)x.Id)
            .FirstOrDefault();
    }
}

我已经将其更改为投影,因为如果您只想要Id,则不需要加载完整实体。您可以让数据库来选择属性,从而略微提高性能。如果在数据库中没有匹配的名称,我还返回一个可空的int。

你可以在你的代码中这样调用它:

int? id = db.GetId<WindowStyle>("abc");
如您所见,对于这个解决方案,您必须在编译时指定WindowStyle类型。

这里假设DomainEntity不是模型的一部分(没有DbSet<DomainEntity>),而只是实体的一个基类。否则,@Paul Keister的解决方案会更简单。

编辑

或者您也可以尝试以下操作:

public static class MyExtensions
{
    public static int? GetId(this Context db, Type entityType, string name)
    {
        return ((IQueryable<DomainEntity>)db.Set(entityType))
            .Where(x => x.Name == name)
            .Select(x => (int?)x.Id)
            .FirstOrDefault();
    }
}

并命名为:

int? id = db.GetId("abc", someType);

如果someType没有从DomainEntity继承,它将在运行时抛出异常。泛型版本将在编译时对此进行检查。所以,如果你可以选择第一个版本

第一个构造不能工作,因为您正在使用非泛型DbSet,因此您不能应用仅适用于泛型的FirstOrDefault扩展方法。听起来你已经明白了,因为你已经在尝试获取非泛型DbSet。使用Cast()方法得到的错误是由于试图将DbSet强制转换为DbSet造成的。这是不允许的,因为如果是这样的话,就有可能向DbSet添加不一致的成员(类型不是WindowsStyle的对象)。另一种说法是,协方差不支持dbset,因为dbset允许添加。

我想你得另找一种方法来做你想做的事。这样混合LINQ和继承显然是有问题的。既然已经定义了基类型,并且只处理基类型上可用的属性,为什么不直接查询基类型呢?

    public static int GetId(this Context db, string name)
    {
        return db.DomainEntities.FirstOrDefault(x => x.Name == name).Id;
    }

您可能担心不同类型之间的名称冲突,但您可能可以从这个开始,并查看派生类型关联,以验证您正在查看正确的类型。处理此问题的一种方法是在DomainEntity定义中添加类型标志。

问题来了。DbSet类有自己的Cast<T>()实现,只允许数据库类型(如Cast<WindowStyle>()),所以这个方法不允许Cast<DomainEntity>(),并抛出异常。

相反,您希望使用IQueryable.Cast<T>()扩展方法,它将简单地将您的数据转换为基类型。下面是一个例子:

var set = ((IQueryable)db.Set(type)).Cast<DomainEntity>();
return set.First(x => x.Name == name).Id;

这是我的一个想法,似乎是有效的。

public static int GetId(this Context db, Type type, string name)
{
    var set = db.Set(type);
    foreach (dynamic entry in set)
        if (entry.Name == name)
            return entry.Id; 
}

试着反转一下

<>之前集。Where(x => x. name == name).Select(o=> x. id).FirstOrDefault();之前

您的将返回一个空实体,然后尝试从中获取Id。