Group and flatten a query into a variable number of fields - c#

I've got three tables, one for workers, another one for folders and a third one that combines IDs from workers and folder indicating that the worker has access to the folder.
I need to fill a table representing whether the workers have access or not to the folders.
So far I've come up with this:
var accessQuery = from folder in db.FOLDERS
from worker in db.WORKERS
select new
{
Folder = folder.Name,
Worker = worker.Name,
Access = worker.FolderAccess.Count(f => f.ID_Folder == folder.ID_Folder) > 0
};
I'd use another query to get all the Folders and then create a DataTable where the first column would be the worker's name and then add each folder as a new column.
Then I'd iterate through each row to fill a DataTable with the access data. That DataTable I use to feed a GridView or to export the data.
I'm wondering whether there is a way to accomplish this with just one single query or at least a more efficient way as mine doesn't seem efficient at all.

I ended coming with a solution. Not sure if it's the best (probably not) but it's better than what I was using. Thanks to Ako for point the 'Any' change as it seems better than the count.
Here's the whole code with the DataTable filling
//First I get a list of all the folders
var queryFolders = from f in db.FOLDERS
orderby f.Name
select new
{
f.Name
};
//Here there's the query I was looking for
var queryAccess = from f in db.FOLDERS
from u in db.USERS
orderby f.Name
select new
{
User = u.Name,
Access = u.FolderAccess.Any(x => x.ID_Folder == f.ID_Folder)
}
into crossJoin
group crossJoin by new { crossJoin.User } into results
select new
{
results.Key.User,
AccessList = results.Select(x => x.Access).ToArray()
};
//Now I wrap the queries into a DataTable so I can easily feed them to what I need
DataTable dtAccess = new DataTable();
dtAccess.Columns.Add("User");
foreach (var f in queryFolders)
{
dtAccess.Columns.Add(f.Name, typeof(bool));
}
foreach (var a in queryAccess)
{
DataRow userFolders = dtAccess.NewRow();
userFolders["User"] = a.User;
for (int i = 1; i <= a.AccessList.Length; i++)
{
userFolders[i] = a.AccessList[i - 1];
}
dtAccess.Rows.Add(userFolders);
}

Use join. FYI this is basic RDBMS sql query so imho you better learn sql query first before moving to linq2sql.
var accessQuery = from worker in db.WORKERS
join access in db.ACCESS on access.ID_Worker == worker.ID_Worker
join folder in db.FOLDERS on access.ID_Folder == folder.ID_Folder
select new
{
Folder = folder.Name,
Worker = worker.Name
};

Related

LINQ List overwrites previous value instead of setting new one

I have a LINQ query which outputs ToList it works fine other than the fact each time it's run it updates the original record instead of creating a new one.
On every run through this code the data.EventID changes so I'd like every record to appear in the list.
The code:
foreach(var data in vehicleqry)
{
bool inUK = boundryChecker.IsLongLatInUK((double)data.declatfloat, (double)data.declongfloat);
if (inUK == true)
{
var qryevent = (from e in db.events
where e.eventID == data.EventID
select new
{
e.eventID,
e.sysdatetime,
e.vehicleID
}).ToList();
}
{
I also have a list with the eventIDs in if I can use this to query the list?
I think what you actually want is to only run a single query instead of looping around. You can do this by making use of the Contains method:
var vehicleqry = ...;
// Get all of the individual event IDs for entries that are "inUK"
var vehicleEventIds = vehicleqry
.Where(ve => boundryChecker
.IsLongLatInUK((double)ve.declatfloat, (double)ve.declongfloat)
.Select(ve => ve.EventID);
// Get all the matching events
var qryevent = (from e in db.events
where vehicleEventIds.Contains(e.eventID)
select new
{
e.eventID,
e.sysdatetime,
e.vehicleID
}).ToList();

The LINQ expression contains references to queries that are associated with different contexts

Here's my code:
var myStrings = (from x in db1.MyStrings.Where(x => homeStrings.Contains(x.Content))
join y in db2.MyStaticStringTranslations on x.Id equals y.id
select new MyStringModel()
{
Id = x.Id,
Original = x.Content,
Translation = y.translation
}).ToList();
And I get the error that the specified LINQ expression contains references to queries that are associated with different contexts. I know that the problem is that I try to access tables from both db1 and db2, but how do I fix this?
MyStrings is a small table
Load filtered MyStrings in memory, then join with MyStaticStringTranslations using LINQ:
// Read the small table into memory, and make a dictionary from it.
// The last step will use this dictionary for joining.
var byId = db1.MyStrings
.Where(x => homeStrings.Contains(x.Content))
.ToDictionary(s => s.Id);
// Extract the keys. We will need them to filter the big table
var ids = byId.Keys.ToList();
// Bring in only the relevant records
var myStrings = db2.MyStaticStringTranslations
.Where(y => ids.Contains(y.id))
.AsEnumerable() // Make sure the joining is done in memory
.Select(y => new {
Id = y.id
// Use y.id to look up the content from the dictionary
, Original = byId[y.id].Content
, Translation = y.translation
});
You are right that db1 and db2 can't be used in the same Linq expression. x and y have to be joined in this process and not by a Linq provider. Try this:
var x = db1.MyStrings.Where(xx => homeStrings.Contains(xx.Content)).ToEnumerable();
var y = db2.MyStaticStringTranslations.ToEnumerable();
var myStrings = (from a in x
join b in y on x.Id equals y.id
select new MyStringModel()
{
Id = x.Id,
Original = x.Content,
Translation = y.translation
}).ToList();
Refer to this answer for more details: The specified LINQ expression contains references to queries that are associated with different contexts
dasblinkenlight's answer has a better overall approach than this. In this answer I'm trying to minimize the diff against your original code.
I also faced the same problem:
"The specified LINQ expression contains references to queries that are associated with different contexts."
This is because it's not able to connect to two context at a time so i find the solution as below.
Here in this example I want to list the lottery cards with the owner name but the Table having the owner name is in another Database.So I made two context DB1Context and DB2Context.and write the code as follows:
var query = from lc in db1.LotteryCardMaster
from om in db2.OwnerMaster
where lc.IsActive == 1
select new
{
lc.CashCardID,
lc.CashCardNO,
om.PersonnelName,
lc.Status
};
AB.LottryList = new List<LotteryCardMaster>();
foreach (var result in query)
{
AB.LottryList.Add(new LotteryCardMaster()
{
CashCardID = result.CashCardID,
CashCardNO = result.CashCardNO,
PersonnelName =result.PersonnelName,
Status = result.Status
});
}
but this gives me the above error so i found the other way to perform joining on two tables from diffrent database.and that way is as below.
var query = from lc in db1.LotteryCardMaster
where lc.IsActive == 1
select new
{
lc.CashCardID,
lc.CashCardNO,
om.PersonnelName,
lc.Status
};
AB.LottryList = new List<LotteryCardMaster>();
foreach (var result in query)
{
AB.LottryList.Add(new LotteryCardMaster()
{
CashCardID = result.CashCardID,
CashCardNO = result.CashCardNO,
PersonnelName =db2.OwnerMaster.FirstOrDefault(x=>x.OwnerID== result.OwnerID).OwnerName,
Status = result.Status
});
}

Update a datatable with reference key from another datatable

I have one datatable, say 'dtEmp', with columns [EmployeeID][CompanyID][CompanyName]
and another say 'dtCompany' with columns [CompanyID][CompanyName]
I want to update 'dtEmp' with respective Company names in column [CompanyName]
Plese guide. I tried searching this but I could not find exact words to search :(
How about
for(int i = 0; i < dtCompany.Rows.Count; i++)
{
for(int j = 0 ; j < dtEmp.Rows.Count ; j++)
{
if (Convert.ToString(dtCompany.Rows[i]["CompanyID"]) ==
Convert.ToString(dtEmp.Rows[j]["CompanyID"]))
{
dtEmp.Rows[j]["CompanyName"] =
Convert.ToString(dtCompany.Rows[i]["CompanyName"]);
}
}
}
var result = (from t1 in dtEmp
join t2 in dtCompany on t1.CompanyID equals t2.CompanyID
select new { t1.EmployeeID, t1.CompanyID, t2.CompanyName}).ToList()
I don't know what your long term purpose is for updating the datatable, however using something like this would provide you a list of an anonymous object which would contain the 3 fields that you needed. You could potentially hard cast these to strongly typed Datarows and then create a brand new table (or update your existing one) with each row.
Linq itself cannot do update.
Assuming Typed DataTable, simply:
foreach(var rowEmp in dtEmp)
{
var rowComp = dtCompany
.Where(r => r.CompanyID == rowEmp.CompanyID)
.FirstOrDefault();
if(rowComp == null)
rowEmp.SetCompanyNameNull();
else
rowEmp.CompanyName = rowComp.CompanyName;
}
(Ignoring the case of the content having null or DBNull, do null-check if required please.)
Similar logic is applicable to general DataTable.

Linq Select Clause w/ Unknown Number of Fields

I have a linq query in which I need to be able to select an variable number of fields from a datatable. I do know all of the fields that could be included, but only two will for sure be in the datatable. I also will know which fields are included in the datatable (it will just be different depending on the user's selections). Right now I set up something like this:
var query = from item in dt.AsEnumerable()
group item by item.Field<string>("ID") into g
select new
{
ID = g.Key, //required
Status = g.Min(i => dostuff(i,"Status")), //not required
Disc = g.Min(i => dostuff(i,"Disc")), //not required
Loc = String.Join<string>(",", from i in g select i.Field<string>("Loc")) //required
};
dostuff(DataRow i,string field)
{
try
{
return i.Field<string>(field);
}
catch
{
return null;
}
}
So dostuff basically is just checking whether or not that field exists in the dataset, and then I would just need to ignore the non-existant fields when working with the query results, which would not be too difficult. However, it seems like there is probably a better way to do this, but I've had a tough time finding anything via Google about using a dynamic select clause.
You could do it with dynamic type (nb, I did not test so this might have typos.):
var query =dt.AsEnumerable().GroupBy(item => item.Field<string>("ID"))
.Select(g => {
dynamic t = new System.Dynamic.ExpandoObject();
if (g.Table.Columns.Any(c => c.ColumnName == "Status"))
t.Status = g.Field<string>("Status");
if (g.Table.Columns.Any(c => c.ColumnName == "Disc"))
t.Disc = g.Field<string>("Disc");
t.ID = g.Key;
t.Loc = String.Join<string>(",",g.Select(i => i.Field<string>("Loc")));
return t;
}

linq to sql complex query with group by

I have a table called "Articles".
it includes the following field:
ArticleIndex, ArticleLevel, ArticleParentIndex.
I made query which returns all the articles with ArticleLevel=1 - let's call it query1.
The query which returns all the articles with ArticleLevel=2 - query2.
I would like to have a query that would return Articles of level=1 with at least one child article (the child articles have level=2), and also the number of child articles.
So far I have the following query:
var filteredItemsGrouped = from i in filteredItems
group i by i.ArticleParentIndex into g
select new { Node = g, NodeItemsCount = g.Count() };
and then, in order to get the actual articles with level=1 I do:
IList<ArticleNodeInfo> Nodes = new List<ArticleNodeInfo>();
foreach (var node in filteredItemsGrouped)
{
Nodes.Add(new ArticleNodeInfo
{
Node = articlesService.GetArticleByIndex((int)(node.Node.FirstOrDefault().ArticleParentIndex)),
NodeItemsCount = node.NodeItemsCount
});
}
This process is too expensive. Is it possible to acheive the same with one query (instead of retreiving by article index every time)?
Hope I'm clear enough...
this should do the trick:
var articlesLevel1 = (
from al1 in Articles
join al2 in Articles on new
{
al1.ArticleIndex,
ArticleLevel = 2
} equals new
{
ArticleIndex = al2.ArticleParentIndex,
al2.ArticleLevel
} into g_al2
where (al1.ArticleLevel == 1) && g_al2.Any()
select new
{
ArticlesLevel1 = al1,
ArticlesLevel2Count = g_al2.Count()
});

Categories