8

我正在尝试这样做,但它说我不能使用 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”的定义,并且找不到接受“System.Data.Entity.DbSet”类型的第一个参数的扩展方法“FirstOrDefault”(您是否缺少using 指令或程序集引用?)

然后我尝试了这种Cast方法,但是给出了错误Cannot create a DbSet from a non-generic DbSet for objects of 'WindowStyle'(顺便说一句从下面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; }
}
4

6 回答 6

29

也许你失踪了

using System.Linq;
于 2013-04-28T15:35:49.087 回答
8

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

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。通用版本将在编译时检查这一点。所以,如果你可以更喜欢第一个版本。

于 2011-11-22T19:34:29.350 回答
5

第一个构造不起作用,因为您使用的是非泛型 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 定义中添加一个类型标志。

于 2011-11-21T22:57:17.627 回答
3

这就是问题所在。该类有自己的实现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;
于 2011-11-22T18:09:55.380 回答
2

这是我的一个想法,似乎可行。

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; 
}
于 2011-11-22T15:22:37.333 回答
0

尝试扭转它一点做

set.Where(x => x.Name == name).Select(o=>o.Id).FirstOrDefault();

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

于 2011-11-21T22:43:43.397 回答