Guide to Creating Dynamic LINQ Queries

Are you new to LINQ and trying to figure out how to create dynamic LINQ queries like the same way you do with ADO.Net?  Well it’s not that hard at all and all you need to do is use a combination of the “Where” Extension method and “IQueryable” interface.

So for example we have a Customer Table where we want to filter it by Customer ID, First Name, Last Name, Customer Type, and Active.

Now below is a piece of code that will generate the dynamic LINQ queries

public static List<Customer> GetFilteredData(int? CustomerID, string FirstName, string LastName, string CustomerType, bool Active)
{
    DataClasses1DataContext oDB = new DataClasses1DataContext();
    IQueryable<Customer> oDataQuery = oDB.Customers;

    //Filter by Integer
    if (CustomerID != null)
    {
        oDataQuery = oDataQuery.Where(a => a.CustomerID == CustomerID);
    }

    //Filter by containing words
    if (FirstName.Trim().Length > 0)
    {
        oDataQuery = oDataQuery.Where(a => a.FirstName.Contains(FirstName));
    }

    //Filter by containing words
    if (LastName.Trim().Length > 0)
    {
        oDataQuery = oDataQuery.Where(a => a.LastName.Contains(LastName));
    }

    //Filter by a Foreign Key Relationship
    if (CustomerType.Trim().Length > 0)
    {
        oDataQuery = oDataQuery.Where(a => a.CustomerTypeID == (from b in oDB.CustomerTypes where b.CustomerType1 == CustomerType select b.CustomerTypeID).FirstOrDefault());
    }

    //Filter by boolean
    oDataQuery = oDataQuery.Where(a => a.Active == Active);

    return oDataQuery.ToList();
}

Now let’s dissect what just happened:

1. First you had created your LINQ to SQL Data Context; in our sample we just simply call it oDB.

2. Next we declared an IQueryable interface called oDataQuery and initialized it with your table properties in the Database; in this case we had named it Customers.  At this point nothing had happened yet and no data is being queried, you are just initializing the object type.

3. Now let’s create the where clause dynamically by filtering every step of the way starting from Customer ID to Active Columns.  At this point nothing has been queried on the database and you are just creating a dynamic query on the background. This is the big difference between the two interfaces “IQueryable” and “IEnumerable”.
In “IEnumerable” it gets all of the records first in the associated table then it is the application side that filters it out.

To get a better understanding here are the queries that are generated on the background every step of the way.

At this line

oDataQuery = oDataQuery.Where(a => a.CustomerID == CustomerID);

This query gets generated

SELECT [t0].[CustomerID], [t0].[LastName], [t0].[FirstName], [t0].[CustomerTypeID], [t0].[Active], [t0].[Birthdate]
FROM [dbo].[Customers] AS [t0]
WHERE ([t0].[CustomerID]) = @p0

Then at this line

oDataQuery = oDataQuery.Where(a => a.FirstName.Contains(FirstName));

This query gets generated

SELECT [t0].[CustomerID], [t0].[LastName], [t0].[FirstName], [t0].[CustomerTypeID], [t0].[Active], [t0].[Birthdate]
FROM [dbo].[Customers] AS [t0]
WHERE ([t0].[FirstName] LIKE @p0) AND (([t0].[CustomerID]) = @p1)

So you see how the next condition is appenededon the where clause.  This goes on until you had finalized the whole query and in our sample it will look like this

SELECT [t0].[CustomerID], [t0].[LastName], [t0].[FirstName], [t0].[CustomerTypeID], [t0].[Active], [t0].[Birthdate]
FROM [dbo].[Customers] AS [t0]
WHERE ([t0].[Active] = 1) AND ([t0].[CustomerTypeID] = ((
    SELECT TOP (1) [t1].[CustomerTypeID]
    FROM [dbo].[CustomerTypes] AS [t1]
    WHERE [t1].[CustomerType] = @p0
    ))) AND ([t0].[LastName] LIKE @p1) AND ([t0].[FirstName] LIKE @p2) AND (([t0].[CustomerID]) = @p3

It is your [if] statement that controls whether a new where clause will be added

4. Finally you have to output it as something like a list so we will use the .ToList() method to get your desired query results.

Still not convinced, I had run a profiler at the background to see what queries are performed and it only shows the final query that was generated by LINQ

Create Alerts for SQL Server Mirroring Failovers

You might have SQL Mirroring enabled in your Enterprise but might not have alerting enabled or you don’t know how to enable it.  If you want to enable that functionality just follow this 2 easy steps.

1. Creating SQL Alerts in SQL Server Agent

Go to your SQL Server Agent and on the Alert section, right-click on it and choose New Alert.

Now you are presented with the alert properties and just fill it up with the proper information
Name :  Choose a name for this Alert
Type :  There are 3 different types and since were interested in Database Mirroring State Change we need to query WMI so choose WMI event alert
Namespace : The namespace it auto populated to the namespace you needed so don’t change it
Query : Now write your WMI Query.  We are looking for the DATABASE_MIRRORING_STATE_CHANGE class so your query goes like this.

SELECT * FROM DATABASE_MIRRORING_STATE_CHANGE

Now there 19 Properties for that class and you are mainly interested on DatabaseName or DatabaseID and State.  DatabaseName or DatabaseID will be the database you are monitoring and State would be Mirroring State of the Database and here are the different values.

  • 0 = Null Notification
  • 1 = Synchronized Principal with Witness
  • 2 = Synchronized Principal without Witness
  • 3 = Synchronized Mirror with Witness
  • 4 = Synchronized Mirror without Witness
  • 5 = Connection with Principal Lost
  • 6 = Connection with Mirror Lost
  • 7 = Manual Failover
  • 8 = Automatic Failover
  • 9 = Mirroring Suspended
  • 10 = No Quorum
  • 11 = Synchronizing Mirror
  • 12 = Principal Running Exposed

Having that in mind you can now filter out alerts by those two properties, so for example I want to have an alert for “SAMPLE” Database where the mirroring Failed Over my query would look like this

SELECT * FROM DATABASE_MIRRORING_STATE_CHANGE WHERE DatabaseName = 'SAMPLE' AND State = 8

Now you have an alert you need now to define notifications for operators, you can do that by going to the Response tab and choose who do you want to notify and what notifications they want to receive.

Now you define additional options such as message you want to send to those operator

At this stage you have a working alert.  But it might not work as by default the SQL Server Agent Alert System Mail Profile is disabled

2. Activating SQL Server Agent Alert System Mail Profile.

Go to SQL Server Agents properties

Then go to Alert System Tab then tick Enable mail profile and choose the Mail system installed on your server as well as its profile

Now your done, all you have to do is test, You will get a message similar to this once it’s activated.

Median Calculation using TSQL (no cursors)

Before we start lets define what a Median is first, in laymans term the Median is the “middle number” in a sorted list of numbers.

So in a sample like

3, 4, 5, 8, 10, 11, 100

The middle number which is the median as well is number 8 as you have 3 numbers in before and after number 8, now  what if there are two numbers in the middle and that will happen if there is an even amount of number, in that case we need to find the middle two numbers then get their average or add them (the 2 middle numbers) together and divide them by 2 so on the example earlier lets try to add another number

3, 4, 5, 8, 10, 11, 100, 101

So here the middle number is 8 and 10 and their average is 9 so thats the median number.

Now you know you know what median is, now we try to apply that in SQL, there are a lot of solution but mostly  it uses cursors or complex TSQL queries as its not directly supported by TSQL, and here are the only grouping functions available, isn’t it good to add the median function in the next versions of SQL

thanks to CTE or common table expressions things got easiser.

So lets get started lets say you have this table and you want to calculate the median price for each product

Now to view it easily you can sort it by Price and get the middle numbers

Now to achieve that on TSQL, here are the codes

With
MedianResults
as
(
Select
Name,Price ,
Row_Number() OVER(Partition by Name Order by Price) as A,
Row_Number() OVER(Partition by Name Order by Price desc) as B
from ProductStats

)
Select Name, Avg(Price) as Median
From MedianResults
Where Abs(A-B)<=1
Group by Name

Doing that, the results will yeild

UPDATE!!!

One of my article readers at code project found a bug on the above post and it happens if you have values that are “tied”, the ROW_NUMBER result is not “aligned” as you might want, and it produces an incorrect value.  As he suggested I used a set with tied numbers such as

3, 4, 5, 8, 101, 101, 101, 101

and in that set it sould output 54.5 but it did not and isntead its outputting 8 instead as the ROW_NUMBERS becomes unaligned when you use similar value digits.  So his solution was this :

WITH    
ResultA 
AS 
(SELECT	Name, Price, ROW_NUMBER() OVER ( PARTITION BY Name ORDER BY Price ) AS A FROM  ProductStats), 
ResultB
AS 
(SELECT	Name, Price, A, ROW_NUMBER() OVER ( PARTITION BY Name ORDER BY Price DESC, A DESC ) AS B FROM ResultA)
SELECT  
Name, 
Avg(Price) as Median
FROM ResultB
WHERE  Abs(A - B) <= 1
GROUP BY Name

and I totally agree!

How to handle Database Failover from your Application

There are many ways that you can let your application handle failover situations and here are some suggestions I had used in my past projects.  But before you start with my suggestions make sure that you have the same database names for both and the same goes for user accounts and permissions.  The items here are ordered on how highly I suggest each solution.

1. Modify your connection strings to do Failover

This is the best practice method and it is just as easy as modifying your connection string to handle the failover (yes you can do that),  Because if you connect with ADO.Net or SQL Native Client your application can take advantage of the drivers ability to handle the scenario and redirect the connection when a failover occurs.  To do that you need to use this connection string

Data Source=SQLServerPrimary;Failover Partner=SQLServerMirror;Initial Catalog=DataBase;Integrated Security=True;

Where Data Source is your Primary SQL and Failover Partner will be your SQL Mirror. The above example is for ADO.Net for others use:

OLE DB: FailoverPartner
ODBC: Failover_Partner
JDBC: failoverPartner

2. DNS CNAME

If item 1 does not meet your requirement or its imposible to do the next best thing is using this method, you just need to change your config files to point to the assigned CNAME (and thats all you have to do unless there are some hard coded server names), the change of CNAME can be done automatically or manually and its network admins your choice which option they want to do.  What this does your network guys assigns an alias for your Database Server so for example you have a database name called DBServer1which is the primary and DBServer2 which is the mirror, they can create an alias called DBServerDNS which points to DBServer1 then in a case of failover the entry will be then changed to DBServer2 which you dont need to worry as your application connects to DBServerDNS all the time.

3. Manual Modifications

Finally, if both of above will not work in your situation then this is your last resort, a manual approach (not really recommended) as this cant happen on real time.  Now once the database is running on another server you can either:

a. Create a SQL Server alias just go to SQL Server Configuration Manager and add a SQL Server Alias setting.  With this you dont need to modify any connection strings on your application and when the real server goes back to life just remove the alias setting.

b. Change your config files

Final Note:

Depending on how your applications are written using the solutions above (item 1 and 2) doesn’t mean that your application will not crash as that the network connection to the database server will be lost during a failover and if the exception is not properly handled then issues will arise.  So the best way to do this is to Dispose any commands you are running on the failed SQL Server and Close all the connections so you can reconnect properly on the Mirror Server on the next Execute Command.

Error in Linking new SQL Server x64 versions (2005 and 2008) to old SQL Server x32 versions (2000 and 7.0)

If you encounter this error:

OLE DB provider "SQLNCLI10" for linked server "LinkedServerName" returned message "The stored procedure required to complete this operation could not be found on the server. Please contact your system administrator.".
Msg 7311, Level 16, State 2, Line 1
Cannot obtain the schema rowset "DBSCHEMA_TABLES_INFO" for OLE DB provider "SQLNCLI10" for linked server "LinkedServerName". The provider supports the interface, but returns a failure code when it is used.

Then most probably you are trying to link a new SQL x64 version with an old SQL 2000 x32 Server. The error happens as when the new SQL Server x64 version is trying to run the distributed queriy on the linked server it uses the stored procedure which is called “sp_tables_info_rowset_64″ which is definitely not on the old x32 versions but if you do the other way around linking the old to new then it works as it calls the “sp_tables_info_rowset” which is already on the new x64 versions.

There are two workarounds

1. The easy way which resolves the problem immediately and only that problem.

To fix the issue just add the stored procedure “sp_tables_info_rowset_64″ on the old SQL Server Database and things should work fine.

To add the the stored procedure just run the following on the Query Analyzer of the old SQL Server

CREATE PROCEDURE sp_tables_info_rowset_64

@table_name SYSNAME,
@table_schema SYSNAME = NULL,
@table_type SYSNAME(255) = NULL
AS

DECLARE @Result INT SET @Result = 0
EXEC @Result = sp_tables_info_rowset @table_name, @table_schema, @table_type
GO

2. Now if you want to do the proper way you have to manually run the script called instcat.sql which you can find at the “<SQL Server Installation Path>\MSSQL\Install”.

But before doing that you need to make sure that you have at least SP3 on your SQL Server 2000 or SQL Server 7.0

Now to run “instcat.sql” manually you need copy and paste this on your Query Analyzer on your Old SQL Server Database

osql -E -S <LinkedServerName> -i <Location>\instcat.sql
Follow

Get every new post delivered to your Inbox.

Join 774 other followers