Converting sql stored procedure to Linq/Lambda - c#

I have a stored procedure that displays a menu from table menus and then checks the user type to change the name of a menu item depending on the User type.
MenuID,
Case When #UserType <> 'E' and MenuName='Admin' then 'My Profile' When #UserType = 'A' and MenuName='Change Password' then 'Change Agent Password' else MenuName End As MenuName,
ParentID,
MenuLink,
IconImagePath,
MenuTarget
from tblMenus
Where IsDeleted=0 and
IsEnabled=1 and
MenuID in (Select MenuID From #MenuChild)
Or MenuID In(SELECT ParentID FROM #MenuChild t CROSS APPLY dbo.FindRoot2(t.menuid))
Or MenuID = 1 order by MenuOrder, MenuName
I have been working on converting the entire procedure to linq and it looks like this now.
public List<tblMenu> getmainmenusclass()
{
var UserInfo = GetUserInfo();
UserType = UserInfo[0].UserTypeID;
var menus = DataAccess.Menus.FindByExp(x => x.IsDeleted == false).ToList();
if (UserType == "E")
{
menus = DataAccess.Menus.FindByExp(x => x.IsDeleted == false).ToList();
}
if (UserType == "A")
{
var agentDist = GetAgentDistribution();
Distribution = agentDist[0].AgtDistChannel;
if (Distribution == "P" || Distribution == "S")
{
menus = DataAccess.Menus.FindByExp(x => x.IsDeleted == false && x.MenuCategory != "DGB").ToList();
}
if (Distribution == "G" || Distribution == "B")
{
menus = DataAccess.Menus.FindByExp(x => x.IsDeleted == false && x.MenuCategory != "DPS").ToList();
}
if (Distribution == "W" || Distribution == "P")
{
menus = DataAccess.Menus.FindByExp(x => x.IsDeleted == false && x.MenuCategory != "DGB" && x.MenuName != "NEW Quoting Tool").ToList();
}
}
else
{
var notAllowedMenuCategories = new[] { "DPS", "DGB", "D" };
menus = DataAccess.Menus.FindByExp(x => x.IsDeleted == false && !notAllowedMenuCategories.Contains(x.MenuCategory)).ToList();
if (Distribution == "G")
{
var notAllowedMenuCategoriesForG = new[] { "DPS", "DGB", "D", "PB" };
menus = DataAccess.Menus.FindByExp(x => x.IsDeleted == false && !notAllowedMenuCategoriesForG.Contains(x.MenuCategory)).ToList();
}
}
if (MarketingGroup != "BWCU" && MarketingGroup != "''")
{
menus = DataAccess.Menus.FindByExp(x => x.IsDeleted == false && x.MenuName != "NEW Quoting Tool").ToList();
}
return menus;
}
I am not sure how to change that last bit so that it looks through the menu list and depending on the usertype changes the menu name from "Admin" to "My Profile".

If i understood you corectly, this is one way.
menus = (from p in DataAccess.Menus
where p.usertype == 'A'
select new tblMenu
{ //your properties except name
Name="My Profile"
}).Union(from p in DataAccess.Menus
where p.usertype != "A"
select p).ToList();
Change the usertype condition as you require. In this linq i'm taking all the elements from DataAccess.Menus and if the user type is A then i change one property as you requested. Then i'm joining that result with all the other elements that didn't match that usertype

Related

How to refactor this linq query?

I have this below linq query of which 95% remains the same across 6 different scenarios.How can I refactor it so that I can write in one function and use it for all the cases?
var results = await (from r in Requests
join ra in RequestAuthorisers
on cr.ID equals ra._REQUEST_ID
where cr.FACILITY_ID == facilityId
&& (r._REQUEST_STATUS_ID == RequestSubmitted || r._REQUEST_STATUS_ID == RequestPartiallyAuthorised )//check against more status codes based on different conditions
&& ra.FACILITY_USER_ID == facilityUserId//don't check this if admin
&& ra.AUTHORIZATION_DATE != null
&& ra.REJECTION_DATE == null
select new
{
FacilityId = r.FACILITY_ID,
VersionId = r.VERSION_ID,
CreatedByTitle = r.CREATED_BY_USER_TITLE,
CreatedByFirstName = r.CREATED_BY_USER_FIRST_NAME,
CreatedByLastName = r.CREATED_BY_USER_LAST_NAME,
RequestDate = r.SUBMITTED_DATE,
RequestId = r.ID,
RequestType = r._TYPE_ID,
Status = r._REQUEST_STATUS_ID
}).ToListAsync();
var RequestResponse = results.Select(
r => new RequestResponseDto
{
FacilityId = r.FacilityId,
VersionId = r.VersionId,
CreatedByTitle = r.CreatedByTitle,
CreatedByFirstName = r.CreatedByFirstName,
CreatedByLastName = r.CreatedByLastName,
RequestDate = Convert.ToDateTime(r.RequestDate).ToString("s"),
RequestType = ((RequestType)r.RequestType).ToString(),
Status = ((RequestStatus)r.Status).ToString()
}).ToList();
As you can the scenarios/queries differ only by if isAdmin and check against more status codes(as commented above), rest of the part becomes same.
I am using LINQ to EF(DB First).
Thanks in advance.
Why not pass a boolean into the method:
public RequestResponseDto MyMethod(bool isAdmin, Status status)
{
...
&&
((status == Status.Status1 && (r._REQUEST_STATUS_ID == RequestSubmitted || r._REQUEST_STATUS_ID == RequestPartiallyAuthorised))
||
(status == Status.Status2 && (r._REQUEST_STATUS_ID == Something))
||
(status == Status.Status3 && (r._REQUEST_STATUS_ID == SomethingElse || r._REQUEST_STATUS_ID == AnotherThing)))
...
&& (isAdmin || ra.FACILITY_USER_ID == facilityUserId)
...
}

How to recursively call Where clause in Linq or SQL

Let's say I have the following filter parameters:
Type="Student"
School = "High"
ReferenceID = "123abc"
PaymentOnFile= "Y"
Now, I need to find 1st student based on these 4 parameters. If no students are found then I need to find them based on the 3 parameters, if no students are found then use 2 parameters, etc.
Here's my current code:
var Student = db.Students.Where(x=> x.School == School && x.Type == Type && x.ReferenceID == ReferenceID && x.PaymentOnFile == PaymentOnFile).FirstOrDefault();
if (Student == null)
{
Student = db.Students.Where(x=> x.School == School && x.Type == Type && x.ReferenceID == ReferenceID).FirstOrDefault();
}
if (Student == null)
{
Student = db.Students.Where(x=> x.School == School && x.Type == Type).FirstOrDefault();
}
if (Student == null)
{
Student = db.Students.Where(x=> x.School == School).FirstOrDefault();
}
return Student;
This works but it is not very efficient and ugly. What is a better way to do this? Maybe using expression trees or something else but I cannot figure it out.
SQL also works!
I think something like this would work:
var student = db.Students
.Where(x => x.School == school)
.OrderBy(x => (x.Type == type) ? 0 : 1)
.ThenBy(x => (x.ReferenceID == referenceId) ? 0 : 1)
.ThenBy(x => (x.PaymentOnFile == paymentOnFile) ? 0 : 1)
.FirstOrDefault();
This dynamic solution will perform exactly your recursive logic and will work at case of Linq and EF. You can add another conditions(to predicates, order matters), solution(for loop) will remain the same.
var predicates = new List<Expression<Func<Student, bool>>>
{
x => x.School == "High",
x => x.Type == "Student",
x => x.ReferenceID == "123abc",
x => x.PaymentOnFile == "Y",
};
Student student = null;
for(var i = 0; i < predicates.Count; i++)
{
var query = db.Students.AsQueryable();
for (var j = 0; j < predicates.Count - i; j++)
query = query.Where(predicates[j]);
if ((student = query.FirstOrDefault()) != null)
break;
}
i guess if you could apply this query logic which can help you better
SELECT TOP 1
*
FROM Student AS S
ORDER BY CASE
WHEN S.School = #School AND S.[Type] = #Type AND S.ReferenceID = #ReferenceID AND S.PaymentOnFile = #PaymentOnFile THEN
1
WHEN S.School = #School AND S.[Type] = #Type AND S.ReferenceID = #ReferenceID THEN
2
WHEN S.School = #School AND S.[Type] = #Type THEN
3
WHEN S.School = #School THEN
4
END ASC

Linq query where Contains() checks for list of strings

Consider the Following Query:
var query = from o in this.OrderManager.LoadOrders()
join s in this.SkuManager.LoadSkus() on o.SKU equals s.SKU
where o.ORDER_ID == orderId
let parcelItem = o.RPU != "Y" && o.DROP_SHIP != "Y" && s.TRUCK_SHIP != "T" && o.SKU != "ABC-123" && o.SKU != "XYZ-789" && o.SKU != "JKL-456"
select new OrderMailLineItem
{
OrderId = o.ORDER_ID,
Sku = s.SKU,
WarehouseId = s.SITE_ID,
QualifyingItem = qualifyingItem,
OversizedItem = parcelItem && (s.DROP_SHIP == null)
};
I would like to be able to write the line let parcelItem = ... to be more like !o.SKU.Contains(skuList) where:
List<string> skuList = new List<string> { "ABC-123", "XYZ-789", "JKL-456"};
You should check whether SKU is not in list instead of checking whether list is in SKU:
let parcelItem = !skuList.Contains(o.SKU)
!skuList.Contains(o.SKU) is exactly how you'd usually do it.
But you could write an In operator, if you like:
public static class ExtensionMethods
{
public static bool In<T>(this T t, params T[] values)
=> values.Contains(t);
}
...
let parcelItem = o.RPU != "Y" && o.DROP_SHIP != "Y" && s.TRUCK_SHIP != "T" &&
!o.SKU.In("ABC-123", "XYZ-789", "JKL-456")
I doubt that'll work with Linq to SQL though.
I don't see why this wouldn't work. You would just need to check those three Yes/No flags in addition to your SKU list.
var skuList = new[] { "ABC-123", "XYZ-789", "JKL-456"};
var query = from o in this.OrderManager.LoadOrders()
join s in this.SkuManager.LoadSkus() on o.SKU equals s.SKU
where o.ORDER_ID == orderId
let parcelItem = o.RPU != "Y" && o.DROP_SHIP != "Y" && s.TRUCK_SHIP != "T" && skuList.Contains(o.SKU)
select new OrderMailLineItem
{
OrderId = o.ORDER_ID,
Sku = s.SKU,
WarehouseId = s.SITE_ID,
QualifyingItem = qualifyingItem,
OversizedItem = parcelItem && (s.DROP_SHIP == null)
};

How to select with multiple condition in linq?

I use MVC 4 and There are 5 dropdownlist in my view. when dropdownlist selected text and clicked button search I want to select from 4 table where dropdownlist selected . and if dropdownlist don't selected not checked in condition.
(from met in db.tblMet
from useMet in db.tblUseMet.Where(m => m.UseMetID == met.UseMetID_FK).DefaultIfEmpty()
from typeMet in db.tblTypeMet.Where(m => m.TypeMetID == met.TypeMetID_FK).DefaultIfEmpty()
from mod in db.tblMod.Where(m => m.ModID == met.ModID_FK).DefaultIfEmpty()
from affair in db.tblAffairs.Where(m => m.AffairID == met.AffairID_FK).DefaultIfEmpty()
from desert in db.tblDeserts.Where(m => m.DesertID == met.DesertID_FK).DefaultIfEmpty()
from city in db.tblCities.Where(m => m.CityID == met.CityID_FK).DefaultIfEmpty()
from user in db.tblUsers.Where(m => m.UserID == met.UserIDCreate_FK).DefaultIfEmpty()
from userCh in db.tblUsers.Where(m => m.UserID == met.UserIDChange_FK).DefaultIfEmpty()
from setting in db.tblSettings.Where(m => m.SettingID == met.SettingID_FK).DefaultIfEmpty()
from sim in db.tblSims.Where(m => m.SimID == mod.SimID_FK).DefaultIfEmpty()
from typemod in db.tblTypeMod.Where(m => m.TypeModID == sim.TypeModID_FK_Kind).DefaultIfEmpty()
from gro in db.tblGroupMet.Where(m => m.GroupMetID == met.GroupMetID_FK).DefaultIfEmpty()
from group1 in db.tblMetRelateGroups.Where(x => x.MetID_FK == met.MetID).DefaultIfEmpty()
from status in db.tblStatus1.Where(m => m.StatusID == met.StatusID_FK).DefaultIfEmpty()
where ((city.CityID == City1||city.CityID !=null)
&& (typeMet.TypeMetID == Type1 || typeMet.TypeMetID != null)
&& (useMet.UseMetID == Usemeter1|| useMet.UseMetID != null)
&& (group1.GroupMetID_FK ==Group1 || group1.GroupMetID_FK != null)
&& (affair.AffairID ==Affair1 || affair.AffairID != null)
//|| desert.DesertID==Desert1
)
I want to selected dropdownlist selected and if don't select any, not checked in query. in above code select zero record.
You simply use the fluent syntax for this purpose. Here is an example:
var lQuery = from met in db.tblMet;
if (City1 != null)
lQuery = lQuery.Where(r => r.CityID == City1);
...
This way you can dynamically add conditions to your query.
If you want to add conditions with an logical OR you need a predicate builder, see https://stackoverflow.com/a/1775057/3936440.

Multiple CheckBox Selection Result using Linq without hardcoding

Illustration of database (actual column and table name is different)
EmployeeeID IsSicked IsRetired IsHoliday
1 false false true
2 true false true
3 true true true
4 false false false
What problem am I having?
I have a checkbox for IsSicked, IsRetired and IsHoliday in my application, which allow user to check multiple selection and submit.
The system will return list of employee from database based on the selection of user. For example, when user tick IsSicked and IsRetired, system will return list of user who is sicked and is retired from database.
Based on below code, I need to write 2^3 = 8 possibilities to get the result.
As the column expand to n columns(exluding employeeID column), I will need to write 2^n if else statement to get the query.
If there are 10 column (10 checkboxes selection), then i need to write 2^10 = 1024 if else statement
It seems to me that this supposed to be quite a common problem faced by developers, unfortunately i can't find a good solution to this online
Expectation of solution
Generic combination of C# code and/or LINQ query that no need hard code the logic as below if(...) else if (...)
var employee = db.Employee.AsQueryable():
if(model.IsSicked == true)
{
employee.Where(z => z.IsSicked == true)
}
else if(model.IsRetired == true)
{
employee.Where(z => z.IsRetired == true)
}
...
...
else if(model.IsSicked == true && model.IsRetired == true)
{
employee.Where(z => z.IsSick == true || z.IsRetired == true)
}
else if (model.IsSicked == true && model.IsRetired == true && model.IsHoliday == true)
{
employee.Where(z => z.IsSick == true || z.IsRetired == true || z.IsHoliday == true)
}
Why not use a enum with [Flags]:
[Flags]
public enum EmployeeStatus
{
None = 0,
Sick = 1,
Retired = 2,
Holiday = 4
}
In your database you can store this as an integer.
Your model becomes similar to
public class Employee
{
public int Id { get; set; }
public EmployeeStatus Status { get; set }
}
then LINQ queries become similar to:
employee.Where(z => z.Status == EmployeeStatus.Sick)
Because of using [Flags] you can do the following for multiple states, e.g. sick and retired:
employee.Where(z => z.Status == (EmpoyeeStatus.Sick | EmployeeStatus.Retired))
Please note that | is the boolean OR which translates in the following:
EmpoyeeStatus.Sick | EmployeeStatus.Retired
= 1 | 2
= b01 | b10
= b11
= 3
so effectively works as "and" in our logic.
You can use library called LINQKit and use its PredicateBuilder to create predicate with dynamic ORs.
var employee = db.Employee.AsQueryable():
var predicate = PredicateBuilder.False<Employee>();
if (model.IsSicked == true)
predicate = predicate.Or(p => p.IsSick == model.IsSick);
if (model.IsRetired == true)
predicate = predicate.Or(p => p.IsRetired == model.IsRetired);
if (model.IsHoliday == true)
predicate = predicate.Or(p => p.IsHoliday == model.IsHoliday);
var result = employee.AsExpandable().Where(c => predicate.Invoke(c)).ToList()
You can build up your Where predicate like this:
var employee = db.Employee.AsQueryable():
if(model.IsSicked == true)
employee = employee.Where(z => z.IsSicked == true)
if(model.IsRetired == true)
employee = employee.Where(z => z.IsRetired == true)
if (model.IsHoliday == true)
employee = employee.Where(z => z.IsHoliday == true)
Alternatively, you could do this:
employee.Where(z => (z.IsSicked || !model.IsSicked) &&
(z.IsRetired || !model.IsRetired) &&
(z.IsHoliday || !model.IsHoliday))

Categories

Resources