Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 查询在两个表中拥有所有请求记录的用户_C#_Entity Framework_Linq To Sql - Fatal编程技术网

C# 查询在两个表中拥有所有请求记录的用户

C# 查询在两个表中拥有所有请求记录的用户,c#,entity-framework,linq-to-sql,C#,Entity Framework,Linq To Sql,数据结构如下所示: 用户(id) UserApp(用户id,应用id) 用户技能(用户id、技能id) 使用linq to sql或EF,我将如何构造一个查询,以便只优雅地返回拥有所有请求的应用程序和技能的用户 此外,我如何调整查询以返回至少拥有一个请求的应用程序或技能的任何用户?基本上是一个或vs和(以上) 更新1: 所以我想我们很接近了。基本上,我只想返回拥有所有请求的应用程序和技能的用户。如果我们有两个技能和应用程序请求ID数组: int[] requestedAppIDs // [1,

数据结构如下所示:

用户(id)

UserApp(用户id,应用id)

用户技能(用户id、技能id)

使用linq to sql或EF,我将如何构造一个查询,以便只优雅地返回拥有所有请求的应用程序和技能的用户

此外,我如何调整查询以返回至少拥有一个请求的应用程序或技能的任何用户?基本上是一个或vs和(以上)

更新1:

所以我想我们很接近了。基本上,我只想返回拥有所有请求的应用程序和技能的用户。如果我们有两个技能和应用程序请求ID数组:

int[] requestedAppIDs // [1, 2, 3]
int[] requestedSkillIDs // [4, 5, 6]
我只想返回一个用户,如果他们有应用程序1,2,3和技能4,5,6

var usersWithAllSelectedAppsAndSkills = 
    context.Users
    .GroupJoin(context.UserApp,
        k => k.id,
        k => k.user_id,
        (o, i) => new { User = o, UserApps = i })
    .GroupJoin(context.UserSkill,
        k => k.User.id,
        k => k.user_id,
        (o, i) => new { User = o.User, o.UserApps, UserSkills = i })
    .Where(w => !requestedAppIDs.Except(w.UserApps.Select(x => x.app_id).ToArray()).Any() && !requestedSkillIDs.Except(w.UserSkills.Select(x => x.skill_id).ToArray()).Any())
    .Select(s => s.User)
    .ToList();
显然,LINQ不知道如何将my Where()中的UserSkills.Select().ToArray()转换为SQL。我怎样才能做到这一点


其次,用户也可以使用或解决方案(用户拥有所需的任何一个应用程序或技能)

这里有一种方法,我希望所有语法都正确:)

对于第一种情况(和),只需进行如下内部联接:

from t1 in db.UserApp
join t2 in db.UserSkill on t1.user_id equals t2.user_id
where t1.app_id == "someID" && t2.skill_id == "someID"
select new { t1.user_id,t1.user_app_id, t2.user_skill}

对于第二种情况,只需将&&(AND)与| |(OR)交换即可。

只要UserApp和UserSkill表中的user_id–app_id和user_id–skill_id值是唯一的,这就可以完成任务

var requestedSkillIDs = new[] { 4, 5, 6 };
var skillCount = requestedSkillIDs.Length;
var requestedAppIDs = new[] { 1, 2, 3 };
var appCount = requestedAppIDs.Length;

using (var context = new TestContext()) {
    context.Database.CreateIfNotExists();

    var appQuery = context.UserApp.Where(p => requestedAppIDs.Contains(p.AppId))
                        .GroupBy(p => p.UserId)
                        .Where(p => p.Count() == appCount);

    var skillQuery = context.UserSkill.Where(p => requestedSkillIDs.Contains(p.SkillId))
                        .GroupBy(p => p.UserId)
                        .Where(p => p.Count() == skillCount);

    var result = from a in appQuery
                    join s in skillQuery on a.Key equals s.Key
                    join u in context.Users on s.Key equals u.Id
                    select u;


    var users = result.ToList();
}

有一种更直接的方法可以使用L2E编写所需的查询。要编写这些查询,您必须忘记使用SQL进行思考,而开始使用LINQ进行思考

对于第一种情况,寻找具备所有技能的用户:

var usersWithAll = ctx.Users2.Where(u =>
    appIds.All(aid => u.Apps.Any(a => a.AppId == aid))
    && skillIds.All(sid => u.Skills.Any(s => s.SkillId == sid))
    );
var usersWithAny = ctx.Users2.Where(u =>
    appIds.Any(aid => u.Apps.Any(a => a.AppId == aid))
    && skillIds.Any(sid => u.Skills.Any(s => s.SkillId == sid))
    ).ToList();
翻译为:获取用户,其中,对于所有应用程序id,用户至少拥有一个具有该应用程序id的应用程序,对于所有技能,用户至少拥有一个具有该id的技能

对于第二种情况,拥有任何应用程序和任何技能的用户:

var usersWithAll = ctx.Users2.Where(u =>
    appIds.All(aid => u.Apps.Any(a => a.AppId == aid))
    && skillIds.All(sid => u.Skills.Any(s => s.SkillId == sid))
    );
var usersWithAny = ctx.Users2.Where(u =>
    appIds.Any(aid => u.Apps.Any(a => a.AppId == aid))
    && skillIds.Any(sid => u.Skills.Any(s => s.SkillId == sid))
    ).ToList();
翻译为:获取用户,其中,对于至少一个appId,用户有一个具有该应用id的应用,对于任何技能,用户至少有一个具有该id的技能

如果您运行这个测试类,您还将看到执行的查询(请注意,为此,我使用了
数据库的
Log
属性。我想它只在EF6上可用)

我相信这是正确的,可以让用户拥有所有的技能/应用程序

顺便说一句,我最近用纯SQL解决方案(SQL Server)回答了几乎相同的问题,该解决方案可以转换为存储过程(带有表值参数),看看是否感兴趣。对于大量值,这将执行得更好。实体框架会将列表中的每个技能/应用程序转换为自己的SQL参数,这要慢得多

不幸的是,实体框架还不真正支持表值参数——尽管您可以使用实体框架连接直接使用表值参数调用存储过程(如

回到手头的问题。。。 我将添加(更简单的)查询以选择具有以下任何技能和应用程序的用户:

var result = from u in context.Users
             join _a in (
                from a in context.UserApp
                where requestedAppIDs.Contains(a.AppId)
                select a.UserId;
             ) on u.Id equals _a
             into aGrp
             join _s in (
                from s in context.UserSkill
                where requestedSkillIDs.Contains(s.SkillId)
                select s.UserId;
             ) on u.Id equals _s
             into sGrp
             where aGrp.Any()
                && sGrp.Any()
             select u;
为了完整性,再次强调所有解决方案:

var skillCount = requestedSkillIDs.Length;
var appCount = requestedAppIDs.Length;
var result = from u in context.Users
             join _a in (
                from a in context.UserApp
                where requestedAppIDs.Contains(a.AppId)
                select a.UserId;
             ) on u.Id equals _a
             into aGrp
             join _s in (
                from s in context.UserSkill
                where requestedSkillIDs.Contains(s.SkillId)
                select s.UserId;
             ) on u.Id equals _s
             into sGrp
             where aGrp.Count() == appCount
                && sGrp.Count() == skillCount
             select u;
最后—一个示例,其中主查询主体是固定的,但您可以根据and/OR要求添加不同的where子句

bool onlyReturnWhereAllAreMatched = false;
var skillCount = requestedSkillIDs.Length;
var appCount = requestedAppIDs.Length;
IQueryable<User> result;
var query = from u in context.Users
             join _a in (
                from a in context.UserApp
                where requestedAppIDs.Contains(a.AppId)
                select a.UserId;
             ) on u.Id equals _a
             into aGrp
             join _s in (
                from s in context.UserSkill
                where requestedSkillIDs.Contains(s.SkillId)
                select s.UserId;
             ) on u.Id equals _s
             into sGrp
             select new {u, aCount = aGrp.Count(), sCount = sGrp.Count()};
if (onlyReturnWhereAllAreMatched)
{
   result = from x in query 
            where  x.aCount == appCount
                && x.sCount == skillCount
            select x.u;
} else {
   result = from x in query 
            where  x.aCount > 0
                && x.sCount > 0
            select x.u;
}
bool onlyReturnWherellaRematched=false;
var skillCount=requestedSkillIDs.Length;
var appCount=requestedAppIDs.Length;
可预测的结果;
var query=来自context.Users中的u
加入(
从context.UserApp中的
其中requestedAppID.Contains(a.AppId)
选择a.UserId;
)关于u.Id等于_a
进入aGrp
加入(
来自context.UserSkill中的s
其中requestedSkillIDs.Contains(s.SkillId)
选择s.UserId;
)关于u.Id等于
进入sGrp
选择新的{u,aCount=aGrp.Count(),scont=sGrp.Count()};
如果(仅重新匹配)
{
结果=来自查询中的x
其中x.aCount==appCount
&&x.sCount==skillCount
选择x.u;
}否则{
结果=来自查询中的x
其中x.a帐户>0
&&x.scont>0
选择x.u;
}

根据您的解决方案在上面添加了一个更新。这很接近,但我只希望选择包含所有请求的应用程序和技能的用户。是的,这一个会起作用。bmherold-请记住,您始终需要像上面的示例中那样:Collection.Contains(LinQ属性)。你无法逆转。我的答案呢?它不起作用了?@bmherold你可以发布数据库结构和一个例子吗?@bmherold我的解决方案中有什么不清楚或不简洁?你可以很容易地从(拥有所有/任何技能)(和/或)(拥有所有/任何应用程序)更改。我的解决方案非常小,你可以在推特上发布它;)因此,我想这并不是你真正担心的。请想一想你担心什么,并解释一下。也许你没有很好地解释你在寻找什么。。。甚至你都不知道。