2013-07-08 86 views
0

成本通过QuestionID和AnswerID外键链接到PropertySurvey中的AnsweredQuestion。 费用也可以通过UnitsQuestionID连接到同一PropertySurvey中的AnsweredQuestion,并通过ReplacementQuestionID再次连接。 下面是该查询作为内连接 - 写在避免加入语法@CraigStuntz regards as "messy"使用Linq进行左连接而没有“杂乱”连接

var propertyCosts = from aq in answeredQuestions 
    from a in aq.Answers 
    from c in costs 
    where aq.QuestionID == c.QuestionID && a.ID == c.AnswerID 
    from uq in answeredQuestions 
    where uq.QuestionID == c.UnitsQuestionID 
         && uq.PropertySurveyID == aq.PropertySurveyID 
    from rq in answeredQuestions 
    where rq.QuestionID == c.ReplacementQuestionID 
         && rq.PropertySurveyID == aq.PropertySurveyID 
    select new PropertyCost(aq.Question.Text, 
    a.Text, 
    c.Amount, 
    uq.IntegerAnswer.GetValueOrDefault(1), 
    rq.IntegerAnswer.GetValueOrDefault(0)); 

是否有可能改写这个左加入到UnitQuestion和ReplacementQuestion没有这种DefaultIfEmpty东西的方法吗?

+0

如果答案已经链接到问题为什么需要将成本链接到问题和答案,那么您肯定可以链接到问题? –

+0

如果问题是“需要更换屋顶?”答案是“是”,那么c.Amount指定了一个成本。如果答案是“否”,那么没有成本。 – Colin

+0

但是,UnitQuestion包含其IntegerAnswer属性中的值 - 因此没有链接到该关系中的单独答案 – Colin

回答

0
//Make this problem less complicated to explain by abstracting the inner join 
//that we always need. 
//For efficiency this should probably be put back in line 
var A = from aq in answeredQuestions 
     from a in aq.Answers 
     from c in costs 
     where aq.QuestionID == c.QuestionID && a.ID == c.AnswerID 
     select new 
     { 
      QuestionText = aq.Question.Text, 
      AnswerText = a.Text, 
      Amount = c.Amount, 
      PropertySurveyID = aq.PropertySurveyID, 
      PropertySurvey = aq.PropertySurvey, 
      UnitsQuestionID = c.UnitsQuestionID, 
      UnitsQuestion = c.UnitsQuestion 
     }; 


//This is how to do it using the explicit join keyword 
var B = from a in A 
     join lj in answeredQuestions on 
      new { 
        a.PropertySurveyID, 
        UnitsQuestionID = a.UnitsQuestionID.GetValueOrDefault(0) 
       } 
      equals new { 
          lj.PropertySurveyID, 
          UnitsQuestionID = lj.QuestionID 
         } 
      into unitQuestions 
     from uq in unitQuestions.DefaultIfEmpty() 
     select new PropertyCost(a.QuestionText, 
        a.AnswerText, 
        a.Amount, 
        uq == null ? 1 : uq.IntegerAnswer.GetValueOrDefault(1)); 

//I thought this might do it but the filter on the navigational property 
//means it's equivalent to an inner join 
var C = from a in A 
     from uq in a.PropertySurvey 
        .AnsweredQuestions 
        .Where(x => x.QuestionID == a.UnitsQuestionID) 
     select new PropertyCost(a.QuestionText, 
        a.AnswerText, 
        a.Amount, 
        uq == null ? 1 : uq.IntegerAnswer.GetValueOrDefault(1)); 

//This is the solution 
//Back to the basics described by @CraigStuntz 
//Just that we have to navigate further to get to the Units value 
//Is this less "messy" than a join? Not sure. Maybe if you can think in Linq... 
var D = from a in A 
     select new PropertyCost 
     (
      a.QuestionText, 
      a.AnswerText, 
      a.Amount, 
      a.PropertySurvey 
       .AnsweredQuestions 
       .Where(x => x.QuestionID == a.UnitsQuestionID) 
       .FirstOrDefault() == null 
      ? 
      1 
      : a.PropertySurvey 
       .AnsweredQuestions 
       .Where(x => x.QuestionID == a.UnitsQuestionID) 
       .FirstOrDefault() 
       .IntegerAnswer.GetValueOrDefault(1) 
     ); 

//And here I have further refined it by putting the initial inner join back 
//inline and using the let keyword define how to retrieve the unit question. 
//This makes it much more readable: 

var E = from aq in answeredQuestions 
     from a in aq.Answers 
     from c in costs 
     let unitquestion = aq.PropertySurvey 
      .AnsweredQuestions 
      .Where(x => x.QuestionID == c.UnitsQuestionID) 
      .FirstOrDefault() 
     where aq.QuestionID == c.QuestionID && a.ID == c.AnswerID 
     select new 
     { 
      QuestionText = aq.Question.Text, 
      AnswerText = a.Text, 
      UnitCost = c.Amount, 
      NumUnits = unitquestion == null ? 1 : unitquestion.IntegerAnswer ?? 1, 
     }; 

我花了很长时间才得到这个。我仍然认为在SQL中,我不禁要被诱惑到Use Views in Entity Framework。请注意我已简化了此操作以仅显示第一个左连接到UnitQuestion

0

为了跟随克雷格Stuntz你需要有你的逻辑可以遵循的关联。

因此,要模拟你的Many-Many,你需要一个有1-Many关联的中间表来回答Questions and Answers。您可以在这里使用成本,并且免费的零成本。

所以

问题(QuestionId)1 - > 1 .. *成本(QuestionId) 答案(AnswerId)1 - > 1 .. *成本(AnswerId) 问题(QuestionId)1 - > 0。 *成本(UnitQuestionId) 问题(QuestionId)1 - > 0 .. *成本(ReplacementQuestionId)

然后在模型建立的关联

Question (Property = Costs) -- (Property = Question) Costs 
Question (no mapping) -- (Property = UnitQuestion) Costs 
Question (no mapping) -- (Property = ReplacementQuestion) Costs 
Answer (Property = Costs) -- (Property = Answer) Costs 

此查询应该符合您查询的行为。

from aq in answeredQuestions 
from c in aq.Costs 
select new PropertyCost(aq.Question.Text, 
         c.Answer.Text, 
         c.Amount, 
         c.UnitQuestion == null ? 0 : c.UnitQuestion.IntegerAnswer 
         c.ReplacementQuestion == null ? 0 : c.ReplacementQuestion.IntegerAnswer 
+0

我现在认为问题不在于我的导航属性或关系类型。问题是SQL等价物是一个左连接,在连接上有一个额外的约束。看到这个问题http://stackoverflow.com/q/14740121/150342和我的答案 – Colin