不久前,我创建了一个系统,在这个系统中,用户可以为某些对象定义带有自定义文件的类别。然后,每个对象都有基于其类别的FieldValue。以下课程:
public class DbCategory
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public TextDbField MainField { get; set; }
public List<DbField> Fields { get; set; }
}
public class DbObject
{
public int Id { get; set; }
public byte[] Bytes { get; set; }
[Required]
public DbCategory Category { get; set; }
public TextDbFieldValue MainFieldValue { get; set; }
public List<DbFieldValue> FieldsValues { get; set; }
}
public abstract class DbField
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public bool Required { get; set; }
}
public class IntegerDbField : DbField
{
public int? Minimum { get; set; }
public int? Maximum { get; set; }
}
public class FloatDbField : DbField
{
public double? Minimum { get; set; }
public double? Maximum { get; set; }
}
//... few other types
public abstract class DbFieldValue
{
[Key]
public int Id { get; set; }
[Required]
public DbField Field { get; set; }
[JsonIgnore]
public abstract string Value { get; set; }
}
public class IntDbFieldValue : DbFieldValue
{
public int? IntValue { get; set; }
public override string Value
{
get { return IntValue?.ToString(); }
set
{
if (value == null) IntValue = null;
else IntValue = int.Parse(value);
}
}
}// and other FieldValue types在我的开发机器(i5,16 first和ssd驱动器)上,数据库(在SqlExpress中)有4类,每个字段有5-6个字段,10k记录,第一次查询大约需要15s。第一个查询是
var result = db.Objects
.Include(s => s.Category)
.Include(s => s.Category.MainField)
.Include(s => s.MainFieldValue.Field)
.Include(s => s.FieldsValues.Select(f => f.Field))
.Where(predicate ?? AlwaysTrue)
.ToArray();我这么做是为了把所有东西都装进记忆里。然后,我处理缓存的列表并将更改写入数据库。我这样做是因为用户可以在每个FieldValue上使用过滤器执行搜索。每次查询数据库的速度都要慢得多--不过,这个部分运行得很好。
问题会在稍后发生。有些客户端定义了6个类别,每个类别上都有20+字段,并存储70k+记录,启动有时需要超过15分钟。在此之后,5k和50k之间的速度没有差别。
我发现的每一种改进EF代码第一次启动时间的技术都主要考虑视图创建缓存、ngening EF等等,但在本例中,在添加更多记录之后,启动时间会增加,而不是添加更多实体类型。
我意识到这是由模式的复杂性造成的,但是有什么方法可以加快速度吗?幸运的是,这是Windows,因此一旦启动,它就会持续数周,但仍然如此。
是否应该在第一次加载时删除EF,并在纯SQL中执行此操作?我应该分批做这个吗?我应该把EF改为nHibernate吗?还是别的什么?在执行此行期间,在虚拟化服务器上,该程序使CPU (不是SQL服务器,而是我的应用程序)达到最大。
我尝试过只加载对象,然后再加载它们的属性。这在小型数据库上有点快(但并不明显),但在大型数据库上则更慢。任何帮助都值得赞赏,即使答案是“吸取并等待”。
发布于 2018-01-26 10:01:29
我用这些技巧成功地减少了EF 3倍的总启动时间:
ctx.Database.Initialize()。这仍然需要3-4秒,但由于它与其他事情一起发生,它有很大帮助。在此之前,我只写了Inlude Inlude之后的包含,它转换为多个联接。我在一些博客上发现了一条“经验法则”,其中两个链接包括EF表现得相当好,但每一条都大大减慢了每件事的速度。我还找到了一个博客帖子,它显示了EF缓存:一旦给定的实体加载了包含或加载,它将自动放入正确的属性(博客作者对对象的联合是错误的)。所以我做了这个:
using (var db = new MyContext())
{
db.Fields.Load();
db.Categories.Include(c => c.MainField).Include(x => x.Fields).Load();
db.FieldValues.Load();
return db.Objects.Include(x => x.MainFieldValue.Field).ToArray();
} 这比从问题中获取数据快6倍。我认为,一旦以前加载了实体,EF引擎就不会调用相关对象的数据库,它只是从缓存中获取它们。
其影响几乎不为人们所知,但可能在庞大的数据集上发挥更大的作用。
我还看过罗文·米勒( Rowan )的“EF核心”(EF Core )的这演示,我将在下一次发布时切换到它--在某些情况下,它比EF6快5-6倍。
希望这能帮到别人
https://stackoverflow.com/questions/48441674
复制相似问题