2017-11-27 16 views
0

저는 Linq를 SQL에 사용하고 있으며 새 프로젝트에 Entity Framework를 사용하기 시작했습니다. LinqPad를 사용하여 Visual Studio에 통합하기 전에 코드를 테스트합니다. VS에서 디버깅하는 동안, 나는 나의 카운트가 다른 것을 알아 차렸다. VS에서 내 코드로 작성된 SQL을 검사했을 때 올바르게 변환되지 않은 것으로 나타났습니다.Entity Framework에서 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] 

마지막으로 "내부 조인"A "LEFT OUTER 가입"해야한다 줄 때문에 :

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를 사용하고있다.

는 편집 :

나는 새 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로 조인 생성은 가입 :

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

하지만, 내 현재 프로젝트에서 코드의이 같은 줄은 5 개 레코드를 반환하고, 가입 지난 잘못 가입 내부로 변환됩니다.

현재 프로젝트에서 EF 또는 무언가가 손상되었다는 의미입니까? MVC와 EF의 초보자로서 무엇을해야할지 모르겠습니다. 당신의 AdviceLineCall 엔티티가 이미 관련 기관 (즉 a.AdviceLineCallSubjectMatter)에 대해 선언 된 참조가있는 경우

+0

'join sm in db.AdviceLineCallSubjectMatters'와'gr from grp.DefaultIfEmpty()'에서 이상하게 보입니다. 두 번째'sm in'을'sm_left'로 바꾸고 int를 선택하여'AdviceLineCallSubjectMatterID = sm_left == null? 0 : sm_left.AdviceLineCallSubjectMatterID' – bradbury9

+0

@ bradbury9 나는 당신이 제안한 변경을했다. 나는 아직도 같은 수를 얻는다. 그러나 'AdviceLineCallSubjectMatterDesc = sm.AdviceLineCallSubjectMatterDesc'를 주석 처리하면 정확한 카운트를 얻습니다. 참고 : 나는 Stack Overflow를 처음 사용하고이 주석의 코드 전후에 백틱을 올바르게 사용하고 있다고 생각합니다. "Add Comment?"를 클릭하면 코드 블록으로 표시됩니다. – MikeNaka

+0

저는 bradbury9가 옳다는 것을 확신합니다. 조인과 출처의 태그로 "sm"을 사용해서는 안됩니다. 하나를 다른 것으로 변경하고에서 사용하는 태그 만 참조하십시오. "sm"및 "smgrp". 또한 개체에 할당 할 때 null 속성 참조를 사용합니다 (예 : "AdviceLineCallSubjectMatterID = smgrp? .AdviceLineCallSubjectMatterID ?? string.Empty"). –

답변

0

당신은 당신의 맥락에서 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()는 개체 모델에서 원하는 필드 만 추출합니다. 엔티티가 Optional (null 가능 FK)로 매핑 된 경우 적절한 조인을 자동으로 구성합니다. SubjectMatter와 같은 것이 #null 인 경우 해당 참조 된 엔터티에서 요청 된 두 필드는 null/default가됩니다. .ToList() 또는 .Take()를 게시하는 POCO 객체를 다루는 ViewModel에 .Select() 데이터를 사용하면 여기에 선택 종속성이없는 경우 수행 할 논리를 처리해야합니다. 데이터를 필터링하려면 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 

에 좀 더 깨끗한 방법으로 검사를 선택 널 (null)을 변경할 수

+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 join을 남기고 왜 정규 inne join으로 동작하는지 궁금합니다. – bradbury9

0

(@Jacob Proffitt 코멘트를 확인) 내 문제의 원인과 같은 C# 코드가 SQL로 다르게 번역되는 이유를 발견했으며, 모두 필수 데이터 주석을 사용해야했습니다.

표 AdviceLineCallSubjectMatter는 DB에 새로 추가 된 것입니다. 그래서 새로운 AdviceLineCall 레코드는 AdviceLineCallSubjectMatterID를 가질 것이므로 null 허용 int로 만들었습니다.

"필수"인 특정 AdviceLineCall 필드와 새로운 int가 있습니까? AdviceLineCallSubjectMatterID가 필수 데이터 주석이있는 AdviceLineCall Model 클래스에 추가되었습니다.

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 모델 클래스에서 필요한 데이터 주석을 주석

는, 내 C#은 LEFT OUTER가 AdviceLineCallSubjectMatter에 가입하기로 예상되는 SQL로 변환됩니다.

필수 데이터 주석에이 효과가 나타나는 이유는 무엇입니까 ???

참고 : 동일한 쿼리를 테스트하기 위해 만든 임시 프로젝트에서 DB에서 코드 첫 번째 원본 DB를 통해 DB 컨텍스트 및 모델 클래스를 만들고 필수 데이터 주석을 추가하지 않았습니다.