代码之家  ›  专栏  ›  技术社区  ›  Rohit

将实体框架C#代码转换为原始SQL以提高性能

  •  0
  • Rohit  · 技术社区  · 7 年前

    我有一个要求,我需要检查eventsLogs表中的登录和注销对。一对由一个登录和一个注销组成。在eventLogs表中有许多我感兴趣的事件类型11和12,这意味着用户何时登录(事件11)和何时注销(事件12)。对于该用户,可能存在来自计算机的重复事件。例如,login,login,logout,所以我在C#中所做的是第一次登录,然后找到它的配对。问题是我不知道如何在SQL中执行此操作,不幸的是,我正在将所有登录和注销加载到内存中!。我想要一种方法,这样我可以调用一个存储过程,并得到与我当前绑定到C#中的一个类相同的结果。我将非常感谢你的帮助。如果我没有很好地解释我的问题,请要求解释。

     // C#
       var departmentEventsDic = new Dictionary<string, List<Sessions>>();   
    
       //get all departments and run the loop
     foreach (var department in departments)
                        {
                            // create a list to push a pairs
                            var sessionPairsList = new List<Sessions>();
                          // for each computer in the department
                            foreach (var computer in department.Computers)
                            {
    
                                var  tempLogs= eventLogs.Where(x => x.ComputerId == computer.ComputerId) .OrderBy(x => x.EventDateTime).ToList();
                                var tempUnlock = DateTime.MinValue;
    
                                //for each log for the computer 
                                foreach (var log in tempLogs)
                                {
                                    // if the event is login store it in temp varible 
                                    if (log.EventType == 11)
                                    {
                                        tempUnlock = log.EventDateTime;
    
                                    }
                                    // if its logout and its time is greater than tempUnlock , found a pair create class and add to list 
                                    if (log.EventType == 12 && (tempUnlock != DateTime.MinValue) && log.EventDateTime > tempUnlock)
                                    {
                                        var sessionPair = new Sessions
                                        {
                                            DepartmentId = department.DepartmentId,
                                            DepartmentName = department.DepartmentName,
                                            ComputerId = computer.ComputerId,
                                            ComputerName = computer.Name,
                                            LoginTime = tempUnlock,
                                            LogOutTime = log.EventDateTime,
                                            UserId = department.Users.FirstOrDefault(x => x.UserId == log.UserId)?.UserId,
                                            UserName = department.Users.FirstOrDefault(x => x.UserId == log.UserId)?.Name,
                                            Difference = (log.EventDateTime - temUnlock)
                                        };
                                        sessionPairsList.Add(sessionPair);
                                    }
                                }
                            }
                            // add to dictionary with department name as key and list of sessionspairs as values
    
                            departmentEventsDic.Add(department.DepartmentName, sessionPairsList);
                        }
    
    
    
     public class Results
        {
            public Guid DepartmentId { get; set; }
            public string DepartmentName { get; set; }
            public Guid ComputerId { get; set; }
            public string ComputerName { get; set; }
            public DateTime LoginTime { get; set; }
            public DateTime LogOutTime { get; set; }
            public Guid? UserId { get; set; }
            public string UserName { get; set; }
            public TimeSpan Difference { get; set; }
        }       
        //Tables
    
        CREATE TABLE [dbo].[Departments](
        [DepartmentId] [uniqueidentifier] NOT NULL PrimaryKey,
        [DepartmentName] [nvarchar](max) NULL
        );
        CREATE TABLE [dbo].[Computers](
        [ComputerId] [uniqueidentifier] NOT NULL PrimaryKey,    
        [DepartmentId] [uniqueidentifier] NOT NULL, 
        [Name] [nvarchar](max) NULL );
    
        ALTER TABLE [dbo].[Computers]  WITH CHECK ADD  CONSTRAINT [FK_Computers_Departments_DepartmentId] FOREIGN KEY([DepartmentId])
        REFERENCES [dbo].[Departments] ([DepartmentId])
        ON DELETE CASCADE
    
    
    
         CREATE TABLE [dbo].[EventLogs](
        [EventLogId] [uniqueidentifier] NOT NULL PrimaryKey,
        [ComputerId] [uniqueidentifier] NOT NULL,
        [EventDateTime] [datetime2](7) NOT NULL,
        [EventType] [int] NOT NULL,
        [UserId] [uniqueidentifier] NULL);
        ALTER TABLE [dbo].[EventLogs]  WITH CHECK ADD  CONSTRAINT [FK_EventLogs_Computers_ComputerId] FOREIGN KEY([ComputerId])
        REFERENCES [dbo].[Computers] ([ComputerId])
        ON DELETE CASCADE
    
    
    
       CREATE TABLE [dbo].[Users](
        [UserId] [uniqueidentifier] NOT NULL,
        [DepartmentId] [uniqueidentifier] NOT NULL, 
        [Name] [nvarchar](max) NULL);
        ALTER TABLE [dbo].[Users]  WITH CHECK ADD  CONSTRAINT [FK_Users_Departments_DepartmentId] FOREIGN KEY([DepartmentId])
        REFERENCES [dbo].[Departments] ([DepartmentId])
        ON DELETE CASCADE
    
    2 回复  |  直到 7 年前
        1
  •  2
  •   Steve Py    7 年前

    通过使用DBContext和面向您想要的查询方式的关系,可以极大地优化这样的操作。在你的情况下,你有部门/w计算机,和一个松散的事件日志数据库集。计算机实体通常不需要事件日志的集合,但在本报告中,这种1-many关系将非常有用。

    如果您的主上下文没有映射出这些关系,那么您可以考虑使用一个有界上下文来使用实体映射检索此数据,将计算机视为顶级实体,并且每台计算机都有一个部门和一组事件日志。每个事件都有一个基于其用户ID的用户引用。

    从那里,EF可以完成所有的举重:

    var sessionDataQuery  = dbContext.Computers
        .Select(x => new 
        {
            x.Department.DepartmentId,
            x.Department.DepartmentName,
            x.ComputerId,
            ComputerName = x.Name,
            LoginEvents = x.EventLogs
                .OrderBy(e => e.EventDateTime)
                .Where(e => e.EventType = 11)
                .Select(e => new 
                {
                    e.EventId,
                    e.User.UserId,
                    e.User.UserName,
                    e.EventDateTime
                }.ToList(),
            LogoutEvents = x.EventLogs
                .OrderBy(e => e.EventDateTime)
                .Where(e => e.EventType = 12)
                .Select(e => new 
                {
                    e.EventId,
                    e.User.UserId,
                    e.User.UserName,
                    e.EventDateTime
                }.ToList()
         });
    

    该查询应该会生成一个IQueryable数据,可以用来开始构建会话详细信息。因为它会穿过 全部的 你可能希望避免使用计算机。ToList(),而不是使用Take&跳过/w ToList以获取一批(即一次50个)或在Foreach中迭代并一次选择一个。

    这假设事件的用户参考将与计算机部门匹配。

    从这里,您可以评估每台计算机的登录事件和注销事件,并为它们构建会话视图模型。你可能需要逻辑来根据时间将登录和注销结合起来,并处理可能在没有注销的情况下登录和vise verse的情况。

    假设有一个一对一的登录来注销您的会话par群体,则类似于:

    foreach(var computer in sessionDataQuery)
    {
        for(int count = 0; count < computer.LoginEvents.Count; count++)
        {
           var loginEvent = computer.LoginEvents[count];
           var logoutEvent = computer.LogoutEvents[count];
           sessionPairs.Add( new Sessons
           {
              DepartmentId = computer.DepartmentId,
              DepartmentName = computer.DepartmentName,
              ComputerId = computer.ComputerId,
              ComputerName = computer.ComputerName,
              LoginTime = loginEvent.EventDateTime,
              LogoutTime = logoutEvent.EventDateTime,
              UserId = loginEvent.UserId,
              UserName = loginEvent.UserName,
              Difference = logoutEvent.EventDateTime - loginEvent.EventDateTime
          }
       }
    }
    

    这将在查询上进行迭代,然后在登录上进行迭代(&A);注销事件,假设它们已正确配对。很有可能,你需要在考虑事件的方式上更具选择性,而不是依赖于for循环。

    会话对将是一个 List<Sessions> 并且可以按部门ID分组,以获得每个部门的一组事件。

        2
  •  0
  •   StevenJe    7 年前

    您的标题声明要将其转换为SQL代码。这在TSQL中都是可行的,但首先您需要知道将C#代码转换为TSQL的语法。在这里,我提供了所有的元素,你将需要把你带到那里。

    如果需要从存储过程返回值,请使用带有输出参数的创建过程。存储过程中的所有参数看起来都像带有数据类型的@parameter。

    TSQL中没有foreach语句,但可以用while循环替换这些语句并嵌套它们——但首先需要使用Select count(*)获取要解析的行数,将其存储在变量中,然后每次通过while循环递增。(注意:要小心SQL Server存储过程中的while循环,如果没有明确的while循环退出路径,它们可能会使服务器崩溃。)

    C#中的lamda语句需要转换为一系列IF/Else语句,或者可能转换为TSQL中的CASE语句。

    临时表可以在存储过程中使用,方法是在表名前加上#来命名表名,它有助于将值放入内存,然后以某种方式对其进行操作以获得所需的结果。

    希望这能为您提供更多关于TSQL存储过程解决方案的想法。