通过查找将循环展平为单个linq表达式

在LINQ到实体的类型成员支持中? 我试图声明在LINQ中查询的类属性遇到了一些问题。 在这里,我将在实现中布置代码,希望能够将其转换为查询。

我有一个类Quiz ,其中包含一个Question集合,每个Question根据一个QuestionLevel级别进行分类…我需要确定一个测验是“打开”还是“关闭”,这是通过外部联接完成的与最大值表相比,每个级别的问题级别和问题计数。 这是代码,逐字:

 public partial class Quiz { public bool IsClosed { get { // if quiz has no questions, it's open if (this.Questions.Count() == 0) return false; // get a new handle to the EF container to do a query for max values using (EFContainer db = new EFContainer()) { // we get a dictionary of LevelName/number Dictionary max = db.Registry .Where(x => x.Domain == "Quiz") .ToDictionary(x => x.Key, x => Convert.ToInt32(x.Value)); // count the number of questions in each level, comparing to the maxima // if any of them are less, the quiz is "open" foreach (QuestionLevel ql in db.QuestionLevels) { if (this.Questions.Where(x => x.Level == ql).Count() < max["Q:Max:" + ql.Name]) return false; } } // the quiz is closed return true; } } } 

所以这是我尚未尝试的尝试:

  public static IQueryable WhereIsOpen(this IQueryable query) { EFContainer db = new EFContainer(); return from ql in db.QuestionLevels join q in query on ql equals q.Questions.Select(x => x.Level) into qs from q in qs.DefaultIfEmpty() where q.Questions.Count()  x.Domain == "Quiz") .Where(x => x.Key == "Q:Max" + ql.Name) .Select(x => Convert.ToInt32(x.Value)) select q; } 

它在联接帐户上失败,抱怨:

join子句中某个表达式的类型不正确。 调用’GroupJoin’时类型推断失败

我还在努力解决这个问题。

*更新我*

啊。 傻我。

  join q in query on ql equals q.Questions.Select(x => x.Level).Single() 

还有一个障碍:

指定的LINQ表达式包含对与不同上下文关联的查询的引用。

这是因为我为最大查找创建的新容器; 所以我想重新考虑这样的因素:

  public static IQueryable WhereIsOpen(this IQueryable query) { EFContainer db = new EFContainer(); IEnumerable QuestionLevels = db.QuestionLevels.ToList(); Dictionary max = db.Registry .Where(x => x.Domain == "Quiz") .ToDictionary(x => x.Key, x => Convert.ToInt32(x.Value)); return from ql in QuestionLevels join q in query on ql equals q.Questions.Select(x => x.Level).Single() into qs from q in qs.DefaultIfEmpty() where q.Questions.Count() < max["Q:Max:" + ql.Name] select q; } 

但我无法获得编译的表达式…它需要我将QuestionLevels转换为IQueryable(但是转换不起作用,产生运行时exception)。

*更新II *

我找到了铸造问题的解决方案,但现在我回到了“不同的上下文”exception。 GRR …

 return from ql in QuestionLevels.AsQueryable() 

*更新(柯克的建议)*

所以我现在有了这个,它编译但生成运行时exception:

 public static IQueryable WhereIsOpen(this IQueryable query) { EFContainer db = new EFContainer(); IEnumerable QuestionLevels = db.QuestionLevels.Select(x => x.Name).ToList(); Dictionary max = db.Registry .Where(x => x.Domain == "Quiz") .ToDictionary(x => x.Key, x => Convert.ToInt32(x.Value)); return from ql in QuestionLevels.AsQueryable() join q in query on ql equals q.Questions.Select(x => x.Level.Name).Single() into qs from q in qs.DefaultIfEmpty() where q.Questions.Count() < max["Q:Max:" + ql] select q; } 

然后我这样称呼:

 List p = db.Quizes.WhereIsOpen().Select(x => x.Component.Product).ToList(); 

由此产生的exception:

此方法支持LINQ to Entities基础结构,不应在代码中直接使用。

将数据库对象与域对象耦合时,遇到的问题很常见。 正是由于这个原因,最好有一组代表你的域的单独的类和一组代表你的数据库并用于数据库CRUD的类。 可以预期属性重叠,但这种方法可以更好地控制应用程序,并使数据库与业务逻辑分离。

测验关​​闭的想法属于您的域(业务逻辑)。 您的DAL(数据访问层)应负责加入所有必要的表格,以便在您返回测验时,确定其是否已关闭所需的所有信息都可用。 然后,您的域/服务/业务层应创建正确填充IsClosed属性的域对象,以便在UI层(MVC)中轻松访问它。

我看到你直接访问了数据库上下文,我对此提出警告并鼓励你研究使用DI / IoC框架(Ninject很棒),但是,我还要直接访问数据库上下文

在您的视图/控制器中使用此类:

 public class QuizDomainObject { public int Id {get; set;} public bool IsClosed {get; set;} // all other properties } 

控制器:

 public class QuizController : Controller { public ActionResult View(int id) { // using a DI/IoC container is the // preferred method instead of // manually creating a service var quizService = new QuizService(); QuizDomainObject quiz = quizService.GetQuiz(id); return View(quiz); } } 

服务/业务层:

 public class QuizService { public QuizDomainObject GetQuiz(int id) { // using a DI/IoC container is the // preferred method instead of // access the datacontext directly using (EFContainer db = new EFContainer()) { Dictionary max = db.Registry .Where(x => x.Domain == "Quiz") .ToDictionary(x => x.Key, x => Convert.ToInt32(x.Value)); var quiz = from q in db.Quizes where q.Id equals id select new QuizDomainObject() { Id = q.Id, // all other propeties, // I'm still unclear about the structure of your // database and how it interlates, you'll need // to figure out the query correctly here IsClosed = from q in .... }; return quiz; } } } 

回复:你的评论

对QuestionLevels的加入让它认为有两个上下文…但实际上应该没有,因为QuestionLevels应该包含内存中的对象

我相信如果你加入简单的类型而不是对象,你就可以避免这个问题。 以下可能适合您:

 return from ql in QuestionLevels join q in query on ql.LevelId equals q.Questions.Select(x => x.Level).Single().LevelId into qs 

(如果这不起作用,那么构造一些匿名类型并加入Id)

问题是加入Level 对象会导致EF做一些隐藏的魔法 – 找到数据库中的对象并在那里执行连接。 如果你告诉它加入一个简单类型,那么它应该将值发送到数据库以获取SELECT ,检索对象并将它们拼接在应用程序层中。