使用EF中的基类创建IQueryable <t>扩展

时间:2018-12-10 21:08:14

标签: c# entity-framework linq inheritance

我正在尝试在基类上创建可重用的搜索查询,因此不必为每个派生的类重复使用相同的代码,但是我无法使实体框架正常运行。

我有3个班级: CMEntityBase CMSite CMSiteServer

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

我想定义一些通用搜索: 例如GetByName

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:泛型,IEntity(接口) ->这种方法来自:LINQ to Entities only supports casting EDM primitive or enumeration types with IEntity interface

最终它们都返回错误:

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

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

2 个答案:

答案 0 :(得分:0)

与其接受与您想要的类型不同的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);
}

答案 1 :(得分:-1)

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

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,
            ...
        }
    }
 }