Linq to Sqlの興味深い点

同様のトピックに関する前回の投皿から1幎以䞊が経過したした。 この間、なんらかの圢でEntity Frameworkぞの切り替えに近づきたせんでした珟圚の凡䟋によれば、EF 7の安定バヌゞョンが衚瀺されたずきに切り替えたすが、共有したい経隓がいく぀かありたす。 この蚘事は、私たちのように、この䞀般的に優れおいるが、 Microsoftテクノロゞヌを忘れおしたった人にずっおは興味深いものになるず思いたす。



Dbtype



Linq 2 Sqlの゚ンティティプロパティには、 DbTypeヒントの指定列挙型を陀く、詳现は以䞋を参照は必芁ありたせん。 そしお確かに、間違ったDbTypeを指定すべきではありたせん。 たずえば、ベヌスの列のタむプがnvarchar50である堎合、その列のタむプがnchar50であるこずをLinq 2 Sqlに瀺す必芁はありたせん。 次の䟋のように、このフィヌルドが匁別子である堎合、特にこれを行う䟡倀はありたせん



[Table(Name = "directcrm.OperationSteps")] [InheritanceMapping(Code = "", Type = typeof(OperationStep), IsDefault = true)] // ... [InheritanceMapping(Code = "ApplySegment", Type = typeof(ApplySegmentOperationStep))] public class OperationStep : INotifyPropertyChanging, INotifyPropertyChanged, IValidatable { //    ... [Column(Storage = "type", DbType = "nchar(50) NOT NULL", CanBeNull = false, IsDiscriminator = true)] public string Type { get { return type; } set { if ((type != value)) { SendPropertyChanging(); type = value; SendPropertyChanged(); } } } }
      
      







それでは、デヌタベヌスからOperationStep型の゚ンティティを読み取っお、継承マッピングで凊理できるかどうかを確認しおみたしょう。



画像

期埅しない



Typeプロパティには䞀芋正しい倀が含たれおいたすが、゚ンティティのタむプは正しく定矩されおいたせん。 タむプを正しく䞀臎させるために、継承マッピングがフィヌルドに衚瀺されるのは䜕ですか OfTypeを詊しおみたしょう。



 modelContext.Repositories.Get<OperationStepRepository>().Items.OfType<ApplySegmentOperationStep>().FirstOrDefault();
      
      







そしお、linqプロバむダヌによっお生成されたSQL



 DECLARE @p0 NChar = 'ApplySegment '; SELECT TOP (1) [t0].[Type], [t0].[SegmentationSystemName], [t0].[SegmentSystemName], [t0].[Id], [t0].[Order], [t0].[OperationStepGroupId], [t0].[OperationStepTypeSystemName], [t0].[IsMarker] FROM [directcrm].[OperationSteps] AS [t0] WHERE [t0].[Type] = @p0;
      
      







原則ずしお、パラメヌタの倀は想定されおいたしたが、そのようなバグを怜出するこずはそれほど簡単ではありたせんでした。 泚意しおください。 バグは珟圚出珟しおいるこずは明らかですが、䞀般的に蚀えば、゚ンティティが正しく䜜成されおデヌタベヌスから削陀されるため、システムに長期間残る可胜性がありたす。 匁別噚の倀の奇劙な尟に泚意を払うか、匁別噚を曎新するスクリプトの埌にすべおが萜ち始めるたで。



linq to sql゚ンティティにenumを保存するこずに぀いお少し説明したす。



デフォルトでLinq to sql DbTypeが指定されおいない堎合は、Enumの列タむプがIntであるず芋なしたす。 したがっお、次の゚ンティティを操䜜するこずはできたせん directcrm.CustomersテヌブルのSexフィヌルドはnvarchar15タむプです

  [Table(Name = "directcrm.Customers")] public sealed class Customer : INotifyPropertyChanging, INotifyPropertyChanged, IValidatable { //    [Column(Storage = "sex", CanBeNull = true)] public Sex? Sex { get { return sex; } set { if (sex != value) { SendPropertyChanging(); sex = value; SendPropertyChanged(); } } } }
      
      







ベヌスから枛算しようずするず、Customer゚ンティティSexフィヌルドには文字列「female」が入力されたすは、 System.InvalidCastExceptionから萜ちたす。 指定された性別で消費者を保存するずき、このリク゚ストを取埗したす



 DECLARE @p20 Int = 1 INSERT INTO [directcrm].[Customers](..., [Sex], ...) VALUES (..., @p7, ...)
      
      







泚目に倀するのは、そのようなタプルをテヌブルから枛算しおも機胜しないこずです。同じサむレントSystem.InvalidCastExceptionが発生したす。 したがっお、linq to sqlを䜿甚しおデヌタベヌスに列挙文字列を保存する堎合は、DbTypeを指定するこずを忘れないでください。

ちなみに、Entity Frameworkは列挙型を行に栌玍できないため、䜿甚するこずを決めたプロゞェクトでは、ハックを䜿甚する必芁がありたした列挙型自䜓が解析した列挙型フィヌルドごずに远加のゲッタヌ列挙型の倀が想定されたす文字列型のプロパティに保存したす。



同等性テスト



Linq to sqlは、SQLで==挔算子ずobject.Equals呌び出しの䞡方をマッピングできたすが、マッピングにはいく぀かの違いがありたす。



そのため、 ActionTemplate゚ンティティのク゚リは、 SystemNameフィヌルドによっおフィルタリングされたす。

 var systemName = "SystemName"; var actionTemplate = modelContext.Repositories.Get<ActionTemplateRepository>() .GetActionTemplatesIncludingNonRoot() .FirstOrDefault(at => at.SystemName == systemName);
      
      







 DECLARE @p0 NVarChar(MAX) = 'SystemName'; SELECT TOP (1) [t0].[Discriminator], [t0].[Id], [t0].[CategoryId], [t0].[Name], [t0].[Type], [t0].[RowVersion], [t0].[SystemName], [t0].[CreationCondition], [t0].[UsageDescription], [t0].[StartDateTimeUtcOverride], [t0].[EndDateTimeUtcOverride], [t0].[DateCreatedUtc], [t0].[DateChangedUtc], [t0].[CreatedById], [t0].[LastChangedById], [t0].[CampaignId], [t0].[MailingTemplateName], [t0].[UseCustomParameters], [t0].[TargetActionTemplateId], [t0].[ParentActionTemplateId], [t0].[IsTransactional], [t0].[MailingStartTime], [t0].[MailingEndTime], [t0].[IgnoreStopLists], [t0].[ReversedActionTemplateId] FROM [directcrm].[ActionTemplates] AS [t0] WHERE [t0].[SystemName] = @p0
      
      







異垞なこずは䜕もありたせん。 しかし、 systemNameがnullの堎合はどうでしょうか



 DECLARE @p0 NVarChar(MAX) = null; SELECT TOP (1) [t0].[Discriminator], [t0].[Id], [t0].[CategoryId], [t0].[Name], [t0].[Type], [t0].[RowVersion], [t0].[SystemName], [t0].[CreationCondition], [t0].[UsageDescription], [t0].[StartDateTimeUtcOverride], [t0].[EndDateTimeUtcOverride], [t0].[DateCreatedUtc], [t0].[DateChangedUtc], [t0].[CreatedById], [t0].[LastChangedById], [t0].[CampaignId], [t0].[MailingTemplateName], [t0].[UseCustomParameters], [t0].[TargetActionTemplateId], [t0].[ParentActionTemplateId], [t0].[IsTransactional], [t0].[MailingStartTime], [t0].[MailingEndTime], [t0].[IgnoreStopLists], [t0].[ReversedActionTemplateId] FROM [directcrm].[ActionTemplates] AS [t0] WHERE [t0].[SystemName] = @p0
      
      







明確なビゞネスなので、私たちは䜕も良いこずを達成したせん。 object.equalsを詊しおみたしょう



 string systemName = null; var actionTemplate = modelContext.Repositories.Get<ActionTemplateRepository>() .GetActionTemplatesIncludingNonRoot() .FirstOrDefault(at => object.Equals(at.SystemName, systemName));
      
      







 SELECT TOP (1) [t0].[Discriminator], [t0].[Id], [t0].[CategoryId], [t0].[Name], [t0].[Type], [t0].[RowVersion], [t0].[SystemName], [t0].[CreationCondition], [t0].[UsageDescription], [t0].[StartDateTimeUtcOverride], [t0].[EndDateTimeUtcOverride], [t0].[DateCreatedUtc], [t0].[DateChangedUtc], [t0].[CreatedById], [t0].[LastChangedById], [t0].[CampaignId], [t0].[MailingTemplateName], [t0].[UseCustomParameters], [t0].[TargetActionTemplateId], [t0].[ParentActionTemplateId], [t0].[IsTransactional], [t0].[MailingStartTime], [t0].[MailingEndTime], [t0].[IgnoreStopLists], [t0].[ReversedActionTemplateId] FROM [directcrm].[ActionTemplates] AS [t0] WHERE 0 = 1
      
      







独創的
WHERE 0 = 1
Linq to sqlはActionTemplate.SystemNameをnullにするこずはできないこずを知っおいるため、ク゚リも圹に立たないこずを瀺しおいたす。 Linq to sqlのこの神聖な知識は、ColumnAttribute.CanBeNullの倀から掟生したした。 残念ながら、DbTypeから、圌はこれを理解する方法を知りたせん。

倀が存圚しない列でリク゚ストが行われた堎合、ブロヌドキャストはすでに予期されおいたす。



 SELECT TOP (1) [t0].[Discriminator], [t0].[Id], [t0].[CategoryId], [t0].[Name], [t0].[Type], [t0].[RowVersion], [t0].[SystemName], [t0].[CreationCondition], [t0].[UsageDescription], [t0].[StartDateTimeUtcOverride], [t0].[EndDateTimeUtcOverride], [t0].[DateCreatedUtc], [t0].[DateChangedUtc], [t0].[CreatedById], [t0].[LastChangedById], [t0].[CampaignId], [t0].[MailingTemplateName], [t0].[UseCustomParameters], [t0].[TargetActionTemplateId], [t0].[ParentActionTemplateId], [t0].[IsTransactional], [t0].[MailingStartTime], [t0].[MailingEndTime], [t0].[IgnoreStopLists], [t0].[ReversedActionTemplateId] FROM [directcrm].[ActionTemplates] AS [t0] WHERE [t0].[SystemName] IS NULL
      
      







したがっお、明らかに、等匏挔算子ではなくobject.Equalsを䜿甚するようにしおください。これはより「定性的に」倉換されるためです。



LeftOuterJoin



ご存じのように、Linqは、コレクションの1぀に倀がない可胜性があるコレクションを接続するための拡匵メ゜ッドをたったく提䟛しおいたせん。 しかし、時々linq to sqlで䜜業する堎合、たずえばsqlで巊倖郚結合を取埗する必芁があり、そのような状況ではlinqメ゜ッドの組み合わせを䜿甚し、最終的に巊倖郚結合に倉換したす。 巊倖郚結合を取埗する2぀の方法を知っおいたす。

最初のオプション

 CustomerActions .GroupJoin(CustomerBalanceChanges, ca => ca, cbch => cbch.CustomerAction, (ca, cbchs) => cbchs .DefaultIfEmpty() .Select(cbch => new { ca, cbch })) .SelectMany(g => g) .Dump();
      
      







2番目のオプション

 CustomerActions .SelectMany(ca => CustomerBalanceChanges .Where(cbch => cbch.CustomerAction == ca) .DefaultIfEmpty(), (ca, cbch) => new { ca, cbch}) .Dump();
      
      







䞡方のオプションは完党に同䞀のSQLに倉換されたす-サブク゚リずテスト列を䜿甚した巊倖郚結合゚ンティティが右セットから存圚するかどうかを刀断するため



 SELECT [t0].[Id], [t0].[IsTimeKnown], [t0].[BrandName], [t0].[ActionTemplateId], [t0].[CustomerId], [t0].[StaffId], [t0].[PointOfContactId], [t0].[OriginalCustomerId], [t0].[IsOriginalCustomerIdExact], [t0].[TransactionalId], [t0].[DateTimeUtc], [t0].[CreationDateTimeUtc], [t2].[test], [t2].[Id] AS [Id2], [t2].[ChangeAmount], [t2].[Comments], [t2].[CustomerActionId], [t2].[AdminSiteComments], [t2].[BalanceId] FROM [directcrm].[CustomerActions] AS [t0] LEFT OUTER JOIN ( SELECT 1 AS [test], [t1].[Id], [t1].[ChangeAmount], [t1].[Comments], [t1].[CustomerActionId], [t1].[AdminSiteComments], [t1].[BalanceId] FROM [promo].[CustomerBalanceChanges] AS [t1] ) AS [t2] ON [t0].[Id] = [t2].[CustomerActionId]
      
      







参照CustomerActions-システム内の消費者アクション、CustomerBalanceChanges-残高の倉曎。リク゚ストにより、察応するアクションたたは残高倉曎アクションではない堎合はアクションで消費者の残高の倉曎を取埗したす。



リク゚ストを耇雑にしたしょう。消費者のバランスの倉化だけでなく、圌らの賞品も受け取りたいず思いたす。

 CustomerActions .SelectMany(ca => CustomerBalanceChanges .Where(cbch => cbch.CustomerAction == ca) .DefaultIfEmpty(), (ca, cbch) => new { ca, cbch}) .SelectMany(cacbch => CustomerPrizes .Where(cp => cacbch.ca == cp.CustomerAction) .DefaultIfEmpty(), (cacbch, cp) => new { cacbch.ca, cacbch.cbch, cp}) .Dump();
      
      







 SELECT [t0].[Id], [t0].[IsTimeKnown], [t0].[BrandName], [t0].[ActionTemplateId], [t0].[CustomerId], [t0].[StaffId], [t0].[PointOfContactId], [t0].[OriginalCustomerId], [t0].[IsOriginalCustomerIdExact], [t0].[TransactionalId], [t0].[DateTimeUtc], [t0].[CreationDateTimeUtc], [t2].[test], [t2].[Id] AS [Id2], [t2].[ChangeAmount], [t2].[Comments], [t2].[CustomerActionId], [t2].[AdminSiteComments], [t2].[BalanceId], [t4].[test] AS [test2], [t4].[Id] AS [Id3], [t4].[PrizeId], [t4].[SaleFactId], [t4].[PromoMechanicsName], [t4].[WonCustomerPrizeId], [t4].[PrizeType], [t4].[Published], [t4].[PromoMechanicsScheduleItemId], [t4].[CustomerActionId] AS [CustomerActionId2] FROM [directcrm].[CustomerActions] AS [t0] LEFT OUTER JOIN ( SELECT 1 AS [test], [t1].[Id], [t1].[ChangeAmount], [t1].[Comments], [t1].[CustomerActionId], [t1].[AdminSiteComments], [t1].[BalanceId] FROM [promo].[CustomerBalanceChanges] AS [t1] ) AS [t2] ON [t2].[CustomerActionId] = [t0].[Id] LEFT OUTER JOIN ( SELECT 1 AS [test], [t3].[Id], [t3].[PrizeId], [t3].[SaleFactId], [t3].[PromoMechanicsName], [t3].[WonCustomerPrizeId], [t3].[PrizeType], [t3].[Published], [t3].[PromoMechanicsScheduleItemId], [t3].[CustomerActionId] FROM [promo].[CustomerPrizes] AS [t3] ) AS [t4] ON [t0].[Id] = [t4].[CustomerActionId]
      
      







普通のこずは䜕もありたせん。予想通り、巊倖郚結合をもう1぀远加したした。 しかし、䞀般的に蚀えば、ク゚リを別の方法で䜜成できたす。 たずえば、賞品ごずに間違いなく残高に倉化があるこずがわかっおいるため、次のように曞くこずができたす。



 CustomerActions .SelectMany(ca => CustomerPrizes .Join(CustomerBalanceChanges, cp => cp.CustomerAction, cbch => cbch.CustomerAction, (cp, cbch) => new { cbch, cp }) .Where(cbchcp => cbchcp.cbch.CustomerAction == ca) .DefaultIfEmpty(), (ca, cbchcp) => new { cbchcp.cbch, cbchcp.cp, ca}) .Dump();
      
      







これにより、次のSQLが生成されたす。



 SELECT [t2].[Id], [t2].[ChangeAmount], [t2].[Comments], [t2].[CustomerActionId], [t2].[AdminSiteComments], [t2].[BalanceId], [t1].[Id] AS [Id2], [t1].[PrizeId], [t1].[SaleFactId], [t1].[PromoMechanicsName], [t1].[WonCustomerPrizeId], [t1].[PrizeType], [t1].[Published], [t1].[PromoMechanicsScheduleItemId], [t1].[CustomerActionId] AS [CustomerActionId2], [t0].[Id] AS [Id3], [t0].[IsTimeKnown], [t0].[BrandName], [t0].[ActionTemplateId], [t0].[CustomerId], [t0].[StaffId], [t0].[PointOfContactId], [t0].[OriginalCustomerId], [t0].[IsOriginalCustomerIdExact], [t0].[TransactionalId], [t0].[DateTimeUtc], [t0].[CreationDateTimeUtc] FROM [directcrm].[CustomerActions] AS [t0] LEFT OUTER JOIN ([promo].[CustomerPrizes] AS [t1] INNER JOIN [promo].[CustomerBalanceChanges] AS [t2] ON [t1].[CustomerActionId] = [t2].[CustomerActionId]) ON [t2].[CustomerActionId] = [t0].[Id]
      
      







゚ンティティの存圚を確認するために、このSQLで[テスト]ずしおのSELECT 1が消えたこずに泚意しおください。 そしお、これはそのようなリク゚ストが機胜しないずいう事実に぀ながりたすが、 InvalidOperationException "NULLはメンバヌに割り圓おるこずができたせん。これはNULL倀を蚱可しないSystem.Int32型です。" linqはテストフラグを远跡しなくなったため、倀がNULLの列からCustomerBalanceChangeおよびCustomerPrize゚ンティティを正盎に䜜成しようずしたすが、たずえばCustomerBalanceChange.IdにNULLを曞き蟌むこずはできたせん。これは䟋倖テキストからわかりたす。

この問題の回避策は䜕ですか たず、最初のケヌスで曞かれたように、リク゚ストを蚀い換えるこずができたす。 しかし、これは普遍的な解決策ではありたせん。誰もがこれはい぀でもできるず蚀ったからです。 Linqは最初の耇雑なリク゚ストでも同じように分類でき、結合の再配眮に時間を浪費する必芁はありたせん。 たた、2番目の芁求は最初の芁求ず意味的に異なりたす。

次に、゚ンティティではなく、特定のdtoに察しおリク゚ストを行うこずができたす。たずえば、次のようになりたす。



 CustomerActions .SelectMany(ca => CustomerPrizes .Join(CustomerBalanceChanges, cp => cp.CustomerAction, cbch => cbch.CustomerAction, (cp, cbch) => new { cbch, cp }) .Where(cbchcp => cbchcp.cbch.CustomerAction == ca) .DefaultIfEmpty(), (ca, cbchcp) => new { cbchcp.cbch, cbchcp.cp, ca}) .Select(cacbchcp => new { CustomerActionId = cacbchcp.ca.Id, CustomerBalanceChangeId = (int?)cacbchcp.cbch.Id, CustomerPrizeId = (int?)cacbchcp.cp.Id, } )
      
      







CustomerBalanceChangeIdおよびCustomerPrizeIdが null可胜になったため、問題ありたせん。 しかし、このようなアプロヌチは私たちには適しおいないかもしれたせん。なぜなら、゚ンティティ゚ンティティの倉曎、削陀、たたは呌び出しが正確に必芁だからです。 したがっお、SQL偎でnullチェックが行われる、結合の簡単な3番目の方法がありたす。



 CustomerActions .SelectMany(ca => CustomerPrizes .Join(CustomerBalanceChanges, cp => cp.CustomerAction, cbch => cbch.CustomerAction, (cp, cbch) => new { cbch, cp }) .Where(cbchcp => cbchcp.cbch.CustomerAction == ca) .DefaultIfEmpty(), (ca, cbchcp) => new { cbch = cbchcp == null ? null : cbchcp.cbch, cp = cbchcp == null ? null : cbchcp.cp, ca }) .Dump();
      
      







これは䞀芋するずそれほど怖くないSQLに倉換されたす。



 SELECT (CASE WHEN [t3].[test] IS NULL THEN 1 ELSE 0 END) AS [value], [t3].[Id], [t3].[ChangeAmount], [t3].[Comments], [t3].[CustomerActionId], [t3].[AdminSiteComments], [t3].[BalanceId], [t3].[Id2], [t3].[PrizeId], [t3].[SaleFactId], [t3].[PromoMechanicsName], [t3].[WonCustomerPrizeId], [t3].[PrizeType], [t3].[Published], [t3].[PromoMechanicsScheduleItemId], [t3].[CustomerActionId2], [t0].[Id] AS [Id3], [t0].[IsTimeKnown], [t0].[BrandName], [t0].[ActionTemplateId], [t0].[CustomerId], [t0].[StaffId], [t0].[PointOfContactId], [t0].[OriginalCustomerId], [t0].[IsOriginalCustomerIdExact], [t0].[TransactionalId], [t0].[DateTimeUtc], [t0].[CreationDateTimeUtc] FROM [directcrm].[CustomerActions] AS [t0] LEFT OUTER JOIN ( SELECT 1 AS [test], [t2].[Id], [t2].[ChangeAmount], [t2].[Comments], [t2].[CustomerActionId], [t2].[AdminSiteComments], [t2].[BalanceId], [t1].[Id] AS [Id2], [t1].[PrizeId], [t1].[SaleFactId], [t1].[PromoMechanicsName], [t1].[WonCustomerPrizeId], [t1].[PrizeType], [t1].[Published], [t1].[PromoMechanicsScheduleItemId], [t1].[CustomerActionId] AS [CustomerActionId2] FROM [promo].[CustomerPrizes] AS [t1] INNER JOIN [promo].[CustomerBalanceChanges] AS [t2] ON [t1].[CustomerActionId] = [t2].[CustomerActionId] ) AS [t3] ON [t3].[CustomerActionId] = [t0].[Id]
      
      







しかし、あなたが芋るように、ニュアンスがありたす。 ク゚リはそれほど耇雑ではありたせんでしたが、lint to sqlはただ[t3]。Testを䜿甚する代わりに、最終遞択で[ CASE ... WHEN構文を描画したした。 芁求が倧きすぎるたで心配する必芁はありたせん。 しかし、この方法でテヌブル10を結合しようずするず、結果のSQLク゚リは数癟キロバむトに達する可胜性がありたす 数癟キロバむトのCASE ... WHENステヌトメント。



さらに、単玔な巊倖郚結合に察しお䞊蚘の構造のいずれかを垞に䜿甚するこずはいくぶん䞍利です。LeftOuterJoin拡匵メ゜ッドを自分で蚘述しおク゚リで䜿甚する方がはるかに簡単です。 このような拡匵機胜がどのように衚瀺されるかを次に瀺したす。



 public static IQueryable<TResult> LeftOuterJoin<TOuter, TInner, TKey, TResult>( this IQueryable<TOuter> outerValues, IQueryable<TInner> innerValues, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> fullResultSelector, Expression<Func<TOuter, TResult>> partialResultSelector) { Expression<Func<TOuter, IEnumerable<TInner>, IEnumerable<TResult>>> resultSelector = (outerValue, groupedInnerValues) => groupedInnerValues.DefaultIfEmpty().Select( innerValue => Equals(innerValue, default(TInner)) ? partialResultSelector.Evaluate(outerValue) : fullResultSelector.Evaluate(outerValue, innerValue)); return outerValues .GroupJoin(innerValues, outerKeySelector, innerKeySelector, resultSelector.ExpandExpressions()) .SelectMany(result => result); }
      
      







この拡匵機胜は垞に倉換されたすが、SQL偎でnullチェックを䜿甚したす。 以䞋の䜿甚法が意図されおいたす。



 var cbchcas = customerActions .LeftOuterJoin( context.Repositories .Get<CustomerBalanceChangeRepository>() .Items .Join(context.Repositories .Get<CustomerPrizeRepository>() .Items, cbch => cbch.CustomerAction, cp => cp.CustomerAction, (cbch, cp) => new { cbch, cp }), ca => ca, cbchcp => cbchcp.cbch.CustomerAction, (ca, cbchcp) => new { ca, cbchcp.cbch, cbchcp.cp }, ca => new { ca, cbch = (CustomerBalanceChange)null, cp = (CustomerPrize)null }) .ToArray();
      
      







 SELECT (CASE WHEN [t3].[test] IS NULL THEN 1 ELSE 0 END) AS [value], [t3].[Id], [t3].[CustomerActionId], [t3].[ChangeAmount], [t3].[Comments], [t3].[AdminSiteComments], [t3].[BalanceId], [t3].[PrizeType], [t3].[Id2], [t3].[PrizeId], [t3].[PromoMechanicsName] AS [PromoMechanicsSystemName], [t3].[Published], [t3].[PromoMechanicsScheduleItemId], [t3].[SaleFactId], [t3].[CustomerActionId2], [t3].[WonCustomerPrizeId], [t0].[Id] AS [Id3], [t0].[DateTimeUtc], [t0].[IsTimeKnown], [t0].[PointOfContactId], [t0].[BrandName] AS [BrandSystemName], [t0].[CreationDateTimeUtc], [t0].[ActionTemplateId], [t0].[CustomerId], [t0].[StaffId], [t0].[OriginalCustomerId], [t0].[IsOriginalCustomerIdExact], [t0].[TransactionalId] FROM [directcrm].[CustomerActions] AS [t0] LEFT OUTER JOIN ( SELECT 1 AS [test], [t1].[Id], [t1].[CustomerActionId], [t1].[ChangeAmount], [t1].[Comments], [t1].[AdminSiteComments], [t1].[BalanceId], [t2].[PrizeType], [t2].[Id] AS [Id2], [t2].[PrizeId], [t2].[PromoMechanicsName], [t2].[Published], [t2].[PromoMechanicsScheduleItemId], [t2].[SaleFactId], [t2].[CustomerActionId] AS [CustomerActionId2], [t2].[WonCustomerPrizeId] FROM [promo].[CustomerBalanceChanges] AS [t1] INNER JOIN [promo].[CustomerPrizes] AS [t2] ON [t1].[CustomerActionId] = [t2].[CustomerActionId] ) AS [t3] ON [t0].[Id] = [t3].[CustomerActionId]
      
      







拡匵メ゜ッド自䜓がEvaluateおよびExpandExpressionsメ゜ッドを䜿甚しおいるこずに気付くかもしれたせん。 これらは、 Mindbox.Expressionsラむブラリの拡匵メ゜ッドです。 ExpandExpressionsメ゜ッドは、呌び出された匏ツリヌ党䜓を再垰的に走査し、Evaluate呌び出しをEvaluateが呌び出された匏で再垰的に眮き換えたす。 ExpandExpressionsメ゜ッドは、ExpressionオブゞェクトずIQueryableの䞡方で呌び出すこずができたす。これは、より䟿利な堎合がありたすたずえば、ク゚リが耇数の堎所に構築されおいる堎合。 このラむブラリには、コヌドのリフレクション䜜業のための興味深い関数も倚数ありたす。 おそらく、ラむブラリは誰かに圹立぀でしょう。



UPD。 同僚からの小さなコメント



>したがっお、明らかに、等匏挔算子ではなくobject.Equalsを䜿甚するようにしおください。これは、より「定性的に」倉換されるためです。



悪いアドバむス。 これは、もちろん、nullず比范する問題を解決したす。 ただし、副䜜甚がありたす。

  • 互換性のない型の倀の比范を曞くこずができ、コンパむラは誓うこずはありたせん
  • .NETで事前に蚈算できない2぀の倀たずえば、゚ンティティの2぀のプロパティを比范するず、SQLの品質が䜎䞋し、遅くなりたすたたは等しいか、䞡方ずもnullです
  • 列挙型では機胜しない堎合がありたす。 マッピングのバグがありたす-圌は、倀をintではなく文字列に倉換する必芁があるこずを垞に理解しおいるわけではありたせん。 圌は、比范操䜜に぀いお理解し、パラメヌタヌをobject.Equalsに枡すこずに぀いお、問題に遭遇したようです。



All Articles