Search code examples
c#t-sqlormdapper

How to use SQL functions in Dapper?


I am doing an application that benchmarks selected ORMs (EF,NHibernate,Dapper). I am developing it in .NET Core 2.0 and I am using AdventureWorks database for it. I have a problem with the query that returns all employees that has age more than 50. I don't know how can I use SQL function DateDiff, or I need some other way to do this via SQL. Here is the snippet from my DapperRepository class:

  public List<EmployeeAgeViewModel> GetEmployeesOlderThan50()
    {
        string sqlQuery = @"select Person.BusinessEntityID as PersonID, Person.LastName,
                    Person.FirstName,
                    Employee.BusinessEntityID as EmployeeID,
                    Employee.Gender,
                    Employee.BirthDate,
                    Employee.HireDate,
                    DATEDIFF(YEAR, Employee.BirthDate, GETDATE()) as [WIEK]
                FROM Person.Person
                    JOIN HumanResources.Employee
                    ON Person.BusinessEntityID = Employee.BusinessEntityID
                WHERE DATEDIFF(YEAR, Employee.BirthDate, GETDATE()) >= 50
                ORDER BY Wiek DESC";

        var employeesAge = _con.Query<Person, Employee, EmployeeAgeViewModel>(sqlQuery,
            (per, emp) => new EmployeeAgeViewModel()
            {
                FirstName = per.FirstName,
                LastName = per.LastName,
                Gender = emp.Gender == "M" ? Models.Helpers.Enums.Gender.M : Models.Helpers.Enums.Gender.F,
                BirthDate = emp.BirthDate,
                HireDate = emp.HireDate,
                Age = -1 
                //Description: We cannot use query below because it is inside other query. So I am assiging -1 and then after I close 
                //          current query I am doing the second one to get the age.
                //_con.Query<int>("select DATEDIFF(year, GETDATE(), @date)", new { date = emp.BirthDate }).SingleOrDefault()
            },
            splitOn: "EmployeeID")
            .ToList();

        string sqlQueryAge = "select DATEDIFF(year, @date, GETDATE())";
        foreach (var employee in employeesAge)
        {
            employee.Age = (int)_con.ExecuteScalar(sqlQueryAge, new { date = employee.BirthDate });
        }

        return employeesAge;
    }

For now I am doing two queries, but I am wondering if I can do it only using one query or maybe some different way.

Thanks for help!


Solution

  • You're already using the function correctly. The problem is that you try to calculate the difference from a field. This prevents the server from using any indexes that cover that field.

    Calculate the cutoff date in your code, eg var cutoff=DateTime.Today.AddYears(-50); and pass the cutoff as a parameter to the query as a parameter, eg :

    var cutoff=DateTime.Today.AddYears(-50);
    
    var sqlQuery = @"select Person.BusinessEntityID as PersonID, Person.LastName,
                Person.FirstName,
                Employee.BusinessEntityID as EmployeeID,
                Employee.Gender,
                Employee.BirthDate,
                Employee.HireDate,
                DATEDIFF(YEAR, Employee.BirthDate, GETDATE()) as Age
            FROM Person.Person
                JOIN HumanResources.Employee
                ON Person.BusinessEntityID = Employee.BusinessEntityID
            WHERE Employee.BirthDate <= @cutoff
            ORDER BY Age DESC";
    
    var employeesAge = _con.Query<Person, Employee, EmployeeAgeViewModel>(sqlQuery,
                            new {cutoff},
                           (per, emp) => new EmployeeAgeViewModel()
                            {
                                FirstName = per.FirstName,
                                LastName = per.LastName,
                                Gender = emp.Gender == "M" ? Models.Helpers.Enums.Gender.M : Models.Helpers.Enums.Gender.F,
                                BirthDate = emp.BirthDate,
                                HireDate = emp.HireDate,
                                Age = emp.Age 
                            },
                            splitOn: "EmployeeID")
                            .ToList();