【问题标题】:Create IQueryable<T> extension using base class in EF使用 EF 中的基类创建 IQueryable<T> 扩展
【发布时间】:2018-12-10 21:08:13
【问题描述】:

我正在尝试在我的基类上创建可重用的搜索查询,因此我不必为每个派生类重复相同的代码,但我无法让实体框架很好地发挥作用。

我有 3 个课程: CMEntityBase 网站 CMSiteServer

Site 和 SiteServer 均源自具有共同属性(ID、名称等)的 CMEntityBase

我想定义一些通用搜索: 例如获取姓名

using (var db = new LNOSCMDataModel())
            {
                db.Configuration.LazyLoadingEnabled = false;
                var servers = db.CMSiteServers.
                    AsNoTracking().
                    GetByName(id,Active).
                    ConvertToAPIVM().ToList();
}

我尝试过几种方式来定义 GetByName:

基类:

    public static IQueryable<CMEntityBase> GetByName(this IQueryable<CMEntityBase> Entities, string Name, bool Active = true)
    {
        return Entities.Where(ss => ss.Name == Name && ss.Active == Active || Active == false);//.Cast<IEntity>();
    }

泛型:

public static IQueryable<T> GetByName<T>(this IQueryable<CMEntityBase> Entities, string Name, bool Active = true) where T : CMEntityBase
        {
            return Entities.Where(ss => ss.Name == Name && ss.Active == Active || Active == false).Cast<T>();
        }

我尝试将基类定义为接口,并在泛型中使用 T : class, IEntity (interface) --> 这个方法来自:LINQ to Entities only supports casting EDM primitive or enumeration types with IEntity interface

最终他们都返回错误:

LINQ to Entities 仅支持使用 IEntity 接口转换 EDM 基元或枚举类型。

最后我想在基类属性上定义一个查询,但输出子类。现在看来我需要复制/粘贴每个派生类的方法。

【问题讨论】:

    标签: c# entity-framework linq inheritance


    【解决方案1】:

    而不是接受与您想要的不同类型的IQueryable,并尝试强制转换它(如错误所示,不支持),您只需要接受实际类型的IQueryable您的查询已经是,因此无需转换它。在这种情况下,就像在原始查询中使用泛型类型而不是基类型一样简单:

    public static IQueryable<T> GetByName<T>(this IQueryable<T> Entities, string Name, bool Active = true)
        where T : CMEntityBase //or the interface that specifies the needed members
    {
        return Entities.Where(ss => ss.Name == Name && ss.Active == Active || Active == false);
    }
    

    【讨论】:

      【解决方案2】:

      经过大量试验,解决方案是将基类创建为抽象类

      public abstract class CMEntityBase
      {
      
          [Key]
          [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
          public abstract decimal ID { get; set; }
      
      
          [StringLength(50)]
          public abstract string Name { get; set; }
      
          ....
      }
      

      在静态扩展类中定义我的扩展,这里的关键是使用 .Select( e=> e as T) 将其转换回子类

      public static partial class CMEntityBaseExtensions
      {
          public static IQueryable<T> GetByName<T>(this IQueryable<T> Entities, string Name, bool Active = true) where T : CMEntityBase
          {
              return Entities.Where(ss => ss.Name == Name && ss.Active == Active || Active == false).
                       Select(e => e as T); // cast back to child!
          }
      }
      

      然后我可以在我的控制器中使用它:

       var servers1 = db.CMSiteServers
                      .AsNoTracking().
                      GetByName(id, Active);
      

      和事件使用我的“铸造”功能转换为视图模型

                  var servers = servers1.
                      ConvertToAPIVM().ToList();
      

      看起来像这样:

      public static partial class CMSiteServerExtensions
      {
          public static IQueryable<CMSiteServerAPIVM> ConvertToAPIVM(this IQueryable<CMSiteServer> Servers)
          {
              return Servers.Select(ss => new CMSiteServerAPIVM()
              {
                  SiteServerID = ss.ID,
                  Name = ss.Name,
                  Description = ss.Description,
                  ...
              }
          }
       }
      

      【讨论】:

      • 我没有对你投反对票,但我怀疑你的粗体文本是否正确,因为查询已经是 T 类型,所以不需要强制转换,因为它所做的只是将 T 转换为 T .
      • 我需要再次审查,我记得当时有一些问题,但时间已经够久了,我不记得为什么它是必要的,因为它不应该是。跨度>
      猜你喜欢
      • 2011-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-14
      相关资源
      最近更新 更多