压扁与查找循环到一个单一的LINQ EX pression压扁、LINQ、pression、EX

2023-09-03 04:17:13 作者:安静的美男子

在LINQ到实体类型成员的支持?我试图声明一个类属性在LINQ其中遇到了一些问题,进行查询。在这里,我会制定出实施内部的code有一定帮助的希望将其转换为查询。

我有一个类测验包含问题 S,每个根据被分类的集合 QuestionLevel ...我需要确定一个测验是否为开放或关闭,这是通过外部连接完成关于这一问题的水平和问题的计数在每个级别中,与最大值的表进行比较。这里的code,逐字:

 公共部分类测验
{
    公共BOOL isClosed返
    {
        得到
        {
            //如果测验有没有什么问题,它是开放的
            如果(this.Questions.Count()== 0)返回false;

            //得到一个新的句柄EF容器做的最大值查询
            使用(EFContainer DB =新EFContainer())
            {
                //我们得到LevelName的字典/数
                字典<字符串,INT>最大= db.Registry
                    。凡(X => x.Domain ==测验)
                    .ToDictionary(X => x.Key中,x => Convert.ToInt32(x.Value));
                //计数在每个水平问题的数量,相对于最大值
                //如果任何人都少了,测验是开放
                的foreach(QuestionLevel QL在db.QuestionLevels)
                {
                    如果(this.Questions.Where(X => x.Level == QL).Count之间()<最大[Q:最大:+ ql.Name])
                        返回false;
                }
            }
            //测验关闭
            返回true;
        }
    }
 }
 

所以这里是我还未尝试的工作吧:

 公共静态的IQueryable<测验> WhereIsOpen(这IQueryable的<测验>查询)
    {
        EFContainer DB =新EFContainer();
        从QL在db.QuestionLevels返回
               加盟●在上QL查询等于q.Questions.Select(X => x.Level)
               进入QS
               从●在qs.DefaultIfEmpty()
               其中q.Questions.Count()&其中; db.Registry
                    。凡(X => x.Domain ==测验)
                    。凡(X => x.Key ==Q:最大+ ql.Name)
                    。选择(X => Convert.ToInt32(x.Value))
               选择Q;
    }
 

它没有帐户的加入,在抱怨:

  

前pressions之一的联接子句中的类型不正确。   该类型推断失败在调用​​群组加入

我还在试图找出答案。

*更新I *

啊。傻傻的我。

 加入●在上QL查询等于q.Questions.Select(X => x.Level)。单()
 
P D C A管理循环 图文详解

多了一个障碍:

  

指定的LINQ EX pression包含对有疑问   与不同的上下文相关联。

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

 公共静态的IQueryable<测验> WhereIsOpen(这IQueryable的<测验>查询)
    {
        EFContainer DB =新EFContainer();
        IEnumerable的< QuestionLevel> QuestionLevels = db.QuestionLevels.ToList();
        字典<字符串,INT>最大= db.Registry
                。凡(X => x.Domain ==测验)
                .ToDictionary(X => x.Key中,x => Convert.ToInt32(x.Value));
        从QL在QuestionLevels返回
               加盟●在上QL查询等于q.Questions.Select(X => x.Level)。单()
               进入QS
               从●在qs.DefaultIfEmpty()
               其中q.Questions.Count()&其中;最大值[Q:最大:+ ql.Name]
               选择Q;
    }
 

但我不能让前pression编译...它需要我投QuestionLevels为IQueryable(但铸造不起作用,生产运行时异常)。

*更新II *

我找到了解决铸造问题,但现在我回来了不同的语境异常。 GRR ...

 从QL在QuestionLevels.AsQueryable回报()
 

*更新(柯克的建议)*

所以我现在有这一点,它编译,但会产生一个运行时异常:

 公共静态的IQueryable<测验> WhereIsOpen(这IQueryable的<测验>查询)
{
    EFContainer DB =新EFContainer();
    IEnumerable的<字符串> QuestionLevels = db.QuestionLevels.Select(X => x.Name).ToList();
    字典<字符串,INT>最大= db.Registry
            。凡(X => x.Domain ==测验)
            .ToDictionary(X => x.Key中,x => Convert.ToInt32(x.Value));
    从QL在QuestionLevels.AsQueryable返回()
           加盟●在上QL查询等于q.Questions.Select(X => x.Level.Name)。单()
           进入QS
           从●在qs.DefaultIfEmpty()
           其中q.Questions.Count()&其中;最大值[Q:最大:+ QL]
           选择Q;
}
 

然后我打电话是这样的:

 名单,其中,产品> P = db.Quizes.WhereIsOpen()选择(X => x.Component.Product)。.ToList();
 

与由此导致的异常:

  

此方法支持LINQ到实体基础设施,而不是   打算直接从您的code使用。

解决方案

你来跨越的问题是共同的,当你对夫妇的数据库对象域对象。这是这个确切原因,这是好事,有一个单独的一套再present您的域名和一个单独的一套再present数据库和用于数据库CRUD类的类。重叠的特性是可以预期的,但这种方法提供了应用程序的更多的控制,并从业务逻辑分离出来你的数据库。

有观点认为,一个测验是封闭的属于您的域(业务逻辑)。您的DAL(数据访问层)应负责连接所有必要的表,所以,当你返回一个测验,所有需要的信息,以确定它是否是封闭的可用。您的域名/服务/业务层应该再创建一个 isClosed返属性正确填充,这样在你的UI层(MVC),您可以轻松地访问它的域对象。

我看你是直接访问数据库的情况下,我会警惕这一点,建议您考虑使用DI / IoC框架(Ninject是伟大的),但是,我要直接访问数据库上下文也

您的看法/控制器使用这个类:

 公共类QuizDomainObject
{
    公众诠释编号{获得;组;}
    公共BOOL isClosed返{获得;组;}
    //所有其​​他属性
}
 

控制器:

 公共类QuizCont​​roller:控制器
{
    公众的ActionResult查看(INT ID)
    {
        //使用DI / IoC容器是
        // preferred方法而不是
        //手动创建服务
        VAR quizService =新QuizService();
        QuizDomainObject测验= quizService.GetQuiz(ID);

        返回查看(测验);
    }
}
 

服务/业务层:

 公共类QuizService
{
    公共QuizDomainObject GetQuiz(INT ID)
    {
        //使用DI / IoC容器是
        // preferred方法而不是
        //访问的datacontext直接
        使用(EFContainer DB =新EFContainer())
        {
            字典<字符串,INT>最大= db.Registry
                。凡(X => x.Domain ==测验)
                .ToDictionary(X => x.Key中,x => Convert.ToInt32(x.Value));

            VAR测验=从●在db.Quizes
                       其中,q.Id等于ID
                       选择新QuizDomainObject()
                       {
                            n = q.Id,
                            //所有其​​他化子性质,

                            //我还是不清楚的结构的
                            //数据库以及它如何interlates,你需要
                            //找出正确的查询这里
                            isClosed返=从●在....
                       };


            返回测验;
        }
    }
}
 

In Type member support in LINQ-to-Entities? I was attempting to declare a class property to be queried in LINQ which ran into some issues. Here I will lay out the code inside the implementation in hopes of some help for converting it to a query.

I have a class Quiz which contains a collection of Questions, each of which is classified according to a QuestionLevel... I need to determine whether a quiz is "open" or "closed", which is accomplished via an outer join on the question levels and a count of the questions in each level, as compared with a table of maximum values. Here's the code, verbatim:

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<string, int> 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;
        }
    }
 }

so here's my not-yet-working attempt at it:

    public static IQueryable<Quiz> WhereIsOpen(this IQueryable<Quiz> 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() < db.Registry
                    .Where(x => x.Domain == "Quiz")
                    .Where(x => x.Key == "Q:Max" + ql.Name)
                    .Select(x => Convert.ToInt32(x.Value))
               select q;
    }

it fails on account on the join, complaining:

The type of one of the expressions in the join clause is incorrect. The type inference failed in the call to 'GroupJoin'

I'm still trying to figure that out.

* update I *

ah. silly me.

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

one more roadblock:

The specified LINQ expression contains references to queries that are associated with different contexts.

this is because of the new container I create for the maximum lookups; so I thought to re-factor like this:

    public static IQueryable<Quiz> WhereIsOpen(this IQueryable<Quiz> query)
    {
        EFContainer db = new EFContainer();
        IEnumerable<QuestionLevel> QuestionLevels = db.QuestionLevels.ToList();
        Dictionary<string, int> 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;
    }

but I can't get the expression to compile... it needs me to cast QuestionLevels to an IQueryable (but casting doesn't work, producing runtime exceptions).

* update II *

I found a solution to the casting problem but now I'm back to the "different contexts" exception. grr...

return from ql in QuestionLevels.AsQueryable()

* update (Kirk's suggestion) *

so I now have this, which compiles but generates a run-time exception:

public static IQueryable<Quiz> WhereIsOpen(this IQueryable<Quiz> query)
{
    EFContainer db = new EFContainer();
    IEnumerable<string> QuestionLevels = db.QuestionLevels.Select(x => x.Name).ToList();
    Dictionary<string, int> 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;
}

which I then call like this:

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

with the resulting exception:

This method supports the LINQ to Entities infrastructure and is not intended to be used directly from your code.

解决方案

The issues you're coming across are common when you couple your database objects to your domain objects. It's for this exact reason that it's good to have a separate set of classes that represent your domain and a separate set of classes that represent your database and are used for database CRUD. Overlap in properties is to be expected, but this approach offers more control of your application and decouples your database from your business logic.

The idea that a quiz is closed belongs to your domain (the business logic). Your DAL (data access layer) should be responsible for joining all the necessary tables so that when you return a Quiz, all the information needed to determine whether or not it's closed is available. Your domain/service/business layer should then create the domain object with the IsClosed property properly populated so that in your UI layer (MVC) you can easily access it.

I see that you're access the database context directly, I'd warn against that and encourage you to look into using DI/IoC framework (Ninject is great), however, I'm going to access the database context directly also

Use this class in your views/controllers:

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

Controller:

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);
    }
}

Service/business layer:

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<string, int> 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;
        }
    }
}