【发布时间】:2019-07-09 06:36:18
【问题描述】:
我在 EF 中遇到了一件有趣的事情。如果我们使用基础实体获取子实体,则加载实体需要更多时间。我的模型如下所示:
public abstract class BaseDocument
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public abstract class ComplexDocument : BaseDocument
{
public string AuthorName { get; set; }
}
public abstract class SimpleDocument : BaseDocument
{
public int Level { get; set; }
}
public abstract class OfficeDocument : ComplexDocument
{
public string OfficeName { get; set; }
}
public abstract class ClassDocument : SimpleDocument
{
public string HeadName { get; set; }
}
public class WordDocument : OfficeDocument
{
public int PagesCount { get; set; }
}
public class ExcelDocument : OfficeDocument
{
public int SheetsCount { get; set; }
}
public class TextDocument : ClassDocument
{
public int LinesCount { get; set; }
}
public class Context : DbContext
{
public Context() : base(@"Server=(localdb)\MSSQLLocalDB;Database=EFSIX;Trusted_Connection=True;")
{
Database.CreateIfNotExists();
}
public DbSet<BaseDocument> BaseDocuments { get; set; }
public DbSet<ComplexDocument> ComplexDocuments { get; set; }
public DbSet<SimpleDocument> SimpleDocuments { get; set; }
public DbSet<OfficeDocument> OfficeDocuments { get; set; }
public DbSet<ClassDocument> ClassDocuments { get; set; }
public DbSet<ExcelDocument> ExcelDocuments { get; set; }
public DbSet<WordDocument> WordDocuments { get; set; }
public DbSet<TextDocument> TextDocuments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseDocument>().ToTable("BaseDocuments");
modelBuilder.Entity<ComplexDocument>().ToTable("ComplexDocuments");
modelBuilder.Entity<SimpleDocument>().ToTable("SimpleDocuments");
modelBuilder.Entity<OfficeDocument>().ToTable("OfficeDocuments");
modelBuilder.Entity<ExcelDocument>().ToTable("ExcelDocuments");
modelBuilder.Entity<WordDocument>().ToTable("WordDocuments");
modelBuilder.Entity<ClassDocument>().ToTable("ClassDocuments");
modelBuilder.Entity<TextDocument>().ToTable("TextDocuments");
}
public IQueryable<T> GetEntities<T>() where T : class
{
return Set<T>();
}
}
我正在创建一些数据:
static void CreateTestData()
{
using (Context context = new Context())
{
for (int i = 0; i < 20; i++)
{
ExcelDocument excel = new ExcelDocument()
{
Id = Guid.NewGuid(),
AuthorName = $"ExcelAuthor{i}",
Name = $"Excel{i}",
OfficeName = $"ExcelOffice{i}",
SheetsCount = (i + 1) * 10
};
context.ExcelDocuments.Add(excel);
WordDocument word = new WordDocument()
{
Id = Guid.NewGuid(),
AuthorName = $"WordAuthor{i}",
Name = $"Word{i}",
OfficeName = $"WordOffice{i}",
PagesCount = (i + 2) * 10
};
context.WordDocuments.Add(word);
TextDocument text = new TextDocument()
{
Id = Guid.NewGuid(),
Name = $"Text{i}",
LinesCount = (i + 3) * 10,
HeadName = $"Head{i}",
Level = i + 5
};
context.TextDocuments.Add(text);
}
context.SaveChanges();
}
}
我做了两种从数据库获取WordDocument 的方法。其中一个使用BaseDocument,另一个使用WordDocument。两者都返回 20 个 WordDocument 实例:
static long ReadBaseDoc()
{
using (Context context = new Context())
{
var words= context.GetEntities<BaseDocument>().Where(e => e.Name.StartsWith("Word"));
Stopwatch stopwatch = Stopwatch.StartNew();
var instacnes = excel.ToList();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
}
static long ReadWordDoc()
{
using (Context context = new Context())
{
var words = context.GetEntities<WordDocument>().Where(e => e.Name.StartsWith("Word"));
Stopwatch stopwatch = Stopwatch.StartNew();
var instacnes = words.ToList();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
}
我分别测试了moth方法,几次,平均方法ReadWordDoc需要25ms,方法ReadBaseDoc需要52ms(实例相同)。
现在问题不是太大,但是当我们有复杂的继承时,它需要超过 1 秒。我创建了 10 个类并继承自 BaseDocument。之后我执行了ReadBaseDoc 和ReadWordDoc 方法。 ReadWordDoc 用了 25 毫秒,ReadBaseDoc 用了 1023 毫秒。实例相同,为什么ReadBaseDoc 需要更多时间?在 EF 中避免此类问题的更好方法是什么?
【问题讨论】:
-
afaik,EF 中的主要性能问题与连接建立、模型创建、查询计算和物化有关,其中前两个将在特定上下文类的第一次调用时发生,第三个与每个具有特定条件的第一个(子)查询,并且每次第四个。所以会有一个开销,但一旦你做了第一个查询,它就不应该非常重要。关于问题还有很多要提到的,但归结为这四点。
-
您能否准确添加
GetEntities并包含模型。尝试获取多少条记录 -
@Eldho,我添加了一些细节
-
将您的测试放入一个循环中并重复 1000 次。这应该显示延迟是否是第一次启动的开销。
-
使用TPT或TPH的继承类型。请包含这些信息和详细信息以重现该问题。
标签: c# performance entity-framework