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

时间:2018-05-16 21:51:24

标签: c# sql entity-framework tsql

我有一个要求,我需要检查eventsLogs表中的登录和注销对。一对包含一个登录和一个注销。在eventLogs表中有许多事件,我对eventType 11和12感兴趣,这意味着用户登录(事件11)和注销(事件12)。该计算机可能存在重复的事件。例如,登录,登录,注销,所以我在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 个答案:

答案 0 :(得分:2)

通过使用面向您想要查询方式的关系的DBContext,可以极大地优化此类操作。在您的情况下,您有Departments / w计算机和一个松散的EventLogs DbSet。计算机实体通常不需要事件日志集合,但就本报告而言,1-many关系将是有用的。

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

从那里,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&amp; amp; skip / w ToList获取一批它们(即每次50个)或迭代并在Foreach中一次选择一个。

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

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

假设有一对一登录注销你的人口比例如下:

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

对查询进行迭代,然后跨登录&amp;注销事件,假设它们已正确配对。您可能需要对如何考虑事件而不是依赖于for循环更有选择性。

sessionPairs将是List<Sessions>,可以按DepartmentID分组,以获取每个部门的一组事件。

答案 1 :(得分:0)

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

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

在TSQL中没有foreach语句,但你可以用while循环替换它们并嵌套它们 - 但是你需要首先用Select Count(*)来获取要解析的行数,然后存储在变量中,然后每次通过while循环递增它。 (注意:在SQL Server存储过程中注意while循环,如果没有来自while循环的清除退出路径,它们可能会使服务器瘫痪。)

您在C#中的lamda语句需要转换为一系列IF / Else语句或TSQL中的CASE语句。

临时表可以通过在名称前用#命名表名来在存储过程中使用,并且可以将值抛出到内存中,然后以某种方式操作它们以获得所需的结果。

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