How to make two Joins generate one select? - c#

I currently have 3 tables:
News
ID | Title
Tag
ID | Name
TaggedContent
ContentID | TagID
And I have two context objects: NewsEntities and TagsEntities
I want to select all tags used by News, in my application I have:
static void Main(string[] args)
{
IEnumerable<dynamic> something = null;
IEnumerable<News.Data.News> news = null;
IEnumerable<Tags.Data.Tag> tags = null;
IEnumerable<TaggedContent> tagged = null;
using (var db = new NewsEntities())
{
news = db.News.ToList(); // 1 select
}
using (var db = new TagsEntities())
{
something = news.Join(db.TaggedContents.ToList(),
n => n.ID,
tc => tc.ContentID,
(n, tc) => new { tc.TagID }); // 1 select
something = something.Join(db.Tags.ToList(),
tid => tid.TagID,
t => t.ID,
(tid, t) => t); // 1 select
}
var result = something;
}
I am currently generating 3 selects. How can I reduce it to 2? Or if possible I would like to reduce to 1 without merging the entities.

Since you're joining entities from different contexts, you can't get away with fewer than 2 selects.
Your join is a simple identity check, so you could do this:
var ids = db.News.Select(x => x.ID).ToList();
You now have a local copy of all of the IDs in the news table -- the first select. Then change your first 'something' to:
something = db.TaggedContents
.Where(x => ids.Contains(x.ContentID))
.Select(x => new { x.TagID });
This second statement won't in itself generate a select because of deferred execution. You can now remove the ToList() in the third statement:
something = something.Join(db.Tags,
tid => tid.TagID,
t => t.ID,
(tid, t) => t);
And when you finally enumerate over something, you'll have your second select.

Using DataLoadOptions class passed in the creation of your DataContext.
DataLoadOptions options = new DataLoadOptions();
db.LoadOptions = options;
options.LoadWith((News n) => n.Tags);
So do for the remaining of the data to join.

Related

How to aggregate and SUM EntityFramework fields with multiple joins

I am able to produce a set of results that are desirable, but I have the need to group and sum of these fields and am struggling to understand how to approach this.
In my scenario, what would be the best way to get results that will:
Have a distinct [KeyCode] (right now I get many records, same KeyCode
but different occupation details)
SUM wage and projection fields (in same query)
Here is my LINQ code:
private IQueryable<MyAbstractCustomOccupationInfoClass> GetMyAbstractCustomOccupationInfoClass(string[] regionNumbers)
{
//Get a list of wage data
var wages = _db.ProjectionAndWages
.Join(
_db.HWOLInformation,
wages => wages.KeyCode,
hwol => hwol.KeyCode,
(wages, hwol) => new { wages, hwol }
)
.Where(o => regionNumbers.Contains(o.hwol.LocationID))
.Where(o => o.wages.areaID.Equals("48"))
.Where(o => regionNumbers.Contains(o.wages.RegionNumber.Substring(4))); //regions filter, remove first 4 characters (0000)
//Join OccupationInfo table to wage data, for "full" output results
var occupations = wages.Join(
_db.OccupationInfo,
o => o.wages.KeyCode,
p => p.KeyCode,
(p, o) => new MyAbstractCustomOccupationInfoClass
{
KeyCode = o.KeyCode,
KeyTitle = o.KeyTitle,
CareerField = o.CareerField,
AverageAnnualOpeningsGrowth = p.wages.AverageAnnualOpeningsGrowth,
AverageAnnualOpeningsReplacement = p.wages.AverageAnnualOpeningsReplacement,
AverageAnnualOpeningsTotal = p.wages.AverageAnnualOpeningsTotal,
});
//TO-DO: How to Aggregate and Sum "occupations" list here & make the [KeyCode] Distinct ?
return occupations;
}
I am unsure if I should perform the Grouping mechanism on the 2nd join? Or perform a .GroupJoin()? Or have a third query?
var occupations = _db.OccupationInfo.GroupJoin(
wages,
o => o.KeyCode,
p => p.wages.KeyCode,
(o, pg) => new MyAbstractCustomOccupationInfoClass {
KeyCode = o.KeyCode,
KeyTitle = o.KeyTitle,
CareerField = o.CareerField,
AverageAnnualOpeningsGrowth = pg.Sum(p => p.wages.AverageAnnualOpeningsGrowth),
AverageAnnualOpeningsReplacement = pg.Sum(p => p.wages.AverageAnnualOpeningsReplacement),
AverageAnnualOpeningsTotal = pg.Sum(p => p.wages.AverageAnnualOpeningsTotal),
});

LINQ: How to Select specific columns using IQueryable()

I need to select only two columns from Hospital table, HospitalId and Name.
i tried the below code it selects all columns from Hospital table which lead to slow performance. Please help me to select only two columns from Hospital table
public HttpResponseMessage GetAvailableHospitalsByAjax(System.Guid? DirectorateOfHealthID = null, System.Guid? UnitTypeID = null, string DeviceTypeIDs = null)
{
Context db = new Context();
var query = db.Hospitals.AsQueryable();
if (UnitTypeID != null)
{
query = query.Where(j => j.HospitalDepartments.Any(www => www.Units.Any(u => u.UnitTypeID == UnitTypeID)));
}
if (DirectorateOfHealthID != null)
{
query = query.Where(h => h.DirectorateHealthID == DirectorateOfHealthID);
}
query = query.Where(j => j.HospitalDepartments.Any(u => u.Units.Any(d => d.Devices.Any(s => s.Status == Enums.DeviceStatus.Free)))
&& j.HospitalDepartments.Any(hd => hd.Units.Any(u => u.Beds.Any(b => b.Status == Enums.BedStatus.Free))));
var list = query.ToList().Select(w => new HospitalInfo()
{
Id = w.ID,
Name = w.Name
}).ToList();
return Request.CreateResponse(HttpStatusCode.OK, list);
}
IQueryable<T> executes select query on server side with all filters. Hence does less work and becomes fast.
IEnumerable<T> executes select query on server side, load data in-memory on client side and then filter data. Hence does more work and becomes slow.
List<T> is just an output format, and while it implements IEnumerable<T>, is not directly related to querying.
So,
var list = query.ToList().Select(w => new HospitalInfo()
{
Id = w.ID,
Name = w.Name
}).ToList();
In your code you use query.ToList(). This means at first it pull all data into memory then apply Select query.If you want to retrieve HospitalID and Name then remove ToList() then your code like
var list = query.Select(w => new HospitalInfo
{
Id = w.ID,
Name = w.Name
}).ToList();
Remove the ToList call before the projection:
var list = query.Select(w => new HospitalInfo()
{
Id = w.ID,
Name = w.Name
}).ToList();
With that ToList call you are materializing your query before do the projection
Because you do query.ToList() this materialises the entire query with all columns into memory. It's actually a bad habit to get into. Instead, remove that, you already have it at the end anyway. The Select projection you have will only retrieve the relevant columns though:
var list = query.Select(w => new HospitalInfo()
{
Id = w.ID,
Name = w.Name
}).ToList();

Get a specific value out of a DictionaryList with LINQ?

I have a Dictionary:
var dict = new Dictionary<int, string>();
With this values:
[0]: {[1, "Person1"]}
[1]: {[2, "Person2, Person3"]}
[2]: {[3, "Person4"]}
[3]: {[4, "Person5"]}
And when i use a "foreach" to get the values with "id" 2 i get as result "Person2, Person3".
foreach (var test in dict)
{
if (test.Key == 2)
System.Diagnostics.Debug.WriteLine(test.Value);
}
But when i use this LINQ line:
Person = dict.FirstOrDefault(q => q.Key == s.Person.ID).Value.ToString(),
I get this error:
Local sequence cannot be used in LINQ to SQL implementations of query
operators except the Contains operator.
I tried several things but nothing seems to work, so any ideas?
EDIT
I use this to show output on my page:
DataSelectionQuery = p => new
{
p.ID,
p.FirstName,
p.LastName,
TEST = dict.FirstOrDefault(q => q.Key == p.ID).Value.ToString(),
};
public Expression<Func<LinqClass, object>> DataSelectionQuery { get; set; }
And here is where it trows the error:
var results = query.Select(DataSelectionQuery).ToList();
You cannot use this kind of expressions inside Linq to SQL as they cannot be translated to SQL query
Use .ToList() at the end of your query, then use Linq to objects to complete your entity with values from Dictionary
For example when you have code like:
var result = from x in table
select new Entity
{
Id = x.Id,
Sth = x.Sth,
Person = dict.FirstOrDefault(q => q.Key == s.Person.ID).Value.ToString()
};
You need to change it to something like this:
var result = (from x in table
select new { x.Id, x.Sth, x.PersonId }) // LINQ To SQL part
.ToList() // get query results
.Select(x => new Entity
{
Id = x.Id,
Sth = x.Sth,
Person = dict.FirstOrDefault(q => q.Key == x.PersonId).Value.ToString()
}; // this part will be executed with Linq to objects
Please provide the full source code if you need more detailed help
Ok, so your "SelectionQuery" needs to be simple enough to translate into SQL query and should look like this:
DataSelectionQuery = p => new
{
p.ID,
p.FirstName,
p.LastName
};
And the other part should look like this:
var results = query.Select(DataSelectionQuery).ToList()
.Select(p => new
{
p.ID,
p.FirstName,
p.LastName,
TEST = dict.FirstOrDefault(q => q.Key == p.ID).Value.ToString()
});
Alernatively you can create your own class which will contain the Dictionary and will translate the ID into TEST on the getter action
This is how I solved the problem:
Person = string.Join(", ", PersonsQuery.Where(q => q.ID == s.ID).Select(q => q.PersonInformation))

Entity framework use already selected value saved in new variable later in select sentance

I wrote some entity framework select:
var query = context.MyTable
.Select(a => new
{
count = a.OtherTable.Where(b => b.id == id).Sum(c => c.value),
total = a.OtherTable2.Where(d => d.id == id) * count ...
});
I have always select total:
var query = context.MyTable
.Select(a => new
{
count = a.OtherTable.Where(b => b.id == id).Sum(c => c.value),
total = a.OtherTable2.Where(d => d.id == id) * a.OtherTable.Where(b => b.id == id).Sum(c => c.value)
});
Is it possible to select it like in my first example, because I have already retrieved the value (and how to do that) or should I select it again?
One possible approach is to use two successive selects:
var query = context.MyTable
.Select(a => new
{
count = a.OtherTable.Where(b => b.id == id).Sum(c => c.value),
total = a.OtherTable2.Where(d => d.id == id)
})
.Select(x => new
{
count = x.count,
total = x.total * x.count
};
You would simple do
var listFromDatabase = context.MyTable;
var query1 = listFromDatabase.Select(a => // do something );
var query2 = listFromDatabase.Select(a => // do something );
Although to be fair, Select requires you to return some information, and you aren't, you're somewhere getting count & total and setting their values. If you want to do that, i would advise:
var listFromDatabase = context.MyTable.ToList();
listFromDatabase.ForEach(x =>
{
count = do_some_counting;
total = do_some_totalling;
});
Note, the ToList() function stops it from being IQueryable and transforms it to a solid list, also the List object allows the Linq ForEach.
If you're going to do complex stuff inside the Select I would always do:
context.MyTable.AsEnumerable()
Because that way you're not trying to still Query from the database.
So to recap: for the top part, my point is get all the table contents into variables, use ToList() to get actual results (do a workload). Second if trying to do it from a straight Query use AsEnumerable to allow more complex functions to be used inside the Select

LINQ Method that returns set based on filtered sub-set

I have 2 tables. 1 has entity's, 1 per row. Another is simply a mapping table of my EntitiesID and EmployeeID. I am trying to write a LINQ method that returns all Entities from the First Table where the EntityID is in the mapping table that is filtered by the EmployeeID.
Simplified Table Structure Example
TaskTable: ID, Description, Status
TaskViewTable: ID, TaskID, EmployeeID
So I want to return all Rows from TaskTable where the ID is in a SubQuery results of TaskViewTable based on EmployeeID.
Any help on doing this in LINQ? I have a 1 to Many set up between the two tables as well. I know there are similar questions am maybe I'm dense but they didn't seem to apply completely to what I was asking.(e.g. Linq Return Filtered Children)
Sorry forgot to show what I have so far:
IQueryable<tblTask> tTask=context.GetTable<tblTask>();
return tTask.Where(t => t.tblTasksViews.Where(v => v.EmployeeID == empID))
It, however, does not like my wherewith an unkown method Where(?)
Something like this should do the trick:
var tasks = tTask.Where(t =>
tTaskView.Where(v => v.ID == empId).Select(v => v.TaskId).Contains(t.ID));
You could break up the above into two sections:
//1.) Get all task views for the employeeID and only select the mapped TaskId
var taskViews = tTaskView.Where(v => v.ID == empId).Select(v => v.TaskId); //taskViews = IEnumerable<int>
//2.) Then get all tasks from the filtered task ids
var tasks = tTask.Where(t => taskViews.Contains(t.ID));
UPDATE
//3.) Project filtered results into IEnumerable<Task>
return tasks.Select(t => new Task()
{
ID = t.ID,
ActionableID = t.ActionableID,
StatusID = t.StatusID,
TypeID = t.TypeID,
Description = t.Description
});
You can, of course, string everything into a nice one-liner:
public List<Task> GetTasks(int empId)
{
return tTask
.Where(t => tTaskView.Where(v => v.ID == empId).Select(v => v.TaskId).Contains(t.ID))
.Select(t => new Task()
{
ID = t.ID,
ActionableID = t.ActionableID,
StatusID = t.StatusID,
TypeID = t.TypeID,
Description = t.Description
}).ToList();
}
Try something like this:
var query =
from tt in TaskTable
join tvt in TaskViewTable on tt.ID equals tvt.TaskID into xs
where xs.Any(z => z.EmployeeID == empID)
select tt;

Categories

Resources