我有以下实体(我将显示正在使用的属性,因为我不想使其超出所需的大小):
属性::其中一个属性可以是另一个属性的子项,并且与GeoLocation
具有1-1关系,并且可以具有多个Multimedia
和Operation
< / p>
public partial class Property
{
public Property()
{
InverseParent = new HashSet<Property>();
Multimedia = new HashSet<Multimedia>();
Operation = new HashSet<Operation>();
}
public long Id { get; set; }
public string GeneratedTitle { get; set; }
public string Url { get; set; }
public DateTime? DatePublished { get; set; }
public byte StatusCode { get; set; }
public byte Domain { get; set; }
public long? ParentId { get; set; }
public virtual Property Parent { get; set; }
public virtual GeoLocation GeoLocation { get; set; }
public virtual ICollection<Property> InverseParent { get; set; }
public virtual ICollection<Multimedia> Multimedia { get; set; }
public virtual ICollection<Operation> Operation { get; set; }
}
地理定位:如上所述,它与Property
public partial class GeoLocation
{
public int Id { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public long? PropertyId { get; set; }
public virtual Property Property { get; set; }
}
多媒体:它可以为一个Property
容纳多个大小不同的图像。此处的详细信息是Order
指定要在客户端应用程序中显示的图像的顺序,但并非总是以1开头。在某些情况下,Property
具有{{1 }}以3或x开头的文件。
Multimedia
OPERATIONS::定义public partial class Multimedia
{
public long Id { get; set; }
public long? Order { get; set; }
public string Resize360x266 { get; set; }
public long? PropertyId { get; set; }
public virtual Property Property { get; set; }
}
可以进行的所有操作,并使用Property
将该操作命名。 (租金,出售等)
OperationType
价格:定义每个操作的价格和货币类型。 (即:对于X金额的美元货币,物业可以有租金选项-public partial class Operation
{
public Operation()
{
Price = new HashSet<Price>();
}
public long Id { get; set; }
public long? OperationTypeId { get; set; }
public long? PropertyId { get; set; }
public virtual OperationType OperationType { get; set; }
public virtual Property Property { get; set; }
public virtual ICollection<Price> Price { get; set; }
}
public partial class OperationType
{
public OperationType()
{
Operation = new HashSet<Operation>();
}
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Operation> Operation { get; set; }
}
-但如果使用其他货币类型,则可以为同一Operation
注册另一个价格)
Operation
说,我想获取所有记录(实际上大约是40K-50K),但仅用于少数几个属性。如前所述,public partial class Price
{
public long Id { get; set; }
public float? Amount { get; set; }
public string CurrencyCode { get; set; }
public long? OperationId { get; set; }
public virtual Operation Operation { get; set; }
}
表可以为每个Multimedia
保留很多记录,但是我只需要第一个具有较小Property
值并按Order
排序的记录。之后,我需要将结果转换为MapMarker对象,如下所示:
DatePublished
为了实现这一目标,我做了以下事情:
public class MapMarker : EstateBase
{
public long Price { get; set; }
public int Category { get; set; }
public List<Tuple<string, string, string>> Prices { get; set; }
}
,但是结果要花费14分钟以上,因此一分钟内可以多次调用此方法。我想对其进行优化,以便在更短的时间内返回结果。我曾尝试删除public async Task<IEnumerable<MapMarker>> GetGeolocatedPropertiesAsync(int quantity)
{
var properties = await GetAllProperties().AsNoTracking()
.Include(g => g.GeoLocation)
.Include(m => m.Multimedia)
.Include(p => p.Operation).ThenInclude(o => o.Price)
.Include(p => p.Operation).ThenInclude(o => o.OperationType)
.Where(p => p.GeoLocation != null
&& !string.IsNullOrEmpty(p.GeoLocation.Address)
&& p.GeoLocation.Longitude != null
&& p.GeoLocation.Latitude != null
&& p.StatusCode == (byte)StatusCode.Online
&& p.Operation.Count > 0)
.OrderByDescending(p => p.ModificationDate)
.Take(quantity)
.Select(p => new {
p.Id,
p.Url,
p.GeneratedTitle,
p.GeoLocation.Address,
p.GeoLocation.Latitude,
p.GeoLocation.Longitude,
p.Domain,
p.Operation,
p.Multimedia.OrderBy(m => m.Order).FirstOrDefault().Resize360x266
})
.ToListAsync();
var mapMarkers = new List<MapMarker>();
try
{
foreach (var property in properties)
{
var mapMarker = new MapMarker();
mapMarker.Id = property.Id.ToString();
mapMarker.Url = property.Url;
mapMarker.Title = property.GeneratedTitle ?? string.Empty;
mapMarker.Address = property.Address ?? string.Empty;
mapMarker.Latitude = property.Latitude.ToString() ?? string.Empty;
mapMarker.Longitude = property.Longitude.ToString() ?? string.Empty;
mapMarker.Domain = ((Domain)Enum.ToObject(typeof(Domain), property.Domain)).ToString();
mapMarker.Image = property.Resize360x266 ?? string.Empty;
mapMarker.Prices = new List<Tuple<string, string, string>>();
foreach (var operation in property.Operation)
{
foreach (var price in operation.Price)
{
var singlePrice = new Tuple<string, string, string>(operation.OperationType.Name, price.CurrencyCode, price.Amount.ToString());
mapMarker.Prices.Add(singlePrice);
}
}
mapMarkers.Add(mapMarker);
}
}
catch (Exception ex)
{
throw;
}
return mapMarkers;
}
,但是在ToListAsync()
循环中也要花费很多时间,这很有意义。
那么,您认为我在这里能做什么? 预先感谢。
更新:
这是foreach
方法,我忘了包括这个方法。
GetAllProperties()
以及实体框架针对SQL Server进行的SQL查询:
private IQueryable<Property> GetAllProperties()
{
return _dbContext.Property.AsQueryable();
}
更新2:如@Igor所述,这是执行计划结果的链接: https://www.brentozar.com/pastetheplan/?id=BJNz9KdQI
答案 0 :(得分:1)
好,几件事应该有所帮助。 #1 .Include()
和.Select()
通常应互斥。
您正在选择:
p.Id,
p.Url,
p.GeneratedTitle,
p.GeoLocation.Address,
p.GeoLocation.Latitude,
p.GeoLocation.Longitude,
p.Domain,
p.Operation,
p.Multimedia.OrderBy(m => m.Order).FirstOrDefault().Resize360x266
,但是随后在您的foreach循环中访问Price和OperationType实体。
编辑更新了用于收集操作的示例。 (哇)
相反,我建议:
p.Id,
p.Url,
p.GeneratedTitle,
p.GeoLocation.Address,
p.GeoLocation.Latitude,
p.GeoLocation.Longitude,
p.Domain,
Operations = p.Operation.Select( o => new
{
OperationTypeName = o.OperationType.Name,
o.Price.Amount,
o.Price.CurrencyCode
}).ToList(),
p.Multimedia.OrderBy(m => m.Order).FirstOrDefault().Resize360x266
然后调整您的foreach逻辑以使用返回的属性,而不是返回的实体和相关实体值。
使用类似的图像字段(MultiMedia)加载40-50k条记录可能总是有问题。为什么需要一次加载全部 50k?
这看起来会在地图上放置标记。这样的解决方案应该考虑至少应用半径过滤器,以使标记位于地图上给定中心点的合理半径内,或者如果加载较大区域(缩小地图)以计算区域并按区域过滤数据或获取计算该区域中的位置并以100个左右的批次批量加载/渲染位置,而不是潜在地等待 all 个位置加载。要考虑的事情。