I have a list where I wish to add items to dyanmically in the where clause if I dont have a user id at present the linq will fall over.
List<ScreenermissionsForSearch> _screen = new List<ScreenermissionsForSearch>();
_screen= _security.GetScreenermissionsForSearch();
gridControl1.DataSource = _screen.Where(w => w.Code ==
Convert.ToInt32(txtUserId.Text) || w.ScreenName ==dbscreenanme.Text).ToList();
this.gridView1.Columns[0].Width = 50;
this.gridView1.Columns[1].Width = 100;
So I need some wway of being able to append to the where clause checking if the string is null or not first or am I not doing this right in the frist place?.
Edit to show clairty Here it is just listing them all when i want it to only show provider if user id is empty.
It works here and shows fine as should do but its not for the other condition
New Code
_screen= _security.GetScreenermissionsForSearch();
gridControl1.DataSource = _screen.Where(w => string.IsNullOrEmpty(txtUserId.Text) || w.ScreenName == dbscreenanme.Text).ToList();
this.gridView1.Columns[0].Width = 50;
this.gridView1.Columns[1].Width = 100;
Add this condition to where clause string.IsNullOrEmpty(txtUserId.Text) and change the condition;
gridControl1.DataSource = _screen.Where(w =>
(string.IsNullOrEmpty(txtUserId.Text) || w.Code == Convert.ToInt32(txtUserId.Text)) &&
w.ScreenName == dbscreenanme.Text)).ToList();
If you don't want to get result when parsing is failed try following code;
gridControl1.DataSource = _screen.Where(w =>
w.Code == int.TryParse(txtUserId.Text,out var val) ? val : -1 &&
w.ScreenName == dbscreenanme.Text)).ToList();
var entity = Context.Parents.Include(x => x.Name).ToList();
Related
I have a given date and I want to get the element on a table that is greater than the given date, but if it's not found, I want to return the element with the NULL date.
For example:
Table:
If the given date is, for example, 2019-10-20, I want to return the element with the ID 3.
If the given date is, for example, 2019-11-20, I want to return the element with the ID 4.
I've tried doing this:
var parameter = _db.Parameter
.OrderBy(x => x.EndDate)
.First(givenDate < x.EndDate) || x.EndDate == null);
But it's always returning the last element. What do I need to do?
You can try this:
var query = _db.Parameter
.OrderBy(x => x.EndDate)
.ToList();
var parameter = query.FirstOrDefault(x => givenDate < x.EndDate);
if ( parameter == null )
parameter = query.FirstOrDefault(x => x.EndDate == null);
if ( parameter == null )
...
else
...
We create an initial ordered query on the date, taking a list to evaluate the query.
Next we check if the first givenDate matching the desired result.
Else we try to get the first null row.
Then you can manage the final case.
To avoid parsing twice the query you can use that:
TheTypeOfParameter parameterFound = null;
TheTypeOfParameter parameterHavingDateNotNull = null;
TheTypeOfParameter parameterHavingDateNull = null;
bool foundDateNull = false;
bool foundDateNotNull = false;
foreach ( var item in query )
{
if ( !foundDateNull && item.EndDate == null )
{
parameterHavingDateNull = item;
foundDateNull = true;
}
else
if ( !foundDateNotNull && item.EndDate > givenDate )
foundDateNotNull = true;
if ( !foundDateNotNull )
parameterHavingDateNotNull = item;
if ( foundDateNotNull || foundDateNull )
break;
}
parameterFound = parameterHavingDateNotNull != null
? parameterHavingDateNotNull
: parameterHavingDateNull;
Because I can't test and debug, I hope this loop will work...
I would optimise the code by NOT using LINQ for this:
var parameter = _db.Parameter[0]; // you may need to handle that there's at least 1 item.
for (int i = 1; i < _db.Parameter.Count; i++)
{
var param = _db.Parameter[i];
if (param.EndDate > givenDate)
{ // param is good
if (parameter.EndDate == null || parameter.EndDate > param.EndDate)
parameter = param; // replace parameter with param
}
else if (parameter.EndDate != null && parameter.EndDate < givenDate)
{ // parameter precedes given date, replace it!
parameter = param;
}
}
This will iterate through your list just once, unlike the other solutions provided so far.
If you MUST use LINQ and want to iterate once, maybe you can use the below, which will return a dynamic though, so you need to convert it back to a Parameter. It works by replacing the NULL with DateTime.MaxValue so that when you do an OrderBy, the entries that were NULL would be ordered at the bottom.
var param = _db.Parameter
.Select(x => new
{
ID = x.ID,
EndDate = (x.EndDate.HasValue) ? x.EndDate : DateTime.MaxValue,
Value = x.Value
})
.OrderBy(x => x.EndDate)
.FirstOrDefault();
var parameter = new Parameter()
{
ID = param.ID,
EndDate = (param.EndDate == DateTime.MaxValue) ? null : param.EndDate,
Value = param.Value
};
As mentioned in the comments, NULL will be first after the ordering.
What if you try the following:
var parameter = _db.Parameter
.Where(x => (x.EndDate > givenDate) || (x.EndDate == null))
.OrderBy(x => x.EndDate)
.Last();
After selecting only earlier dates the latest is chosen. If only one element is in the list (the NULL element), this one gets chosen.
I have a list of objects. If any of the properties equal null I want that entire element removed from the list. Is there a better way to do it then what I have below.
I've tried for loops and for each and either can't figure out how to do it or don't fully understand how to accomplish my task.
var i = 0;
while (i < filterCriterias.Count())
{
if (filterCriterias[i].ColumnName == null
|| filterCriterias[i].Comparator == null
|| filterCriterias[i].Criteria == null)
{
filterCriterias.RemoveAt(i);
}
else
{
i++;
}
}
So if I have the list below:
List<Order> orders = new List<Order>
{
new Order {ColumnName = null, OperantOrder = null},
new Order {ColumnName = Session, OperantOrder = ASC},
new Order {ColumnName = null, OperantOrder = null},
}
I only want the list to only contain the element 1 where columnName = session and operantorder = asc.
A more idiomatic approach would be to use RemoveAll:
filterCriterias.RemoveAll(c => c.ColumnName == null ||
c.Comparator == null ||
c.Criteria == null);
There is no need of removing just select what you want using System.Linq like below. This will return a new collection.
var order = orders.Where(x => x.ColumnName == Session && x.OperantOrder == ASC).ToList();
You can as well consider using FirstOrDefault() like
var order = orders.FirstOrDefault(x => x.ColumnName == Session && x.OperantOrder == ASC);
To use a for loop when removing items from a list, it's normally best to start at the last element and work backwards. This way you don't end up skipping items or get an IndexOutOfRange exception because the Count changed during the loop (which happens when removing items in a forward direction):
// Start at the last index and move towards index '0'
for (int i = filterCriterias.Count - 1; i >= 0; i--)
{
if (filterCriterias[i].ColumnName == null ||
filterCriterias[i].Comparator == null ||
filterCriterias[i].Criteria == null)
{
filterCriterias.RemoveAt(i);
}
}
I have made a simple database search program using c# linq and mysql(code below) which works pretty well. This database has 16 columns 6 of which are for address (State,City,District,Street, Building Name, Door Number). My code now searches for many variations of indexes aside from anything related to address and as seen below ID is overriding value. What my prof. wants is to search with ID and address values to find who might be from the same place. The way this is wanted is to have a checkbox.
If checkbox is clicked and ID entered the search result returned with everyone with the same address and if nobody else has the same address then just the entered ID value to return.The rest of the index values doesn't needed for this operation. My problem with this whole equation is I can't find any applicable way to join the 6 address columns and do a double search with ID and the whole adress. I have to use linq as it is required.
Code Sample;
var query = from i in sqlcmd.table select i;
if (ID.Text.Length > 0)
{
double id = Convert.ToDouble(ID.Text);
query = query.Where(s => s.ID == id);
}
else
{
if (Name.Text.Length > 0)
{
query = query.Where(s => s.Name == Name.Text);
}
if (Sname.Text.Length > 0)
{
query = query.Where(s => s.Sname == Sname.Text);
}
if (ClassList.Text.Length > 0)
{
query = query.Where(s => s.ClassList == ClassList.Text);
}
}
gridview.DataSource = query.ToList();
P.S: Thx to #juancarlosoropeza for heads-up of the mess of a question I made.
just include the check value on the if conditions.
if (chkName.Checked && Name.Text.Length > 0)
{
query = query.Where(s => s.Name == Name.Text);
}
if (chkSName.Checked && Sname.Text.Length > 0)
{
query = query.Where(s => s.Sname == Sname.Text);
}
if (chkClassList.Checked && ClassList.Text.Length > 0)
{
query = query.Where(s => s.ClassList == ClassList.Text);
}
I don't know if I completely understood what you are looking for but I'll give it a try.
If I got you right you are looking for a way to find anybody who has the same address as the person with a given ID. If you want to exclude the person with the given ID just uncomment the last line in the where-clause of the query.
var query = from i in sqlcmd.Table select i;
if (ID.Text.Length > 0)
{
var personGivenByID = from person in query.AsEnumerable()
where person.ID == Convert.ToDouble(ID.Text)
select person;
var sameAddressLikeGivenPerson = from row in query.AsEnumerable()
where row.State == personGivenByID.FirstOrDefault().State
&& row.City == personGivenByID.FirstOrDefault().City
&& row.District == personGivenByID.FirstOrDefault().District
&& row.Street == personGivenByID.FirstOrDefault().Street
&& row.BuildingName == personGivenByID.FirstOrDefault().BuildingName
&& row.DoorNumber == personGivenByID.FirstOrDefault().DoorNumber
//&& row.ID != personGivenByID.FirstOrDefault().ID
select row;
gridview.DataSource = sameAddressLikeGivenPerson != null ? sameAddressLikeGivenPerson : sameAddressLikeGivenPerson;
}
How to update linq to sql values with select new keyword with anonymous types because I am using var keyword with select new query I need in in this but it returns an error like this
Compiler Error Message: CS0200: Property or indexer 'AnonymousType#1.Code' cannot be assigned to -- it is read only
This is my code:
var ProjectView12 = (from x in db.Projects
select new
{
add = db.Locations.Where(y = > y.ID == x.RegionID).FirstOrDefault().Name,
Province = db.Locations.Where(y = > y.ID == x.ProvinceID).FirstOrDefault().Name,
District = db.Locations.Where(y = > y.ID == x.DistrictID).FirstOrDefault().Name,
Code = x.Code,
Name = x.Name,
ProjectIdentificationDate = db.Milestones.Where(y = > y.ProjectID == x.ID && y.StageID == 1 && y.ComponentID == 1 && y.ModuleID == 1).FirstOrDefault().Date.ToString(),
ProjectLat = Convert.ToDecimal(x.Lat),
ProjectLong = Convert.ToDecimal(x.Lon),
Remarks = db.Milestones.Where(y = > y.ProjectID == x.ID && y.StageID == 1 && y.ComponentID == 1 && y.ModuleID == 1).FirstOrDefault().Remarks.ToString(),
}).ToList();
foreach(var item in ProjectView12)
{
item.Code = txtSubProjectCode.Text;
item.Name = txtSubProjectName.Text;
item.ProjectLat = Convert.ToDecimal(txtLatitude.Text);
item.ProjectLong = Convert.ToDecimal(txtLongitude.Text);
item.ProjectIdentificationDate = txtDate.Text;
item.Remarks = txtRemarks.Text;
} // txtLocation.Text = item.Region + ">" + item.Province + ">" + item.District;
try
{
db.SubmitChanges();
}
catch (Exception ex)
{
throw ex;
}
Well, you're getting the compiler error because - as it says - the properties of anonymous types are read-only in C#.
More fundamentally, even if you could modify them, you'd have to expect LINQ to SQL to reverse whatever you put into your projection in order to update the original tables. That seems a fairly tall order to me - particularly for the Remarks property in this particular case.
Basically, in order to update the database, you need to make changes to the entities that are mapped from your tables. Make your query select the relevant entities, then you can project from those later on in client-side code, if necessary - so long as you've still got a reference to the entity to modify.
I have a table in a SQL Server database with many columns but the important columns are LoggedState and InteractionType.
I need to find the number of break agents and the number of idle agents.
What I have tried
SqlCommand GraphCmd = new SqlCommand("getAgentStatues", Graphsqlcon);
SqlParameter tdate = new SqlParameter();
GraphCmd.CommandType = CommandType.StoredProcedure; ;
SqlDataAdapter DAGraph = new SqlDataAdapter(GraphCmd);
DataSet DSGraph = new DataSet();
DSGraph.Clear();
DAGraph.Fill(DSGraph);
DataTable DTgraph = new DataTable();
DTgraph = DSGraph.Tables[0];
int numberOfBreakAgents = 0;
int numberOfIdelAgents = 0;
foreach (DataRow row in DTgraph.Rows)
{
String LoggedState = row["LoggedState"].ToString().Trim().ToLower();
String InteractionType = row["InteractionType"].ToString();
if (LoggedState == "break")
{
numberOfBreakAgents++;
}
else if ((LoggedState == "activo") && (row["InteractionType"] == DBNull.Value))
{
numberOfIdelAgents++;
}
}
it works perfectly, but I am asking if there is a way (like grouping) to avoid the foreach statement
You could use the Group function from Linq:
var loggedStateGroups = dt.AsEnumerable().GroupBy(d => d["LoggedState"].ToString(), (group, row) => new
{
LoggedState = group,
AllCount = row.Count(),
NullCount = row.Where(r => r["InteractionType"] == DBNull.Value).Count()
});
That will group by the LoggedState with a count for each matching row (AllCount) and a count for rows where the InteractionType is DBNull.Value (NullCount).
We can then select the counts we are after by doing:
int numberOfBreakAgents = loggedStateGroups.Where(y => y.LoggedState == "break").First().AllCount;
int numberOfIdelAgents = loggedStateGroups.Where(y => y.LoggedState == "activo").First().NullCount;
Note I'm only using First assuming you will always have results. If you won't always have results you should use FirstOrDefault and perform a null check.
You could filter before using the Group by adding the following Where depending on your data.
.Where(r => r["LoggedState"].ToString() == "break" || r["LoggedState"].ToString() == "activo")
I've tested this with the following setup:
DataTable dt = new DataTable();
dt.Columns.Add("LoggedState");
dt.Columns.Add("InteractionType");
dt.Rows.Add("break", "inter1");
dt.Rows.Add("activo", DBNull.Value);
dt.Rows.Add("break", "inter1");
dt.Rows.Add("break", "inter2");
dt.Rows.Add("activo", "inter2");
And I get 3 and 1 for the numberOfBreakAgents and numberOfIdelAgents respectively.
Edit for using FirstOrDefault:
If you'd like to perform the null check as mentioned above you can replace the two int declaration lines above with:
var breakAgents = loggedStateGroups.Where(y => y.LoggedState == "break").FirstOrDefault();
var idelAgents = loggedStateGroups.Where(y => y.LoggedState == "activo").FirstOrDefault();
int numberOfBreakAgents = breakAgents != null ? breakAgents.AllCount : 0;
int numberOfIdelAgents = idelAgents != null ? idelAgents.NullCount : 0;
This is taking the first group that has the LoggedState of "break" or null if there isn't one. It then assigns numberOfBreakAgents the AllCount property if the group is not null or 0 if it is.
A similar thing is done for numberOfIdelAgents except we filter for the "activo" group and use the NullCount property as we aren't interested in all rows we are only interested in those where the InteractionType was DBNull.Value which we've captured in the NullCount property.
The null check is necessary if the result set will ever contain zero rows with the LoggedState of "activo" or zero rows with the LoggedState of "break". In that instance the .First() will return null and accessing AllCount or NullCount from that will result in a "Sequence contains no elements" exception.
Using the following DataTable definition will highlight the difference as it causes an exception for numberOfBreakAgents using First() but correctly returns 0 when using FirstOrDefault.
DataTable dt = new DataTable();
dt.Columns.Add("LoggedState");
dt.Columns.Add("InteractionType");
dt.Rows.Add("activo", "inter1");
dt.Rows.Add("activo", DBNull.Value);
dt.Rows.Add("activo", "inter1");
dt.Rows.Add("activo", "inter2");
dt.Rows.Add("activo", "inter2");
Could you not do
var breakAgents = from row in DTgraph.AsEnumerable()
where row["LoggedState"].ToString().Trim().ToLower() == "break"
select row;
var breakAgentsCount = breakAgents.Count();
and
var idleAgents = from row in DTgraph.AsEnumerable()
where row["LoggedState"].ToString().Trim().ToLower() == "activo"
&& row["InteractionType"] == DBNull.Value
select row;
var idleAgentsCount = idleAgents.Count();
Using the Count-function that LINQ provides us, the following solution should work:
// Cast the rows to a collection of DataRows.
IEnumerable<DataRow> collection = DTgraph.Rows.Cast<DataRow>();
// Get the number of Break Agents.
int numberOfBreakAgents = collection.Count(row => row["LoggedState"].ToString().Trim().ToLower() == "break");
// Get the number of Idel Agents.
int numberOfIdelAgents = collection.Count(row => row["LoggedState"].ToString().Trim().ToLower() == "activo" && row["InteractionType"] == DBNull.Value);
The cast is used to allow the use of LINQ on the DataRow-collection.
Another option would be to cast the DataRow-collection to a List of type DataRow. Then using a ForEach (also LINQ), to determine the agent-type:
List<DataRow> collection = DTgraph.Rows.Cast<DataRow>().ToList();
collection.ForEach(row =>
{
if (row["LoggedState"].ToString().Trim().ToLower() == "break")
numberOfBreakAgents++;
else if (row["LoggedState"].ToString().Trim().ToLower() == "activo" && row["InteractionType"] == DBNull.Value)
numberOfIdelAgents++;
});
Above example is very much the same to your example, but written a bit shorter and without the use of two strings (LoggedState and InteractionType).
You can execute sql query like this:
select
sum(case when LoggedState = "break" then 1 else 0 end) break_count,
sum(case when LoggedState = "activo" and InteractionType is null then 1 else 0 end) active_count
from table_name