【发布时间】:2021-04-26 21:54:32
【问题描述】:
我需要使用 EF6 从 SQL Server 数据库中获取大量记录。它需要很多时间的问题。主要问题是名为Series 的实体,其中包含Measurements。它们大约有 250K,每个都有 2 个嵌套实体,称为 FrontDropPhoto 和 SideDropPhoto。
[Table("Series")]
public class DbSeries
{
[Key] public Guid SeriesId { get; set; }
public List<DbMeasurement> MeasurementsSeries { get; set; }
}
[Table("Measurements")]
public class DbMeasurement
{
[Key] public Guid MeasurementId { get; set; }
public Guid CurrentSeriesId { get; set; }
public DbSeries CurrentSeries { get; set; }
public Guid? SideDropPhotoId { get; set; }
[ForeignKey("SideDropPhotoId")]
public virtual DbDropPhoto SideDropPhoto { get; set; }
public Guid? FrontDropPhotoId { get; set; }
[ForeignKey("FrontDropPhotoId")]
public virtual DbDropPhoto FrontDropPhoto { get; set; }
}
[Table("DropPhotos")]
public class DbDropPhoto
{
[Key] public Guid PhotoId { get; set; }
}
我已经写了这样的 fetch 方法(为了清楚起见,省略了大部分属性):
public async Task<List<DbSeries>> GetSeriesByUserId(Guid dbUserId)
{
using (var context = new DDropContext())
{
try
{
var loadedSeries = await context.Series
.Where(x => x.CurrentUserId == dbUserId)
.Select(x => new
{
x.SeriesId,
}).ToListAsync();
var dbSeries = new List<DbSeries>();
foreach (var series in loadedSeries)
{
var seriesToAdd = new DbSeries
{
SeriesId = series.SeriesId,
};
seriesToAdd.MeasurementsSeries = await GetMeasurements(seriesToAdd);
dbSeries.Add(seriesToAdd);
}
return dbSeries;
}
catch (SqlException e)
{
throw new TimeoutException(e.Message, e);
}
}
}
public async Task<List<DbMeasurement>> GetMeasurements(DbSeries series)
{
using (var context = new DDropContext())
{
var measurementForSeries = await context.Measurements.Where(x => x.CurrentSeriesId == series.SeriesId)
.Select(x => new
{
x.CurrentSeries,
x.CurrentSeriesId,
x.MeasurementId,
})
.ToListAsync();
var dbMeasurementsForAdd = new List<DbMeasurement>();
foreach (var measurement in measurementForSeries)
{
var measurementToAdd = new DbMeasurement
{
CurrentSeries = series,
MeasurementId = measurement.MeasurementId,
FrontDropPhotoId = measurement.FrontDropPhotoId,
FrontDropPhoto = measurement.FrontDropPhotoId.HasValue
? await GetDbDropPhotoById(measurement.FrontDropPhotoId.Value)
: null,
SideDropPhotoId = measurement.SideDropPhotoId,
SideDropPhoto = measurement.SideDropPhotoId.HasValue
? await GetDbDropPhotoById(measurement.SideDropPhotoId.Value)
: null,
};
dbMeasurementsForAdd.Add(measurementToAdd);
}
return dbMeasurementsForAdd;
}
}
private async Task<DbDropPhoto> GetDbDropPhotoById(Guid photoId)
{
using (var context = new DDropContext())
{
var dropPhoto = await context.DropPhotos
.Where(x => x.PhotoId == photoId)
.Select(x => new
{
x.PhotoId,
}).FirstOrDefaultAsync();
if (dropPhoto == null)
{
return null;
}
var dbDropPhoto = new DbDropPhoto
{
PhotoId = dropPhoto.PhotoId,
};
return dbDropPhoto;
}
}
通过 FluentAPI 配置的关系:
modelBuilder.Entity<DbSeries>()
.HasMany(s => s.MeasurementsSeries)
.WithRequired(g => g.CurrentSeries)
.HasForeignKey(s => s.CurrentSeriesId)
.WillCascadeOnDelete();
modelBuilder.Entity<DbMeasurement>()
.HasOptional(c => c.FrontDropPhoto)
.WithMany()
.HasForeignKey(s => s.FrontDropPhotoId);
modelBuilder.Entity<DbMeasurement>()
.HasOptional(c => c.SideDropPhoto)
.WithMany()
.HasForeignKey(s => s.SideDropPhotoId);
我需要所有这些数据来填充 WPF DataGrid。显而易见的解决方案是向此 DataGrid 添加分页。这个解决方案很诱人,但它会严重破坏我的应用程序的逻辑。我想在运行时使用这些数据创建图,所以我需要所有这些,而不仅仅是某些部分。我试图通过使每种方法都使用异步等待来对其进行一些优化,但这并没有足够的帮助。我已经尝试添加
.Configuration.AutoDetectChangesEnabled = false;
对于每个上下文,但加载时间仍然很长。如何解决这个问题?
【问题讨论】:
-
对代码的表面级修改不会神奇地让它变得更快。但是,您可能会幸运地将其转换为原始 SQL。
-
如果您并不真正需要 EF 围绕该任务的记录构建的所有基础架构,也许您应该将旧的 bare metal 方法与 SP 和经典 ADO.NET 一起使用.或者你可以尝试Raw Sql Feature 如果没有
-
是将它加载到 DataGrid 还是从数据库中检索它所花费的时间?如果是前者,那么分页可能是您可以做出的唯一真正的改进(假设虚拟化正在工作)。如果是后者,您可以尝试自己编写 SQL 查询并像这样手动填充您的类。
-
(1) 设置 dbContext Database.Log 属性并检查 EF 生成的 SQL。 (2) 确保所有外键都有索引。
标签: c# entity-framework ef-fluent-api