How to "select all" in LINQ query with nullable column - c#

This takes a little bit of setup to explain. I know "nulls are bad" in a database and I am experiencing why that can be so, but I have no choice about the way the business uses the data source. The data source has nulls in the dataset and I have to work with it as-is. (Hopefully this addresses the anticipated "Well, your dataset shouldn't have nulls..." or "Why don't you just remove the nulls...?")
Suppose I have sample set like this, where "NULL" is an actual null:
Campus | Name | Relationship
---------------------------------
A | Bob | Relationship 1
B | Bill | NULL
B | Carol | Relationship 2
C | Sally | Relationship 1
Now suppose I am using an option list to filter the results by a distinct list of the values in the Relationship column:
All (meaning show all records)
NULL
Relationship 1
Relationship 2
If I didn't have the "All" option, it would be simple enough:
private IEnumerable<RwsBasePhonesAndAddress> PopulateQuery(string SelectedCampus,
string SelectedRelationship)
{
IEnumerable<RwsBasePhonesAndAddress> query =
db.RwsBasePhonesAndAddresses.Where(m => m.Campus == SelectedCampus);
if (!string.IsNullOrEmpty(SelectedRelationship))
query = query.Where(m => m.Relationship == SelectedRelationship);
else
query = query.Where(m => m.Relationship == null);
query = query.OrderBy(m => m.StudentName).AsEnumerable();
return query;
}
I have verified that the code as written returns results matching the "SelectedRelationship" parameter, including "NULL" records when "NULL" is chosen as a filter.
It's the inclusion of the "All" in the option list that makes this difficult. If the "Relationship" column had no nulls, I could use a null SelectedRelationship parameter as the "do not filter by 'SelectedRelationship'" option.
Is there some way that I can use a "do not filter" option into the code?

Not completely sure that I understood the question. From what I understood to the code above you are missing the part that if you get an "All" value then not to filter out anything. Right? If so:
private IEnumerable<RwsBasePhonesAndAddress> PopulateQuery(string SelectedCampus,
string SelectedRelationship)
{
IEnumerable<RwsBasePhonesAndAddress> query =
db.RwsBasePhonesAndAddresses.Where(m => m.Campus == SelectedCampus);
if(string.IsNullOrEmpty(SelectedRelationship))
query = query.Where(m => m.Relationship == null);
else if (SelectedRelationship != "All")
query = query.Where(m => m.Relationship == SelectedRelationship);
query = query.OrderBy(m => m.StudentName).AsEnumerable();
return query;
}

Related

Selecting Distinct Count and Sum of columns received as sub-query in Entity Framework

I want to get summarized data for a report that shows total amount & suppliers Count per decision in entity Framework Syntax. My Result needed to include a SUM of Amount and COUNT of total suppliers per decision.
I have a table of suppliers with the following columns:
SupplierNo | Decision | DecisionIssuedOn | Amount | SupplierGroup | SubSupplier
Raw SQL query to get above data for a specific time period is:
SELECT S.Decision, SUM(S.Amount) AS TotalAmount, COUNT(DISTINCT S.SupplierNo) AS SupplierCount
FROM (SELECT * FROM Indentors WHERE Indentors.DecisionIssuedOn BETWEEN '2018-01-01' AND '2018-12-31') S
GROUP BY S.Decision
Which gives data as:
SupplierCount | Amount
-----------------------
Approved 20 | 5000
Rejected 11 | 3000
In-Process 5 | 1500
Now from front end, the condition parameters can be anything from the given pool of options (dropdowns) which when selected add where clause in the exixting query like
WHERE Decision = 'Approved' AND SupplierGroup ='ABC' AND SubSupplier ='zxc'
The problem is I am having a hard time getting the desired result using Entity Framework lambda expressions instead of raw SQL.
What I did so far:
I checked for the availability of Options from fornt-end to build where clause as:
IQueryable<Supplier> suppliers = this.db.suppliers.OrderByDescending(i => i.Id);
if (string.IsNullOrEmpty(selectedSupplierGroup) == false)
{
suppliers = suppliers.Where(i => i.SupplierGroup == selectedSupplierGroup);
}
if (string.IsNullOrEmpty(selectedSubSupplier) == false)
{
suppliers = suppliers.Where(i => i.SubSupplier == selectedSubSupplier);
}
if (string.IsNullOrEmpty(selectedDecision) == false)
{
suppliers = suppliers.Where(i => i.Decision == selectedDecision);
}
if (selectedDecisionIssuedOn.HasValue)
{
suppliers = suppliers.Where(i => i.DecisionIssuedOn >= selectedDecisionIssuedOn);
}
var result = suppliers
.GroupBy(i => i.Decision)
.Select(i => i.SupplierNo).Distinct().Count(); // Gives me error
The error is:
IGrouping does not contain a definition for SupplierNo, and no extension method blah blah blah...
But after that I am unable to get data as the raw query (described above) would get me. Thanks
This should give you a similar result to your SQL query. Give it a try and see how you get on:
var results = suppliers
.Where(i => i.DecisionIssuedOn >= selectedDecisionIssuedOn)
.GroupBy(i => i.Decision)
.Select(group => new
{
Decision = group.Key,
TotalAmount = group.Sum(g => g.Amount),
SupplierCount = group.Select(i => i.SupplierNo).Distinct().Count()
});

NHibernate C# e SQL Server

Good Morning,
I wonder if there's any method within the NHIBERNATE to which I can retrieve the first row of the table?
For example:
Line | ID | Name |Last Name |
1 | 0 | Test | of Information |
2 | 1 | Mauricio | Silva |
If I want the first line or the line 1 of the table
You can use Linq to create queries with nHibernate. There is a method called FirstOrDefault() which takes only the first record. If the query return empty, the FirstOrDefault method will return null, so, remember to check the result before using, for sample:
var firstItem = session.Query<Entity>().FirstOrDefault();
if (firstItem != null)
{
string name = firstItem.Name;
// use object
}
NHibernate does support paging, so we can select "any" record using the .Take() and .Skip(). In our case we can do it like this:
var list = session
.QueryOver<Person>()
.Take(1) // this will take just a first record
//.Skip(0) // we can even skip some, to get next page
;
Then the resulting list will contain 1 or none row...
var person = list.FirstOrDefault();
Also, we can never be sure, what order will be used by DB engine, so we should use explicit ORDER BY:
var list = session
.QueryOver<Contact>()
.OrderBy(p => p.ID)
.Asc
.Take(1)
;
Now we can be sure, that the first result will be with ID == 0

Get item from database result by using entity framework linq

I have a schedule object that is returned from the database. It contains information from a few tables. One of the tables is called ScheduleData and has four columns. It has this format:
Id | ScheduleId | Name | Value
I need the value of the fourth column where the Name is Mine and the ScheduleId is 5
I have tried this, but it doesn't work:
string val = from s in schedule.ScheduleData where s.Name.Equals("Mine") && s.ScheduleId == 5 select s.Value;
Use First method or FirstOrDefault method.The query returns an IEnumerable<T>, you can't assign it to string.
string val = (from s in schedule.ScheduleData
where s.Name == "Mine" && s.ScheduleId == 5
select s.Value).First();

Problem with linq query

I have a self referencing table "Product" with the following structure (where D = Draft and A = Approved)
ID ParentID Status Name
---------------------------
1 NULL A Foo
2 1 A Foo2
3 NULL D Bar
4 1 D Foo3
A row can either be "new" (where ParentID == null) or can be a version of an existing row. So we can see from the table that there are 3 versions for the item "Foo" and only 1 for "Bar".
I need a way of returning the latest versions of each item based on whether the user is able to see only "Approved" items or is able to see "Draft" as well. So for example
Users who can see "D" would have:
3 NULL D
4 1 D
The "latest" row for "Foo" and "Bar".
Users who can see "A" would have:
2 1 A
ie. only the "Approved" versions.
Thanks in advance,
Jose
Here is the Linq query that should work for you:
bool hasDraftAccess = false;
var query = DataContext.Records.AsQueryable();
if (!hasDraftAccess) {
query = query.Where(r => r.Status == 'A');
}
var seriesQuery = query.Select(r => new { Record = r, SeriesID = r.ParentID ?? r.ID });
var latestQuery = seriesQuery.GroupBy(s => s.SeriesID).Select(g => g.OrderByDescending(s => s.Record.ID).First());
var resultsQuery = latestQuery.Select(s => s.Record);
var results = resultsQuery.ToArray();
Here's what's happening:
First, add a WHERE clause to filter out draft records if the user doesn't have access to them
Then add a pseudo column called 'SeriesID' that groups all the related versions into that one column. That is, make it easy to group parent and related children.
Group the related records and then pick whichever record is most recent
Select the Linq Entity from the anonymous type so that it is updatable
I should note that if you have the ability to change your data schema you should consider adding a column called InsertDate or something to that effect. Right now I am assuming that whatever record has the highest ID is the latest. It is often better to add a DateTime field and sort on that instead.
I apologize that this isn't actually using Linq syntax--I prefer fluent coding styles--but it could be easily translated to Linq syntax if you preferred it.
Totally untested - but something like this might work, if I've understood the question correctly.
Can see approved
context.Table.Where(p => p.Status == "A")
Can see approved and draft
context.Table.Where(p => p.Status == "D" || (p.Status == "A" && !context.Table.Any(q => q.Status == "D" && q.Parent == p.Parent)))

Child collection not filtering even though linq to sql join exists

I have a LINQ query:
var result = from mt in MessageTypes
join mtfmt in MessageTypeField_MessageTypes
on new { MessageTypeID = mt.ID, MessageTypeFieldID = messageTypeFieldId } equals new { MessageTypeID = mtfmt.MessageTypeID, MessageTypeFieldID = mtfmt.MessageTypeFieldID }
where (mt.StatusID == (int)status)
select mt;
Or Lambda syntax if you prefer (the one I am using) (messageTypeFieldID is set above the var call from a param.):
var messageTypes = context.MessageTypes
.Join(
context.MessageTypeField_MessageTypes,
mt =>
new
{
MessageTypeID = mt.ID,
MessageTypeFieldID = messageTypeFieldID
},
mtfmt =>
new
{
MessageTypeID = mtfmt.MessageTypeID,
MessageTypeFieldID = mtfmt.MessageTypeFieldID
},
(mt, mtfmt) =>
new
{
mt = mt,
mtfmt = mtfmt
}
)
.Where(x => (x.mt.StatusID == (int)status))
.Select(x => x.mt);
I've just started learning LINQ joins and approached a situation where it is required in a new normalization (many-many) table I am setting up and I wish to return all message types each with their associated field which lives under 'MessageTypeField_MessageType'.
My 'MessageTypeField_MessageTypes' table is a fairly simple normalization strategy setup like this and I should state even though MessageTypeID 'has many' it is unique data so it could be:
[ID | MessageTypeID | MessageTypeFieldID]
1 63 10
1 63 11
1 63 12
Now the above code executes and returns a query I am happy with.. (performs correct INNER JOIN) but when I look at messageTypes.ToList()[0].MessageTypeField_MessageTypes for example with quick watch, where I would expect to see 1 record, I am getting an entire stack of messageTypeField_MessageType records ~17, only filtered by MessageTypeFieldID and not by MessageTypeID as well. It should filter by the message type id on each iteration and only return me just the one record. Any ideas on what I am doing wrong, or how I can achieve what I need?
Sorry for the complicated example, but I hope you guys can help!
If you are looking at a navigation property (i.e. some child collection of mt), then this is unrelated to your query; it sounds like you want to use AssociateWith. This largely replaces the join if you are just trying to filter the child data...
Consider changing your select to:
select new
{
MessageType = mt,
MessageField = mtfmt
}
As Marc mentioned, changing the query is unrelated to the navigation properties. Something similar to the above is probably what you intended.
If you want to stick with the navigation properties, you should use a combination of AssociateWith and LoadWith. The first to filter, and the later to make it an eager load (so you don't end with multiple round trips).

Categories

Resources