EF条件包含实体类型

时间:2015-12-01 11:53:46

标签: c# entity-framework entity-framework-6

请假设这个架构:

public class Mammal
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Dog : Mammal
{
    public int TailId { get; set; }
    public Tail Tail { get; set; }
}

public class Bat : Mammal
{
    public int WingId { get; set; }
    public Wing Wing { get; set; }
}

public class Buffalo : Mammal
{
    public virtual ICollection<Horn> Horns { get; set; }
}

public class Tail
{
    public int Id { get; set; }
    ...
}

public class Wing
{
    public int Id { get; set; }
    ...
}

public class Horn
{
    public int Id { get; set; }
    ...
}

现在,我的背景:

public class MyContext : DbContext
{
    public DbSet<Mammal> Mammals { get; set; }
}

所以,我想只进行一次SQL查询,并包含(并加载)所有嵌套实体,如:

var query = myContext.Mammals
    .IncludeIfTypeIs<Dog>(d => d.Tail)
    .IncludeIfTypeIs<Bat>(b => b.Wing)
    .IncludeIfTypeIs<Buffalo>(b => b.Horns)
    ...
    ...
;

我知道我可以单独执行此操作,但我不想要,因为我有很多实体,我需要最小化数据库请求。

我不想使用延迟加载,因为这也会产生许多数据库请求。

如何实现?

6 个答案:

答案 0 :(得分:0)

你可以尝试这样的事情:

    public static string SearchPage(Uri url, int timeOutSeconds)
    {
        StringBuilder sb = new StringBuilder();

        try
        {
            string place = HttpUtility.ParseQueryString(url.Query).Get("destination").Split(':')[1];
            string resultID = HttpUtility.ParseQueryString(url.Query).Get("resultID");
            string checkin = HttpUtility.ParseQueryString(url.Query).Get("checkin").Replace("-", "");
            string checkout = HttpUtility.ParseQueryString(url.Query).Get("checkout").Replace("-", "");
            string Rooms = HttpUtility.ParseQueryString(url.Query).Get("Rooms");
            string adults_1 = HttpUtility.ParseQueryString(url.Query).Get("adults_1");
            string languageCode = "EN";
            string currencyCode = "INR";
            string ck = "languageCode=" + languageCode + "; a_aid=400; GcRan=1; __RequestVerificationToken=IHZjc7KM_LbUXRypz02LoK4wmeLNcmRpIr-6vmPl5eNepILScAc15vn0TgQJtmABgedDy8xz4bnkqC30_zUGE1A1SaA1; Analytics=LandingID=place:77469:0m&LanguageCode=" + languageCode + "&WebPageID=9; Tests=165F000901000A1100F81000FE110100000102100103100104000105100052; dcid=dal05; currencyCode=" + currencyCode + "; countryCode=" + languageCode + "; search=place:" + place + "#" + checkin + "#" + checkout + "#" + adults_1 + "; SearchHistory=" + place + "%" + checkin + "%" + checkout + "%" + adults_1 + "%" + currencyCode + "%%11#" + place + "%" + checkin + "%" + checkout + "%" + adults_1 + "%" + currencyCode + "%%" + resultID + "#; visit=date=2015-11-23T18:26:05.4922127+11:00&id=45111733-acef-47d1-aed3-63cef1a60591; visitor=id=efff4190-a4a0-41b5-b807-5d18e4ee6177&tracked=true";


            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Timeout = timeOutSeconds * 1000;
            request.ReadWriteTimeout = timeOutSeconds * 1000;
            request.KeepAlive = true;
            request.Method = "GET";
            request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
            request.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36 OPR/33.0.1990.115";
            request.Headers.Add("Accept-Language", "en-US,en;q=0.8");
            request.Headers.Add("Cookie", ck);
            request.Headers.Add("Upgrade-Insecure-Requests", "1");
            request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);

            StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream());

            string line = reader.ReadToEnd();

            sb.Append(line);
            sb.Replace("<br/>", Environment.NewLine);
            sb.Replace("\n", Environment.NewLine);
            sb.Replace("\t", " ");

            reader.Close();
            reader.Dispose();
            request.Abort();

        }
        catch (Exception ex)
        {
            //throw ex;
        }

        return sb.ToString();
    }

然后在你的数据库调用中:

public static class Extensions
{
    public static IQueryable<Mammal> IncludeExtraEntities<Mammal,T>(this IQueryable<Mammal> query, T derivedType) where T :Mammal
    {
        if (derivedType is Dog)
            return query.Include("Tail");
        if (derivedType is Bat)
            return query.Include("Wing");
        return query;
    }
}

也许这会奏效。

答案 1 :(得分:0)

您可以创建一个方法来包含表达式列表。 (或者可能是一种扩展方法)。

public static IQueryable<Mammal> GetMammals(params Expression<Func<T, Object>>[] includeExps)
{
     var query = context.Mammals.AsQueryable();
     if (includeExps != null)
        query = includeExps.Aggregate(query, (current, exp) => current.Include(exp));

     return query;

}

然后,在你的代码中:

//Bat and Dog will be included here
var mammals = GetMammals(i => i.Bat, i => i.Dog);

希望它有所帮助!

答案 2 :(得分:0)

问题是,如何仅使用基类的DbSet并且不使用延迟加载来加载派生类型的属性?我担心这是不可能的。

但你可以这样做:

public class MyContext : DbContext
{
    public DbSet<Mammal> Mammals { get; set; }
    public DbSet<Dog>    Dogs    { get; set; }
    public DbSet<Bat>    Bats    { get; set; }
}

您还可以简单地为每种类型实现一种特定于类型的方法

public static class Extensions
{
    public static IQueryable<Dog> IncludeExtraEntities<Dog>(this IQueryable<Dog> query) where Dog : Mammal
    {
            return query.Include("Tail");
    }

    public static IQueryable<Bat> IncludeExtraEntities<Bat>(this IQueryable<Bat> query) where Bat: Mammal
    {
            return query.Include("Wing");
    }
}

然后,您只需致电:

myContext.Dogs.IncludeExtraEntities();

将调用取决于您的类型的方法。

答案 3 :(得分:0)

你对Include的期望太高了。您在Include中输入的lambda表达式只是一个看起来太聪明的字符串提供程序。在引擎盖下,解析其成员表达式以获取属性的名称,这就是全部。该名称将输入到接受字符串参数的Include方法中。该方法可以完成实际工作。

lambda表达式必须指向IQueryable中类型的导航属性。你做不到......

myContext.Mammals.Include(d => d.Tail)

...因为Tail不是基本类型的属性。你只能做......

myContext.Mammals.OfType<Dog>().Include(d => d.Tail)

你能得到的最好的东西就像是

from m in context.Mammals
let tail = (m as Dog).Tail
let wing = (m as Bat).Wing
let horns= (m as Buffalo).Horns
select new { Mammal = m, tail, wing, horns }

由于所有内容都已转换为SQL,因此您无需担心空引用异常。

答案 4 :(得分:0)

我遇到了类似的问题,在这里我是如何解决的:

 public IEnumerable<OrderLine> GetAllOrderLinesData()
    {
        var _StockOrderLines = appContext.OrderLines.OfType<StockOrderLine>()
            .Include(p => p.Order).Include(p => p.Product);

        var _AnnualOrderLines = appContext.OrderLines.OfType<AnnualOrderLine>()
            .Include(p => p.Order).Include(p => p.Customer);

        return _StockOrderLines.ToList<OrderLine>().Union<OrderLine>(_AnnualOrderLines.ToList<OrderLine>());
    }

你可以改变你的课程 :)

答案 5 :(得分:0)

EF Core在2.1及更高版本中支持此功能。参见Github问题here

var query = myContext.Mammals
    .Include(d => (d as Dog).Tail)
    .Include(b => (b as Bat).Wing)
    .Include(b => (b as Buffalo).Horns)

这将在一个查询中包含所有属性。

此处是指向official documentation的链接。