2017-11-27 6 views
0

私はLinqをSQLに使用しており、新しいプロジェクトにEntity Frameworkを使用し始めました。私はVisual Studioに組み込む前にLinqPadを使って自分のコードをテストします。 VSでデバッグしている間、私のカウントに違いがあることに気付きました。 VSで自分のコードで作成したSQLを調べると、正しく翻訳されていないことに気付きました。エンティティフレームワークでC#がSQLに正しく変換されない

VSでの私のコード:

var adviceLineCallTotalViewModelList = 
    from a in db.AdviceLineCalls 
    .Include(a => a.Agency)  
    .Include(a => a.Staff) 
    .Include(a => a.StatusOfAdviceLineCaller) 
    .Include(a => a.AdviceLineCallSubjectMatter) 

    join ag in db.Agencies on a.AgencyNumber equals ag.AgencyNumber 
    join st in db.StatusOfAdviceLineCallers on a.StatusOfAdviceLineCallerID 
              equals st.StatusOfAdviceLineCallerID 
    join s in db.Staffs on a.StaffID equals s.StaffID 
    join sm in db.AdviceLineCallSubjectMatters on a.AdviceLineCallSubjectMatterID 
               equals sm.AdviceLineCallSubjectMatterID into grp 
    from sm in grp.DefaultIfEmpty() 
    where s.Employed == true 
    select new AdviceLineCallTotalViewModel() 
    { 
     AdviceLineCallID = a.AdviceLineCallID, 
     AdviceLineCallSubjectMatterID = sm.AdviceLineCallSubjectMatterID, 
     AdviceLineCallSubjectMatterDesc = sm.AdviceLineCallSubjectMatterDesc, 
     StatusOfAdviceLineCallerID = st.StatusOfAdviceLineCallerID, 
     StatusOfAdviceLineCallerDesc = st.StatusOfAdviceLineCallerDesc, 
     AgencyNumber = a.AgencyNumber, 
     AgencyNumberNameFacility = ag.AgencyNumberNameFacility, 
     CallDate = a.CallDate, 
     CallLength = a.CallLength, 
     Comments = a.Comments, 
     StaffID = a.StaffID, 
     LastName = s.LastName 
    }; 

私はデバッグし、生成されたSQLを見て、私は次を参照してください。

SELECT 
[Extent1].[AdviceLineCallID] AS [AdviceLineCallID], 
[Extent5].[AdviceLineCallSubjectMatterID] AS [AdviceLineCallSubjectMatterID], 
[Extent5].[AdviceLineCallSubjectMatterDesc] AS [AdviceLineCallSubjectMatterDesc], 
[Extent3].[StatusOfAdviceLineCallerID] AS [StatusOfAdviceLineCallerID], 
[Extent3].[StatusOfAdviceLineCallerDesc] AS [StatusOfAdviceLineCallerDesc], 
[Extent1].[AgencyNumber] AS [AgencyNumber], 
[Extent2].[AgencyNumberNameFacility] AS [AgencyNumberNameFacility], 
[Extent1].[CallDate] AS [CallDate], 
[Extent1].[CallLength] AS [CallLength], 
[Extent1].[Comments] AS [Comments], 
[Extent1].[StaffID] AS [StaffID], 
[Extent4].[LastName] AS [LastName] 
FROM  [dbo].[AdviceLineCall] AS [Extent1] 
INNER JOIN [dbo].[Agency] AS [Extent2] ON [Extent1].[AgencyNumber] = [Extent2].[AgencyNumber] 
INNER JOIN [dbo].[StatusOfAdviceLineCaller] AS [Extent3] ON [Extent1].[StatusOfAdviceLineCallerID] = [Extent3].[StatusOfAdviceLineCallerID] 
INNER JOIN [dbo].[Staff] AS [Extent4] ON [Extent1].[StaffID] = [Extent4].[StaffID] 
INNER JOIN [dbo].[AdviceLineCallSubjectMatter] AS [Extent5] ON [Extent1].[AdviceLineCallSubjectMatterID] = [Extent5].[AdviceLineCallSubjectMatterID] 
WHERE 1 = [Extent4].[Employed] 

最後の "INNER JOINを" "LEFT OUTER JOINの" あるべき次の行のために:

join sm in db.AdviceLineCallSubjectMatters on a.AdviceLineCallSubjectMatterID equals sm.AdviceLineCallSubjectMatterID into grp 
from sm in grp.DefaultIfEmpty() 

注:「LEFT OUTER JOIN」が含まれていない理由についての別の記事を読んだ後、「Include」ステートメントを含めました。私は "Includes"の有無にかかわらず同じ結果を得ます。

私はこれまで、他の簡単なクエリでDefaultIfEmpty()を使用していましたが、この問題は発生していませんでした。

EF初心者として、私が何か間違っているのか、私のプロジェクトのEFがどうやってどうやって壊れているのか分かりません。私はEF 6.2を使用しています。

EDIT:

私は新しいVisual Studioプロジェクトを作成し、次のコードを使用:

 var adviceLineCallTotalViewModelList = from a in db.AdviceLineCalls 
               join ag in db.Agencies on a.AgencyNumber equals ag.AgencyNumber 
               join st in db.StatusOfAdviceLineCallers on a.StatusOfAdviceLineCallerID equals st.StatusOfAdviceLineCallerID 
               join s in db.Staffs on a.StaffID equals s.StaffID 
               join sm in db.AdviceLineCallSubjectMatters on a.AdviceLineCallSubjectMatterID equals sm.AdviceLineCallSubjectMatterID into grp 
               from sm_left in grp.DefaultIfEmpty() 
               where s.Employed == true 
               select new AdviceLineCallTotalViewModel() 
               { 
                AdviceLineCallID = a.AdviceLineCallID, 
                AdviceLineCallSubjectMatterID = sm_left == null ? 0 : sm_left.AdviceLineCallSubjectMatterID, 
                AdviceLineCallSubjectMatterDesc = sm_left == null ? String.Empty : sm_left.AdviceLineCallSubjectMatterDesc, 
                StatusOfAdviceLineCallerID = st.StatusOfAdviceLineCallerID, 
                StatusOfAdviceLineCallerDesc = st.StatusOfAdviceLineCallerDesc, 
                AgencyNumber = a.AgencyNumber, 
                AgencyNumberNameFacility = ag.AgencyNumberNameFacility, 
                CallDate = a.CallDate, 
                CallLength = a.CallLength, 
                Comments = a.Comments, 
                StaffID = a.StaffID, 
                LastName = s.LastName 
               }; 

これは行(5104)の正しい数を取得します。また、正しく、最後はLEFT OUTERとして参加作成しますが、JOIN:

LEFT OUTER JOIN [dbo].[AdviceLineCallSubjectMatter] AS [Extent5] ON [Extent1].[AdviceLineCallSubjectMatterID] = [Extent5].[AdviceLineCallSubjectMatterID] 

しかし、私の現在のプロジェクト内のコードの同じ行は5つのレコードを返し、最後の結合が間違って登録しようINNERに翻訳されています。

現在のプロジェクトでEFなどが壊れていますか? MVCとEFの入門者として、私は何をすべきか分かりません。

+0

'join sm in db.AdviceLineCallSubjectMatters'とgrp.DefaultIfEmpty()のsm from weirdが奇妙に見えます。 2番目の 'sm in'を' sm_left'に変更して、 'AdviceLineCallSubjectMatterID = sm_left == null 'のようなselect参照をintに変更してみてください。 0:sm_left.AdviceLineCallSubjectMatterID' – bradbury9

+0

@ bradbury9あなたが提案した変更を加えました。私はまだ同じカウントを取得します。しかし、私がコメントすると: 'AdviceLineCallSubjectMatterDesc = sm.AdviceLineCallSubjectMatterDesc '、私は正しいカウントを取得します。注:私はスタックオーバーフローの新しさと私はこのコメントの私のコードの前後にバッククイックを正しく使用している "考える"。 「コメントを追加」をクリックすると、コードブロックとして表示されることがあります。 – MikeNaka

+0

私は確かに@ bradbury9が正しいです。結合と開始の両方のタグとして "sm"を使用すべきではありません。 1つを別のものに変更してから、あなたが使用しているタグのみを参照してください。例えば、 "sm"と "smgrp"。また、オブジェクトに割り当てる際にnullプロパティリファレンスを使用します(つまり、 "AdviceLineCallSubjectMatterID = smgrp?.AdviceLineCallSubjectMatterID ?? string.Empty")。 –

答えて

0

あなたAdviceLineCallエンティティはすでに関連企業のための宣言の参照(すなわちa.AdviceLineCallSubjectMatter)を持っているなら、あなたはあなたのコンテキストでDbSetsを宣言し、手動でクエリでこれらのテーブルを結合するない必要性を行います。 EFはこのすべてを、マップされた参照を介して処理します。

var adviceLineCallTotalViewModelList = db.AdviceLineCalls 
    .Where(a=> a.Staff.Employed) 
    .Select(a => new { 
    a.AdviceLineCallId, 
    a.SubjectMatter.AdviceLineCallSubjectMatterID, 
    a.SubjectMatter.AdviceLineCallSubjectMatterDesc, 
    a.StatusOfAdviceLineCaller.StatusOfAdviceLineCallerID, 
    a.StatusOfAdviceLineCaller.StatusOfAdviceLineCallerDesc, 
    a.Agency.AgencyNumber, 
    a.Agency.AgencyNumberNameFacility, 
    a.CallDate, 
    a.CallLength, 
    a.Comments, 
    a.Staff.StaffID, 
    a.Staff.LastName}) 
    .ToList() // I'd recommend using .Take() to limit the # of rows returned. 
    .Select(x => new AdviceLineCallTotalViewModel() 
    { 
     AdviceLineCallID = x.AdviceLineCallID, 
     AdviceLineCallSubjectMatterID = x.AdviceLineCallSubjectMatterID, 
     AdviceLineCallSubjectMatterDesc = x.AdviceLineCallSubjectMatterDesc, 
     StatusOfAdviceLineCallerID = x.StatusOfAdviceLineCallerID, 
     StatusOfAdviceLineCallerDesc = x.StatusOfAdviceLineCallerDesc, 
     AgencyNumber = x.AgencyNumber, 
     AgencyNumberNameFacility = x.AgencyNumberNameFacility, 
     CallDate = x.CallDate, 
     CallLength = x.CallLength, 
     Comments = x.Comments, 
     StaffID = x.StaffID, 
     LastName = x.LastName 
    }); 

最初の.Select()は、オブジェクトモデルから必要なフィールドだけを抽出します。あなたのエンティティがオプション(ヌル可能なFK)としてマップされている場合、適切な結合を自動的に構成します。 SubjectMatterのようなものが#nullの場合、その参照されたエンティティから要求された2つのフィールドはnull/defaultになります。 POCOオブジェクトを扱っているViewModelにそのデータを.Select()すると.ToList()または.Take()がポストされるので、ここにオプションの依存関係がない場合の対処方法についてのロジックを処理する必要があります。データをフィルタリングする場合は、Where()句でデータをフィルタリングして、関連するデータのみがSQLから返されるようにします。

1

変更

join sm in db.AdviceLineCallSubjectMatters ... 
from sm in grp.DefaultIfEmpty() ... 
.... 
select 
    AdviceLineCallSubjectMatterID = sm.AdviceLineCallSubjectMatterID 

.NET Frameworkのバージョンに応じて

join sm in db.AdviceLineCallSubjectMatters ... 
from sm_left in grp.DefaultIfEmpty() ... 
.... 
select 
    AdviceLineCallSubjectMatterID = sm_left == null? 0 : sm_left.AdviceLineCallSubjectMatterID 

にあなたはもっときれいな方法にチェックを選択ヌルを変更することができ

+0

これは動作し、動作しません。あなたの提案を試してみると、私のカウント= 5、これは私の元のコードと同じです。しかし、次の行をコメントアウトすると、 'AdviceLineCallSubjectMatterDesc = sm_left.AdviceLineCallSubjectMatterDesc == null? String.Empty:sm_left.AdviceLineCallSubjectMatterDesc、 'それから、私のCount = 5,104が正しいです。私はまた試みました: 'AdviceLineCallSubjectMatterDesc = sm_left == null? String.Empty:sm_left.AdviceLineCallSubjectMatterDesc、 '私は 'AdviceLineCallSubjectMatterDesc'を選択から外し、別のコードブロックを使用することができます。 foreach、それを設定する。 – MikeNaka

+1

あなたが 'sm'を選択したままにすると、左の結合を暗黙的に通常の内部結合に変換することができます。 'select * from tableAを作るのと同じようなものが、a.Id = b.Id where b.Id = X'にtableB joinBを残して、なぜそれが通常のinne joinとして動作するのか不思議です。 – bradbury9

0

(@Jacob Proffittのコメントをご確認ください)私は問題の原因と、なぜ同じC#コードがSQLに別々に変換されているのかを知り、それはすべて必須データアノテーションと関係していました。

表AdviceLineCallSubjectMatterは、DBに新たに追加されたものです。したがって、新しいAdviceLineCallレコードだけがAdviceLineCallSubjectMatterIDを持つため、null可能なintにしました。

「必須」のAdviceLineCallフィールドと新しいintがありますか? AdviceLineCallSubjectMatterIDが、AdviceLineCall ModelクラスにRequiredデータアノテーションで追加されました。

public partial class AdviceLineCall 
{ 
    . 
    . 
    . 
    [Required(ErrorMessage = "Subject Matter is required")] 
    [DisplayName("Subject Matter")] 
    public int? AdviceLineCallSubjectMatterID { get; set; } 
    . 
    . 
    . 
} 


public partial class AdviceLineCallSubjectMatter 
{ 
    public AdviceLineCallSubjectMatter() 
    { 
     AdviceLineCalls = new HashSet<AdviceLineCall>(); 
    } 

    [DisplayName("Subject Matter")] 
    public int AdviceLineCallSubjectMatterID { get; set; } 

    [StringLength(3)] 
    [DisplayName("Subject Matter")] 
    public string AdviceLineCallSubjectMatterDesc { get; set; } 

    public virtual ICollection<AdviceLineCall> AdviceLineCalls { get; set; } 
} 

}

私はAdviceLineCall Modelクラスに必要なデータ注釈をコメントアウトすると、私のC#はLEFT OUTERと予想SQLに変換されAdviceLineCallSubjectMatterに登録しよう。

必須のデータアノテーションにこの効果がある理由はわかりませんか?

注:同じクエリをテストするために作成した一時的なプロジェクトでは、DBコンテキストとモデルのクラスをCode First From DB経由で作成し、Requiredデータアノテーションを追加しませんでした。

関連する問題