【问题标题】:Linq SubQuery for Unrelated entity variable不相关实体变量的 Linq 子查询
【发布时间】:2019-04-14 15:28:22
【问题描述】:

是否可以使用 Linq 转换/复制以下内容?

DECLARE @UserID INT = 1, @ViewerUserID INT = 1002;

SELECT UGR.*,
CASE
WHEN (
    SELECT subR.[Level]
    FROM UserGameRanks subUGR WITH (NOLOCK)
    INNER JOIN Ranks subR WITH (NOLOCK) ON (subUGR.RankId = subR.Id)
    INNER JOIN Games subG WITH (NOLOCK) ON (subUGR.GameId = subG.Id)
    INNER JOIN Users subU WITH (NOLOCK) ON (subUGR.UserID = subU.Id)
    WHERE subUGR.IsDeleted = 0 AND subU.Id = @ViewerUserID AND subUGR.GameID = UGR.GameId
) > R.[Level] THEN 1
ELSE 0
END AS CanEdit
FROM UserGameRanks UGR WITH (NOLOCK)
INNER JOIN Ranks R WITH (NOLOCK) ON (UGR.RankId = R.Id)
INNER JOIN Games G WITH (NOLOCK) ON (UGR.GameId = G.Id)
INNER JOIN Users U WITH (NOLOCK) ON (UGR.UserID = U.Id)
WHERE UGR.IsDeleted = 0 AND U.Id = @UserID
AND
(
    (
        (@ViewerUserID <> - 1)
        AND
        (UGR.VisibilityId = 1)
    )
    OR
    (
        (UGR.VisibilityId = 2)
    )
    OR
    (
        (UGR.VisibilityId = 0)
        AND
        (@UserID = @ViewerUserID)
    )
)

特别是 CASE 子查询?我向 Users 实体类添加了一个 [NotMapped] CanEdit 值,但是我不确定如何使用一个 sql 查询填充它,而不是先执行初始获取,然后循环并更新 CanEdit。

我看过 StackOverflow。

我们将不胜感激。

编辑:查看您的答案,我知道该怎么做,但我意识到我在问题中编造了一个东西。我最初创建了上述查询作为我的问题的示例,但我现在看到它与我的问题没有直接关系。我已经更新了 SQL 部分...

我们的想法是我们可能有许多排名,其中最低排名是“未设置”,这将是 0 级。因此,如果我玩另一个用户玩的游戏并且我的排名设置为 2 级,我可以分配排名将此用户设置为 1。但在不同的游戏中,我也可能是“未设置”(级别 0),因此无法编辑他/她的排名。

@CodingYoshi:这是我尝试过的

var viewerQuery = from ugr in context.UserGameRanks
                               join r in context.Ranks on ugr.RankId equals r.Id
                               join g in context.Games on ugr.GameId equals g.Id
                               join u in context.Users on ugr.UserId equals u.Id
                               where (!ugr.IsDeleted) && (ugr.UserId == viewerUserId)
                               select new { UserGameRank = ugr };

            var query = from ugr in context.UserGameRanks
                        join r in context.Ranks on ugr.RankId equals r.Id
                        join g in context.Games on ugr.GameId equals g.Id
                        join u in context.Users on ugr.UserId equals u.Id
                        where (!ugr.IsDeleted) && (ugr.UserId == userId) &&
                        (
                            (viewerUserId != -1 && ugr.VisibilityId == Visibility.RegisteredUsers)
                            ||
                            (ugr.VisibilityId == Visibility.Public)
                            ||
                            (ugr.VisibilityId == Visibility.Hidden && userId == viewerUserId)
                        )
                        select new { GameName = g.Name, Username = ugr.Username, RankName = r.Name, CanEdit = (viewerQuery.Rank.Level > r.Level ? 1 : 0) };

【问题讨论】:

  • 对于 case 部分,使用 c# 三元运算符,如 here 所示。你想做的就是直截了当,你有没有尝试过?
  • 查看我的编辑:3 我会看看那个。

标签: c# sql linq subquery sql-to-linq-conversion


【解决方案1】:

几个问题:

1) 所有的连接是怎么回事?您应该在模型配置中正确设置导航属性。

2) 您生成的查询的CanEdit 属性依赖于指定viewerUserId 的游戏等级级别,但似乎指定viewerUserId 是可选的。如果没有指定或找到查看器用户,默认行为是什么?我假设CanEdit 应该是假的。

public class YourResultClass()
{
    public string GameName { get; set; }
    public string UserName { get; set; }
    public string RankName { get; set; }
    public bool CanEdit { get; set; }
}

var targetUserGameRanksQuery = context.Users
    .Where(u => !u.IsDeleted 
        && u.UserId == userId)
    // flatten for use in subsequent join
    .SelectMany(u => u.UserGameRanks);

IQueryable<YourResultClass> query = null;

if(viewerUserId.HasValue)
{
    var viewerGameRanksQuery = context.Users
        .Where(u => !u.IsDeleted 
            && u.UserId == viewerUserId)
        // flatten for use in subsequent join
        .SelectMany(u => u.UserGameRanks);

    var joinQuery = targetUserGameRanksQuery // outer source `o`
        .Join(viewerGameRanksQuery, // inner source `i`
            o => o.GameId,
            i => i.GameId,
            (o, i) => new
            {
                GameName = o.Game.Name,
                TargetUserName = o.Username,
                TargetRankName = o.Rank.Name,
                CanEdit = i.Rank.Level > o.Rank.Level,
                Visibility = o.VisibilityId
            });

    query = joinQuery
        .Where(at =>
            at.Visibility == Visibility.RegisteredUsers

            || (at.Visibility == Visibility.Hidden
                && userId == viewerUserId.Value)

            || at.Visibility == Visibility.Public )
        .Select(at =>
            new YourResultClass()
            {
                GameName = at.GameName,
                UserName = at.UserName,
                RankName = at.RankName,
                CanEdit = at.CanEdit,
            });
}
else
{
    query = targetUserGameRanksQuery
        .Where(ugh => ugr.VisibilityId == Visibility.Public)
        .Select(ugh => new YourResultClass()
        {
            GameName = ugr.Game.Name,
            UserName = ugr.Username,
            RankName = ugr.Rank.Name,
            CanEdit = false,
        });
}

【讨论】:

    【解决方案2】:

    大概是这样的:

    var users = new List<User>
    {
        new User { Id = 1, UserName = "User1", UserTypeId = 1 }, 
        new User { Id = 2, UserName = "User2", UserTypeId = 2 }, 
        new User { Id = 3, UserName = "User3", UserTypeId = 2 }, 
    };
    
    var userTypes = new List<UserType>
    {
        new UserType { Id = 1, Type = "Admin", Security = 1 }, 
        new UserType { Id = 2, Type = "User", Security = 2  }
    };
    
    var userId = 1;
    
    var ViewerUserID = 2;
    var viewerSecurity = 
        (from u in users
        join ut in userTypes on u.UserTypeId equals ut.Id
        where u.Id == ViewerUserID
        select ut.Security).FirstOrDefault();
    
    var res = 
        (from u in users
        join ut in userTypes on u.UserTypeId equals ut.Id
        where u.Id == userId || u.Id == -1
        select new
        {
            Id = u.Id,
            UserName = u.UserName,
            CanEdit = viewerSecurity > ut.Security ? 1 : 0
        });
    }
    
    class User {
        public int Id {get; set;}
        public string UserName {get; set;}
        public int UserTypeId {get; set;}
    }
    
    class UserType {
        public int Id {get; set;}
        public string Type {get; set;}
        public int Security {get; set;}
    }
    

    更新: 首先,无需编辑您的问题,因为其他人回答了您的初始问题,现在他们和我的初始回答毫无意义。 您应该提出一个新问题或更新这个问题。

    这是您编辑的问题的代码:

    var res = 
        (from ugr in userGameRanks
        join r in ranks on ugr.RankId equals r.Id
        join g in games on ugr.GameId equals g.Id
        join u in users on ugr.UserId equals u.Id
        where !ugr.IsDeleted    // assuming IsDeleted is of type BIT in your DB
            && u.Id == userId
            && (
                    (ViewerUserID != -1 && ugr.VisibilityId == 1)
                    || ugr.VisibilityId == 2
                    || (ugr.VisibilityId == 0 && userId == ViewerUserID)
                )
        let level =
            (from subUGR in userGameRanks
            join subR in ranks on subUGR.RankId equals subR.Id
            join subG in games on subUGR.GameId equals subG.Id
            join subU in users on subUGR.UserId equals subU.Id
            where !subUGR.IsDeleted 
                && subU.Id == ViewerUserID
                && subUGR.GameId == ugr.GameId
            select subR.Level).FirstOrDefault()
    
        select new 
        {
            ugr.RankId, 
            ugr.UserId,
            ugr.GameId,
            ugr.IsDeleted,
            ugr.VisibilityId,
            CanEdit = level > r.Level ? 1 : 0
        });
    

    希望对你有帮助

    【讨论】:

    • 您的回复让我意识到我需要更新我的问题。
    • 抱歉,我会记住这一点,以备日后提问。
    【解决方案3】:

    我将数据库建模为类以确保语法正确。不确定我是否一切都正确,但很接近

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                int UserID = 1;
                int ViewerUserID = 2;
    
                DataBase db = new DataBase();
    
                var resutls = (from subU in db.subU
                               join subUt in db.subUT on subU.Id equals subUt.UserTypeID
                               select new { subU = subU, subUt = subUt })
                               .Where(x => ((x.subU.Id == ViewerUserID) && (x.subUt.Security > x.subU.Security)) || x.subU.Id == -1)
                               .ToList();
            }
        }
        public class DataBase
        {
            public List<Users> subU { get; set; }
            public List<UserTypes> subUT { get; set; }
    
        }
        public class Users
        {
            public int Id { get; set; }
            public int Security { get; set; }
        }
        public class UserTypes
        {
            public int UserTypeID { get; set; }
            public int Security { get; set; }
        }
    }
    

    【讨论】:

    • 这让我很困惑。在您的加入中,您加入 UserTypes 上的 Users.ID 等于 UserTypes.UserTypeID 吗? Users 表没有 Security 值,它只存储在 UserTypes 上。
    • 我可能犯了一个错误。我试图根据您发布的 Select 创建查询。今天晚些时候我会看看你的更新。我认为我的解决方案比您尝试的解决方案简单得多。您可能想查看以下 msdn 网页上的左外连接:code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
    • 哦,谢谢你的链接,这真的很有帮助。
    猜你喜欢
    • 1970-01-01
    • 2013-05-07
    • 2020-09-26
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    相关资源
    最近更新 更多