Reputation: 2480
Recently I have come across the code to fix a bug as shown below. It is just hard to understand it and make the changes seem risky as well. Is there any easy way to breakdown such a query i.e. De Morgan's law or re-writing in different way.
SomeReturnType xyz()
{
return (from m in this.context.tblMessages
where m.SystemActionID.HasValue &&
(userID == null || m.RecipientID == null || m.RecipientID == userID) &&
m.TargetUserType == userType &&
(
(territoryID.HasValue && !m.TerritoryID.HasValue) ||
(!territoryID.HasValue && !m.TerritoryID.HasValue) ||
(territoryID.HasValue && m.TerritoryID.HasValue && territoryID.Value == m.TerritoryID.Value)
) &&
(
(regionID.HasValue && !m.RegionID.HasValue) ||
(!regionID.HasValue && !m.RegionID.HasValue) ||
(regionID.HasValue && m.RegionID.HasValue && regionID.Value == m.RegionID.Value)
) &&
(
(teamID.HasValue && !m.TeamID.HasValue) ||
(!teamID.HasValue && !m.TeamID.HasValue) ||
(teamID.HasValue && m.TeamID.HasValue && teamID.Value == m.TeamID.Value)
) &&
m.SystemActionData == additionalData &&
(!completed.HasValue || m.IsSystemActionCompleted == completed.Value)
select m).FirstOrDefault();
} //function xyz ends
Upvotes: 0
Views: 1102
Reputation: 13458
I am using IQueryable in such cases. I have used some dummy names instead of your example to demonstrate idea itself - you need to apply "where" clauses step by step.
IQueryable<Account> query1 = from account in storage.Accounts
where account.Username == username
select account;
IQueryable<SomeNewTypeIfNecessary> query2 = from account in query1
where account.ID > 100
select new SomeNewTypeIfNecessary { ID = account.ID };
// Final call doing real query to database.
List<SomeNewTypeIfNecessary> accounts = query2.ToList();
In addition such technique allows to dynamically add desired statements (mostly where and orderby clauses) to get specific data dependent on some user choice (for example sort direction).
Upvotes: 1
Reputation: 131423
First, this code looks like an attempt to move bad SQL code to LINQ: the statements contain parameter checks in an attempt to create a query that can have "optional" parameters. It looks like the author tried to move a stored procedure to code.
Both of these things are really bad ideas.
For example, you can change this:
(from m in this.context.tblMessages
where m.SystemActionID.HasValue &&
(userID == null || m.RecipientID == null || m.RecipientID == userID) &&
m.TargetUserType == userType &&
(
(territoryID.HasValue && !m.TerritoryID.HasValue) ||
(!territoryID.HasValue && !m.TerritoryID.HasValue) ||
(territoryID.HasValue && m.TerritoryID.HasValue && territoryID.Value == m.TerritoryID.Value)
) &&
to this
if (userId==null)
return null;
var query=from m in this.context.tblMessages
where m.SystemActionID != null
&& (m.RecipientID == null || m.RecipientID == userID)
&& m.TargetUserType == userType ;
if (territoryID.HasValue)
query=query.Where(m=>m.TerritoryId==null || m.TerritoryId==teritoryID);
You can simplify the rest of the statement in the same way, eg:
if (regionID.HasValue)
query=query.Where(m=>m.RegionId==null || m.RegionId==regionID);
if (teamID.HasValue)
query=query.Where(m=>m.TeamID==null || m.TeamID==teamID);
The LINQ query should never compare parameters against constants - why force the server to do a constantc check you can easily do on the client?
This will result in queries that are a lot simpler and easier for the server to optimize
Upvotes: 3