How to write this LINQ with foreach in a better way - asp.net-mvc-3

I was doing project in MVC3 with Entity framework. I have a LINQ query with foreach. Everything is fine. But when the data size goes up, i was facing performance issues. I dont have much experience with LINQ. So I couldn't fix my issue. Pls have a look at my code and provide a better suggestion for me.
Code
List<int> RouteIds = db.Cap.Where(asd => asd.Type == 3).Select(asd => asd.UserId).ToList();
var UsersWithRoutingId = (from route in db.RoutingListMembers
where RouteIds.Contains(route.RoutingListId.Value) && route.User.UserDeptId == Id
select
new RoutingWithUser
{
UserId = route.UserId,
RoutingId = route.RoutingListId
});
var ListRouteValue = (from cap in db.CareAllocationPercents
where cap.Type == 3
select new UserWithDeptId
{
Year = (from amt in db.CareAllocations where amt.CareItemId == cap.CareItemId select amt.Year).FirstOrDefault(),
UserId = cap.UserId,
UserDeptId = (from userdept in db.Users where userdept.Id == cap.UserId select userdept.UserDeptId).FirstOrDefault(),
});
List<UserWithDeptId> NewRouteList = new List<UserWithDeptId>();
ListRouteValue = ListRouteValue.Where(asd => asd.Year == Year);
foreach (var listdept in ListRouteValue)
{
foreach (var users in UsersWithRoutingId)
{
if (users.RoutingId == listdept.UserId)
{
UserWithDeptId UserwithRouteObj = new UserWithDeptId();
UserwithRouteObj.UserId = users.UserId;
UserwithRouteObj.Year = listdept.Year;
UserwithRouteObj.UserDeptId = db.Users.Where(asd => asd.Id == users.UserId).Select(asd => asd.UserDeptId).FirstOrDefault();
NewRouteList.Add(UserwithRouteObj);
}
}
}
NewRouteList = NewRouteList.Where(asd => asd.UserDeptId == Id).ToList();
Thanks,

You have to use join in first statement. Examples of how to do this are for example here: Joins in LINQ to SQL

I have some idea for you:
First:
Take care to complete your where close into your linq query to get just what you need to.
With Linq on collection, you can remove one foreach loop. I don't know the finality but, i've tryied to write something for you:
var UsersWithRoutingId = (from route in db.RoutingListMembers
where RouteIds.Contains(route.RoutingListId.Value) && route.User.UserDeptId == Id
select
new RoutingWithUser
{
UserId = route.UserId,
RoutingId = route.RoutingListId
});
var ListRouteValue = (from cap in db.CareAllocationPercents
where cap.Type == 3
select new UserWithDeptId
{
Year = (from amt in db.CareAllocations
where amt.CareItemId == cap.CareItemId && amt.Year == Year
select amt.Year).FirstOrDefault(),
UserId = cap.UserId,
UserDeptId = (from userdept in db.Users
where userdept.Id == cap.UserId && userdept.UserDeptId == Id
select userdept.UserDeptId).FirstOrDefault(),
});
List<UserWithDeptId> NewRouteList = new List<UserWithDeptId>();
foreach (var listdept in ListRouteValue)
{
var user = UsersWithRoutingId.Where(uwri => uwri.RoutingId == listdept.UserId).FirstOrDefault();
if (user != null)
{
NewRouteList.Add(new UserWithDeptId { UserId=user.UserId, Year=listdept.Year, UserDeptId=listdept.UserDeptId });
}
}
return NewRouteList
Is that right for you ?
(i don't poll the db.user table do get the UserDeptId for the NewRouteList assuming that the one in the listdept is the good one)
Second:
Take care of entity data loading, if you have tables with foreign key, take care to remove the lazy loading if you don't need the children of your table to be loaded at same time. Imagine the gain for multiple table with foreign key pointing to others.
Edit:
Here is a link that explain it:
http://msdn.microsoft.com/en-us/library/vstudio/dd456846%28v=vs.100%29.aspx

Related

Fastest method to Fill Data in Nested List

I am trying to fill a viewmodel(List) from Datatable.
Customer and WorkingTime
WorkingTime is a child List of Customer. There are several entries for a Customer.
Using Adapter.Fill Meathod
DataTable dtCust=GetCustomersInTable(Uid);
DataTable dtWorking=GetAllCustWorkTimeInTable(Uid);
var Customers = (from C in dtCust.AsEnumerable()
select new CustomerViewModel
{
CustomerId = (long)C["CustomerId"],
Address1 = DBNull.Value == C["Name"] ? "" : Convert.ToString(C["Name"]),
Address2 = DBNull.Value == C["Address"] ? "" : Convert.ToString(C["Address"]),
City = DBNull.Value == C["City"] ? "" : Convert.ToString(C["City"]),
WorkingTime = GetCustWorkingTime(dtWorking, Convert.ToInt64(C["CustomerId"])),
}}).ToList();
private List<CustomerWorkingTime> GetCustWorkingTime(DataTable dtWorking, long CustomerId)
{
var CustomerWorkingTimes = (from C in dtWorking.AsEnumerable()
where Convert.ToInt64(C["CustomerId"]) == CustomerId
select new CustomerWorkingTime
{
AfterNoonFrom = Convert.ToDateTime(C["AfterNoonFrom"]),
AfterNoonUntil = Convert.ToDateTime(C["AfterNoonUntil"])
}).ToList();
return result;
}
I need to take whole data. It used not for displaying in UI.
This meathod is taking too much time to fill data. especially CustomerWorkingtime Filling.
Please suggest a better method to fill data.
Pagination is not possible here.
Existing setup is in EntityFramework and performance is poor.
Yu can try playing around with the TPL Parallel.ForEach, I think in your case this might give you a boost.
for example:
var Customers = Parallel.ForEach(dtCust.AsEnumerable(), c =>
select new CustomerViewModel
{
CustomerId = (long)C["CustomerId"],
Address1 = DBNull.Value == C["Name"] ? "" : Convert.ToString(C["Name"]),
Address2 = DBNull.Value == C["Address"] ? "" : Convert.ToString(C["Address"]),
City = DBNull.Value == C["City"] ? "" : Convert.ToString(C["City"]),
WorkingTime = GetCustWorkingTime(dtWorking, Convert.ToInt64(C["CustomerId"])),
}}).ToList();
Or in your "GetCustWorkingTime":
var result= dtWorking
.AsEnumerable()
.AsParallel()
.Where(c => Convert.ToInt64(C["CustomerId"]) == CustomerId)
.Select(c= > new CustomerWorkingTime
{
AfterNoonFrom = Convert.ToDateTime(C["AfterNoonFrom"]),
AfterNoonUntil = Convert.ToDateTime(C["AfterNoonUntil"])
}).ToList();
you should try them separately, and together, to see which one gives you the best performance (depends on the size of the tables).
Note: using both together might cause problems since in "GetCustWorkingTime" you'll end up iterating over the same table by multiple tasks in the same time.

LINQ to Entities does not recognize the method 'Boolean HasFlag(System.Enum)' method, and this method cannot be translated into a store expression

I have this service:
//seroiunoiweucroewr
///wercewrwerwerwer
//wcererewrwerwer
public List<UserRoleContract> GetRolePagesByUserId(long plngUserId, DisplayType displayType)
{
List<UserRoleContract> result = new List<UserRoleContract>();
using (CitiCallEntities context = new CitiCallEntities())
{
try
{
//var DisplayList = Utility.GetEnumDescriptions(typeof(DisplayType)).ToList();
//var selectValue = DisplayList.Where(i => i.Key == (byte) DisplayType.Windows).FirstOrDefault();
result = (from oUser in context.User
join oUserRole in context.UserRole on oUser.Id equals oUserRole.UserId
join oRoleRightsPage in context.RoleRightsPage.Where(i => i.IsActive == true)
on oUserRole.RoleId equals oRoleRightsPage.RoleId
join oApplicationPage in context.ApplicationPage.Where(i => i.IsActive == true)
on oRoleRightsPage.PageId equals oApplicationPage.Id
join oRole in context.Role on oUserRole.RoleId equals oRole.Id
join oEmployee in context.Employee on oUser.EmployeeId equals oEmployee.Id
join oSection in context.Section on oEmployee.SectionId equals oSection.Id
where oUser.IsActive == true && oUser.Id == plngUserId
&& oRole.IsActive == true && (((DisplayType)oRoleRightsPage.DisplayType).HasFlag(displayType))
//am getting error in has flag
// am having three display type web, windows and all
// how to overcome
select new UserRoleContract
{
UserId = oUser.Id,
RoleId = oRole.Id,
RoleName = oRole.RoleName,
PageID = oApplicationPage.Id,
PageName = oApplicationPage.PageName,
IsOPsCtrl = oRole.IsOPsCtrl,
ISOPsCtrlFor = oRole.OPsCtrlFor,
SectionId = oSection.Id,
DisplayType = oRoleRightsPage.DisplayType,
}).Distinct().ToList();
}
catch (Exception exception)
{
HandleExpcetion(exception);
//throw new CitiCallException(exception.Message);
}
}
return result;
}
I am getting Linq error in has flag conversion, how do I overcome this problem?
you are geeting error because HasFlag method is not paresent in database i.e. it might be part of language or local function in code which is not present in database.
So when query is translated it found this method is not available and that is the reason you are getting error.
one solution to avoid this error is
Brind all data from databae
Than filter than data, i.e. apply HasFlag method of it.
But this will bring all data and might decrease performance.
Example is
remove this line (((DisplayType)oRoleRightsPage.DisplayType).HasFlag(displayType) from your query
var list = querieddata //first fetch data without hasflag condition/method
.AsEnumerable() // Rest of the query in-process
.Where(oRoleRightsPage=> ((DisplayType)oRoleRightsPage.DisplayType).HasFlag(displayType))//apply condition here once fetching done
.ToList();
The HasFlag method has no equivalent in Linq to Entities which is why you get that error. You can get around it by using bitwise comparison instead of using HasFlag, for example this:
((DisplayType)oRoleRightsPage.DisplayType).HasFlag(displayType)
Becomes:
(oRoleRightsPage.DisplayType & displayType) > 0

LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method

I've looked at the various solutions here but none of them seem to work for me, probably because I'm too new to all this and am groping in the dark a bit. In the code below, the object "appointment" contains some basic LDAP information. From a list of such objects I want to be able to get a single record, based on employee id. I hope the code here is sufficient to illustrate. FTR, I've tried various formulations, including trying to use from and a select. All fail with the error given in the Title above.
IQueryable<appointment> query = null;
foreach(var record in results)
{
BoiseStateLdapDataObject record1 = record;
query = db.appointments.Where(x => x.student_id == record1.Values["employeeid"]);
}
if (query != null)
{
var selectedRecord = query.SingleOrDefault();
}
Try to move employee id getting out of query:
IQueryable<appointment> query = null;
foreach(var record in results)
{
var employeeId = record.Values["employeeid"];
query = db.appointments.Where(x => x.student_id == employeeId);
}
if (query != null)
{
var selectedRecord = query.SingleOrDefault();
}

showing multiple rows from database in datagridview using entity framework

I am trying to show multiple records from database in datagridview but I'm having only a single record all the time.
2 tables are involved in this query, from 1st table I acquire all the id's those fulfill the condition and from 2nd table I am getting the user information.
1st table is tblUsers_Roles and 2nd is tblUsers.
These tables have primary/foriegn key relationship.
Here is my code:
IEnumerable<tblUsers_Role> id = db.tblUsers_Role.Where(a => a.User_Role == selectRole);
foreach (var user in id)
{
var userinfo = from b in db.tblUsers
where b.User_Id == user.User_Id
select new { b.First_Name, b.Last_Name, b.Status, b.Authenticated };
dgvResults.DataSource = userinfo.ToList();
dgvResults.Show();
}
You are assigning the grid in the loop. That is not going to work. Maybe something like this will work:
var userinfo =(from ur in db.tblUsers_Role
join u in db.tblUsers
on ur.User_Id equals u.User_Id
where ur.User_Role == selectRole
select new
{
u.First_Name,
u.Last_Name,
u.Status,
u.Authenticated
}).ToList();
dgvResults.DataSource = userinfo;
dgvResults.Show();
Or a alteretive would be like this:
var roles=db.tblUsers_Role
.Where(a => a.User_Role == selectRole)
.Select (a =>a.User_Id).ToList();
var userinfo=
(
from u in db.tblUsers
where roles.Contains(u.User_Id)
select new
{
u.First_Name,
u.Last_Name,
u.Status,
u.Authenticated
}
).ToList();
dgvResults.DataSource = userinfo;
dgvResults.Show();
Not as nice as the first one. But maybe you understand the concept.

Access any data that is not contained in grouped element

from teamBudget in TeamBudgets
where teamBudget.TeamID == 71002
join teamBroker in TeamBrokers on 71002 equals teamBroker.TeamID
join goal in Goals on teamBroker.GlobalBrokerID equals goal.GlobalBrokerID
group goal by goal.GlobalBrokerID into g
select new
{
// TeamID=teamBroker.TeamID,
// MTDGoal=teamBudget.Sum(t => t.Budget),
RevenueMTDCurrent = g.Sum(x => x.RevenueMTDCurrent)
}
Commented part is a problem. How to access any data that is not contained in grouped element?
you need to Group multiple fields then only you can access that data.
like
var result = from i in
(from uh in db.UserHistories
where uh.User.UserID == UserID && uh.CRMEntityID == (int)entity
select new { uh.ActionID, uh.ActionType, uh.ObjectID })
group i by new { i.ActionID, i.ActionType, i.ObjectID } into g
select new { g.ActionID, g.ActionType, g.ObjectID };
Hope this will help

Resources