如何解决LINQ 或 Navigation Properties 命令检索 1 到多个数据
我正在寻求有关 LINQ SQL 查询的帮助。
我有一个从 Azure SQL 数据库获取数据的 blazor 应用程序。我正在寻求从数据库中获取数据集以链接到数据网格,其中每一行都是主表中的一条记录,与第二个表中的一条记录相连。第二张表有数百万条记录,需要加入一个key(securityId)相同的记录,日期为指定日期的记录,或者指定日期之前的最后一个日期。
由于第二个文件的大小,我需要一个有效的查询。目前我正在使用以下内容,但我相信必须有更有效的方法来做到这一点而没有滞后。也试过导航属性但无法工作
reviewdateS 是我希望第二条记录匹配的日期或该日期之前的最晚日期
result = (from cmpn in _ctx.MstarCompanies
join prcs in _ctx.MstarPrices
on cmpn.securityId equals prcs.securityId into cs
from c in cs.DefaultIfEmpty()
where c.date01 == reviewDateS
select new ClsMarketPrices { })
以下是 3 个相关的类。 ClsMarketPrices 与数据库表无关,它是一个简单的类,它结合了其他 2 个类,这可能不是必需的,但据我所知,它是如何工作的。
_ctx 是链接到数据上下文的存储库。
public MySQLRepositories(ApplicationDbContext ctx)
{
_ctx = ctx;
}
public class ClsMarket
{
[Key]
public int CompanyId { get; set; } = 0;
public string securityId { get; set; } = "";
public string companyName { get; set; } = "";
public string mic { get; set; } = "";
public string currency { get; set; } = "";
[ForeignKey("securityId")]
public virtual ICollection<ClsPrices> Prices { get; set; }
}
public class ClsMarketPrices
{
[Key]
public int CompanyId { get; set; } = 0;
public string companyName { get; set; } = "";
public string period { get; set; } = "";
public string mic { get; set; } = "";
}
public class ClsPrices
{
[Key]
public int PricesId { get; set; }
[ForeignKey("securityId")]
public string securityId { get; set; } = "";
public string mic { get; set; } = "";
public string date01 { get; set; } = "";
public virtual ClsMarket ClsMarket {get; set;}
}
解决方法
我想将第一个文件中的记录与第二个文件中的记录连接起来,其中第二个文件中的记录的日期等于或指定日期之前的最后一个。
所以我们谈论的是文件,而不是数据库!
这很重要,因为这意味着您的本地进程将执行 LINQ,而不是数据库管理系统。换句话说:LINQ 将是 IEnumerable,而不是 IQueryable。
这很重要,因为作为 Enumerable,您将能够定义自己的 LINQ 扩展方法。
虽然你提供了大量不相关的属性,但你忘了给我们最重要的事情:你在谈论两个文件,你告诉我们你有两个具有一对多关系的类,但你给了我们三个班。哪些和你说的有关系?
我认为 ClsMarketPrices
的每个对象都有零个或多个 ClsPrices
,并且每个 ClsPrice
都是 ClsMarketPrices
的价格之一,即 {{1 }} 外键 ClsMarketPrices
(相当混乱的名称)所指的。
首先,让我们假设您已经有了从文件中读取两个序列的过程。当然,这些过程不会读取超过需要的内容(因此,如果您只使用第一个 SecurityId
,请不要读取整个文件)。我假设您已经知道如何做到这一点:
ClsMarket
所以您选择了IEnumerable<ClsMarketPrices> ReadMarketPrices();
IEnumerable<ClsPrices> ReadPrices();
。每个 DateTime reviewDate
都有零个或多个 MarketPrice
。每个 Prices
都有一个 DateTime 属性 Price
。对于每个 DateStamp
,您希望 MarketPrice
的最大值小于或等于 reviewDate。
如果一个 Price
没有这样的 DateStamp
,例如因为它根本没有 MarketPrice
,或者它的所有 Prices
都有一个 { {1}} 大于 Price
,您需要一个 null 值。
如果一个 MarketPrice 有多个具有相同最大 DateStamp
直接的 LINQ 方法是使用 GroupJoin、Where、Orderby 和 FirstOrDefault:
Prices
问题是:这必须有效地完成,因为您有大量的 DateStamp
和 reviewDate
。
虽然我们已经通过删除 reviewDate 之后的价格来限制要排序的价格数量,但如果您只使用第一个日期,那么订购所有日期仍然是一种浪费。
我们可以通过使用 Aggregate for DateTime reviewDate = ...
IEnumerable<ClsMarketPrices> marketPricess = ReadMarketPrices();
IEnumerable<ClsPrices> prices = ReadPrices().Where(price => price.DateStamp <= reviewDate);
// GroupJoin marketPrices with prices:
var result = markets.GroupJoin(prices,marketPrice => marketPrice.CompanyId,// from every MarketPrice take the primary key
price => price.CompanyId,// from every price take the foreign key to its market
// parameter resultSelector: from every market,with its zero or more matching prices
// make one new:
(marketPrice,pricesOfThisMarketPrice) => new
{
// select the marketPrice properties that you plan to use:
Id = marketPrice.CompanyId,Name = ...
...
// from all prices of this marketPrice,take the one with the largest DateStamp
// we know there are no marketPrices with a DataStamp larger than reviewData
LatestPrice = pricesOfThisMarketPrice.OrderbyDescending(price => price.DateStamp)
.Select(price => new
{
// Select the price properties you plan to use;
Id = price.PricesId,Date = price.DateStamp,...
})
.FirstOrDefault(),});
来优化它。这将断言 Markets
将仅被枚举一次。
旁注:聚合仅适用于 IEnumerable,不适用于 IQueryable,因此它不适用于数据库。此外,MarketPrices
可能是一个空序列;我们必须解决这个问题。
pricesOfThisMarketPrice
尽管此 Aggregate 比 OrderBy 更有效,但您的第二个序列仍将被枚举不止一次。查看Enumerable.GroupJoin的源代码。
如果你真的想枚举你的第二个源一次,并限制第一个源的枚举次数,可以考虑创建一个扩展方法。这样您就可以将其用作任何 LINQ 方法。如果您不熟悉扩展方法,请参阅 extension methods demystified。
您可以为 ClsPrices 和 ClsPrice 创建扩展方法,但是,如果您认为需要更频繁地“查找属于另一个元素的最大元素”,为什么不创建一个泛型方法,就像 LINQ 那样。
下面我创建了最广泛的扩展方法,一个带有 resultSelector 和 equalityComparers。如果您将使用标准等式,请考虑添加一个没有这些比较器的扩展方法,并让该扩展方法调用另一个为比较器提供空值的扩展方法。
有关使用和不使用相等比较器的重载的示例,请参阅多个 LINQ 方法,例如 ToDictionary:有一种方法没有比较器,另一种方法有比较器。第一个调用第二个,比较器的值为空。
我将使用婴儿步骤,以便您了解会发生什么。 这可以稍微优化。 最重要的是,您将只枚举一次最大的集合。
pricesOfThisMarketPrice
实现很简单:
-
将所有的T1放入字典中,t1Key作为key,{T1,T2}作为value,keyComparer作为比较器
-
然后仅枚举一次 T2。
- 检查属性是否
- 如果是,则在字典中搜索具有相同键的{T1,T2}组合
- 检查当前 t2Item 是否大于 {T1,T2} 组合中的 T2
- 如果是:替换
我们需要一个内部类:
类字典值 { 公共 T1 T1 {get;放;} 公共 T2 T2 {get;放;} }
代码:
pricesOfThisMarketPrice
t2Sequence的枚举:
LatestPrice = pricesOfThisMarketPrice.Any() ?
pricesOfThisMarketPrice.Aggregate(
// select the one with the largest value of DateStamp:
(latestPrice,nextPrice) => nextPrice.DateStamp >= latesPrice.DateStamp) ? nextPrice : latestPrice)
// do not do the aggregate if there are no prices at all:
: null,
因此,对于每个 t1,我们都找到了具有属性
IEnumerable<TResult> TakeLargestItem<T1,T2,TKey,Tproperty,TResult>(
this IEnumerable<T1> t1Sequence,IEnumerable<T2> t2Sequence,// Select primary and foreign key:
Func<T1,TKey> t1KeySelector,Func<T2,TKey> t2KeySelector,// Select the property of T2 of which you want the largest element
Func<T2,TProperty> propertySelector,// The largest element must be <= propertyLimit:
TProperty propertyLimit,// From T1 and the largest T2 create one TResult
Func<T1,TResult> resultSelector,// equality comparer to compare equality of primary and foreign key
IEqualityComparer<TKey> keyComparer,// comparer to find the largest property value
IComparer<TProperty> propertyComparer)
{
// TODO: invent a property method name
// TODO: decide what to do if null input
// if no comparers provided,use the default comparers:
if (keyComparer == null) keyComparer = EqualityComparer<TKey>.Default;
if (propertyComparer == null) propertyComparer = Comparer<TProperty>.Default;
// TODO: implement
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。