Update IQueryable result before using as join in next query - c#

I need to use Linq to Entity Framework to query a LOCATION table to get the record of the location code with the MAX effective date, then use that result as a join in the next query.
I BELIEVE I need to do convert before the IQueryable is used, because I have that last clause in the second query where I want to exclude records where the FLOOR code is in the excludedSchools list. That excludedSchools list will have the newLocationCode in it.
So, I need to update the values in the IQueryable result before I use it. Can I do this? Here is my code:
using (var db = new TheContext())
{
IQueryable<LocationTable> locatinWithMaxEffDate =
(from lc in db.LocationTable
where lc.EFF_STATUS == "A" && lc.EFFDT <= DateTime.Now
group lc by lc.LOCATION into g
select g.OrderByDescending(x => x.EFFDT).FirstOrDefault()
);
foreach (var location in locatinWithMaxEffDate.ToList())
{
string newLocationCode;
if(codeMappingDictionary.TryGetValue(location.FLOOR, out newLocationCode))
{
// how do I update locatinWithMaxEffDate FLOOR value
// with newLocationCode so it works in the query below?
location.FLOOR = newLocationCode;
}
}
var query =
(from fim in db.PS_PPS_FIM_EE_DATA
join mloc in locatinWithMaxEffDate on fim.LOCATION equals mloc.LOCATION
where
fim.EMPL_STATUS == PsPpsFimEeData.EmployeeStatusValues.Active
&& fim.AUTO_UPDATE == PsPpsFimEeData.AutoUpdateValues.Enabled
&& includeJobCodes.Contains(fim.JOBCODE)
&& !excludedSchools.Contains(mloc.FLOOR)
select new PpsAdministratorResult
{
SchoolId = mloc.FLOOR,
Login = fim.OPRID,
EmployeeId = fim.EMPLID,
}
With the code above, the locatinWithMaxEffDate does not have the updated FLOOR values. I can see why this is, but can't seem to fix it.
So far, I have tried introducing another list to ADD() the new location record to, then casting that as an IQueryable, but I get an error about primitive vs concrete types.

I decided to make things easier on myself. Since both sets of data are very small (fewer than 1000 records each) I call take the entire set of data as an annonymous type:
using (var db = new TheContext())
{
IQueryable<LocationTable> locatinWithMaxEffDate =
(from lc in db.LocationTable
where lc.EFF_STATUS == "A" && lc.EFFDT <= DateTime.Now
group lc by lc.LOCATION into g
select g.OrderByDescending(x => x.EFFDT).FirstOrDefault()
);
var query =
(from fim in db.PS_PPS_FIM_EE_DATA
join mloc in locatinWithMaxEffDate on fim.LOCATION equals mloc.LOCATION
where
fim.EMPL_STATUS == PsPpsFimEeData.EmployeeStatusValues.Active
&& fim.AUTO_UPDATE == PsPpsFimEeData.AutoUpdateValues.Enabled
&& includeJobCodes.Contains(fim.JOBCODE)
select new PpsAdministratorResult
{
SchoolId = mloc.FLOOR,
Login = fim.OPRID,
EmployeeId = fim.EMPLID,
}
}
Then, just work with the two objects:
List<PpsAdministratorResult> administratorList = new List<PpsAdministratorResult>();
foreach (var location in query.ToList())
{
string newLocationCode;
if(schoolCodeMappings.TryGetValue(location.SchoolId, out newLocationCode)) // && newLocationCode.Contains(location.LOCATION))
{
location.SchoolId = newLocationCode;
}
if( !excludedSchools.Contains(location.SchoolId) )
{
administratorList.Add(location);
}
}
Now, I have the list I want.

Related

Linq with two tables and an excel file

I have a file that where I have rows that need to be inserted into a destination_table. The destination_table is related in the database with another table, we will call it head_table.
I am using a DataTable class to get the data.
I have the following code but I don't get the expected result.
The query returns zero and this must not be so, it must be greater than zero
If someone could guide me a little, thank you very much.
var query =
from file in ta.AsEnumerable()
join head_table in tc.AsEnumerable()
on file["ID"] equals head_table["ID"]
join destination_table in td.AsEnumerable()
on new { ID = file["ID"].ToString().Trim() } equals new { ID = destination_table["ID"].ToString().Trim() }
into gj
from result in gj.DefaultIfEmpty()
select new { line = file, destination_table_key = (result == null ? null : result["ID"]) };
var insertValues = query.Where(a => a.destination_table_key == null);
//It has to return greater than zero and currently I can't get it
if (insertValues.Count() > 0)
{
foreach (var re in insertValues)
{
InsertRow... //Here is the part of the insert
}
}

passing linq anonymous result to iqueryable object for another query

I have two tables (tbPerson and tbDataLog) where I need to return Id from one table (tbPerson) after checking certain conditions on both. After this, this result should be passed to another query. My first query returns the Id (primary key of a table) successfully and I need to pass these ids to another query so that it return me data based upon these Id. I also has an IQueryable type base object to check certain conditions to fetch data.
IQueryable<tbPerson> dataset
and I cannot changes this from Iqueryable to other as it will break other part of the code)
My first linq statement:
public static IQueryable<LogResults> GetResultsForYes()
{
Databasename ents = new Databasename();
var ids = (from f in ents.tbPerson
join g in ents.tbDataLog
on f.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select new LogResults { _LogID = f.Id }).OrderBy(x => x._LogID);
return ids;
}
public class LogResults
{
public int _LogID { get; set; }
}
I access my result something like this where I can see in debugger all the Ids.
IQueryable<LogResults> log = GetResultsForYes();
Problem comes, when I tried to get records from tbPerson based upon these returned Id.
dataset=log.where(x=>x._LogID != 0);
I get this error:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Linq.IQueryable'. An explicit conversion exists(are you missing a cast)?
Any suggestions or some other good approach is welcome.
I love this thing about stackoverflow. when we write questions we force our brain to think more deeply and after 30 mins of posting this question, I solved it in a simple way. Sometimes we overcomplicated things!
var ids = (from f in ents.tbPerson
join g in ents.tbDataLog
on f.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select new { f.Id }).ToArray();
var allId = ids.Select(x => x.Id).ToArray();
dataset = dataset.Where(x => allId.Contains(x.Id));
#ankit_sharma : I have not tested yours but will give a try and come back to you. Thanks for giving time and effort.
IQueryable<tbPerson> dataset=log.where(x=>x._LogID != 0);
The result of log.where(x=>x._LogID != 0) is an IQueryable<LogResults>, and you are trying to assign this result to dataset of type IQueryable<tbPerson>, two diferent types.
EDIT:
I see you make a join to get the tbPerson ids, and then you do a second query to get the persons. You could get the persons in the first join.
I just modify your code:
IQueryable<tbPerson> persons = from person in ents.tbPerson
join g in ents.tbDataLog
on person.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select person;

Calling 'Read' when the data reader is closed is not a valid operation in Entity Framework

I am using Entity Framework in windows application C#, Trying to retrieve data from two entities using DbContext and want to make simple join, but my code breakdown (at var modellst line). My sample code is as below
using (var ctx = new DbEntities())
{
var lst = ctx.AUMaterials.Where(o => o.ServiceRequestTypeId == serviceReqId && o.SSStock.Quantity > 0).ToList();
var modellst = ctx.AUModelMaterials.Where(o => o.ModelId == modelId).ToList();
// here i want to make join on these two list
}
Here in first list thousands of records in AUMaterials entity. And I think it will take to much time to load. Same way in AUModelMaterials entity, Here also thousands of records. But same code works fine in earlier stage.
var results = (from t1 in context.AUMaterials
join t2 in context.AUModelMaterials
on t1.Col1 equals t2.Col1
where t1.ServiceRequestTypeId == serviceReqId && t1.SSStock.Quantity > 0 && t2.ModelId == modelId
select new { t1, t2}).ToList();
Joining on multiple columns
var results = (from t1 in context.AUMaterials
join t2 in context.AUModelMaterials
on new {t1.Col1, t1.Col2, t1.Col3 } equals
new { t2.Col1, t2.Col2, t2.Col3 }
where t1.ServiceRequestTypeId == serviceReqId && t1.SSStock.Quantity > 0 && t2.ModelId == modelId
select new { t1, t2}).ToList();

LINQ - using result from one in another

I'm completely new to LINQ, i want to rewrite some of mine SQL querys into LINQ (just to learn) and i'v already stuck at the beginning. Probably solution is very simple but as i'v said I'm completely new and i didn't find solution to this.
I have one query :
string typMoneta = textBox1.Text;
var moneta = from x in db.grupyTowarowes
where x.typ == typMoneta
select new
{
x.grupa
};
Which works ok and when i set
dataGridView1.DataSource = moneta;
Then i got output
And i want to use this output in my second query :
var query = from c in dbContext.Picking
where c.Number == 1000 && c.Group == moneta
select new
{
c.id
};
Problem is with c.Group == moneta. I don't know the correct syntax. Could someone help me?
I think you meant to use moneta.Contains(c.Group). In first query, make sure you use ToList() to load data into memory.
IList<string> moneta = (from x in db.grupyTowarowes
where x.typ == typMoneta
select x.grupa).ToList();
var query = (from c in dbContext.Picking
where c.Number == 1000 && moneta.Contains(c.Group)
select c.id).ToList();
The moneta is an IEnumerable<T> where T in your case is the type of grupa
That being said you should write your query like below:
var query = from c in dbContext.Picking
where c.Number == 1000
&& moneta.Contais(c.Group)
select new
{
c.id
};
or in fluent syntax like below:
var query = dbContext.Picking
.Where(pick => pick.Number == 1000
&& moneta.Contains(pick.Group))
.Select(pick => pick.id);
Note that moneta is not a collection of strings. It's a collection of objects that have a string property named "grupa".
Does this work for you?
var query =
from c in dbContext.Picking
where c.Number == 1000
&& moneta.Any(m => m.grupa == c.Group)
select new { c.id };
You could also do this:
// Get list of strings
var groups = moneta.Select(m => m.grupa).ToList();
// Get items where "Group" value is one of the strings in groups list, above.
var query =
from c in dbContext.Picking
where c.Number == 1000
&& groups.Contains(c.Group)
select new { c.id };

More efficient way of loading children of entity objects in linq to entity query

I have a rather complex linq to entity query that I'm performing, in the end, I have a result set. I loop through that result set, build business objects and return that list of business objects. it's pretty quick, the problem is that 2 of the child properties are complex objects with their own child objects. for every business object in my loop, I then have to make 2 DB calls to fill its child object. Those 2 calls slow down the overall process, is there a better way to do this? noob to EF here. (EF 4,SQL Server 2008,c#)
Get a result set:
var newresult = from r in result // result is another complex query
join subedit in
(from sa in context.Security_Access
join g in context.Security_UserGroup on sa.EntityID equals g.GroupID
where (sa.PrivledgeID == xx) && g.UserID == userId
select new { user = g.UserID, linkid = sa.LinkID }).Distinct() on new { aid = r.AssetId } equals new { aid = subedit.linkid } into theSubEdit
from subEditAccess in theSubEdit.DefaultIfEmpty()
join subdownload in
(from sa in context.Security_Access
join g in context.Security_UserGroup on sa.EntityID equals g.GroupID
where (sa.PrivledgeID == xx|| sa.PrivledgeID == yy) && g.UserID == userId
select new { user = g.UserID, linkid = sa.LinkID }).Distinct() on new { aid = r.AssetId } equals new { aid = subdownload.linkid } into theSubDownload
from subDownloadAccess in theSubDownload.DefaultIfEmpty()
join subView in
(from sa in context.Security_Access
join g in context.Security_UserGroup on sa.EntityID equals g.GroupID
where (sa.PrivledgeID == xx|| sa.PrivledgeID == yy|| sa.PrivledgeID == 101) && g.UserID == userId
select new { user = g.UserID, linkid = sa.LinkID }).Distinct() on new { aid = r.AssetId } equals new { aid = subView.linkid } into theSubView
from subViewAccess in theSubView.DefaultIfEmpty()
select new { r, EditAccess = (int?)subEditAccess.user, DownloadAccess = (int?)subDownloadAccess.user, ViewAccess = (int?)subViewAccess.user };
I then loop through that result set:
foreach (var asset in newresult)
{
// and build a new business object, set its properties
BoAsset boAsset = new BoAsset();
boAsset.HasEditRights = (asset.EditAccess > 0);
boAsset.HasDownloadRights = (asset.DownloadAccess > 0);
boAsset.HasViewRights = (asset.ViewAccess > 0);
boAsset.Description = asset.r.Description;
boAsset.DetailedDescription = asset.r.DetailedDescription;
boAsset.Keywords = asset.r.Keywords;
boAsset.Notes = asset.r.Notes;
boAsset.Photographer = asset.r.Photographer;
boAsset.PhotographerEmail = asset.r.PhotographerEmail;
boAsset.Notes = asset.r.Notes;
boAsset.Author = asset.r.Author;
// these 2 properties i've commented out are
// complex objects/entities, setting them the way I am
// requires me to call 2 separate methods which make 2 DB trips
// per business object.
//boAsset.Domains = GetAssetDomains(asset.r.AssetId);
//boAsset.DomainEntries = GetAssetCustomDomains(asset.r.AssetId);
myListofObjects.Add(boAsset);
}
return myListofObjects;
Is there a better way?
Just add this .Include("Domains").Include("DomainEntries") to your Linq in in context.Security_Access That should get rows from those tables all in one go.
So your "inner" queries would look like:
from sa in context.Security_Access.Include("Domains").Include("DomainEntries")
join g in context.Security_UserGroup on sa.EntityID equals g.GroupID
where (sa.PrivledgeID == xx) && g.UserID == userId
select new { ...
Here is the documentation from MS: http://msdn.microsoft.com/en-us/library/bb738708.aspx
If you want to improve your performance use compile queries !
You can check the example here.
static readonly Func<AdventureWorksEntities, Decimal,
IQueryable<SalesOrderHeader>> s_compiledQuery2 =
CompiledQuery.Compile<AdventureWorksEntities, Decimal, IQueryable<SalesOrderHeader>>((ctx, total) =>
from order in ctx.SalesOrderHeaders.Include("Orders") where order.TotalDue >= total select order);
MSDN
AND
You can Introduce Include suppose to select all the employees along with their departments . If you have a navigational property, you won't need a join at all. You can use Include like this:
List<Employee> employeesWithDepartments = CreateObjectSet<Employee>().
Include(e => e.Department).
ToList();

Categories