More efficient way to check DataTable values? - c#

I want to iterate through my table, check to see if the Quantity received is higher than the quantity expected for each row, and if so execute some code.
The way I've done it below feels amateurish.
bool allRecieved = true;
foreach (DataRow row in SubVwr.Tables[0].Tbl.Rows)
{
if(row["QtyRcvd"] < row["QtyPer"]) allRecieved = false;
}
if(allRecieved) // execute code

You can use LINQ, for better readability (presuming those are int-columns):
bool allRecieved = SubVwr.Tables[0].Tbl.AsEnumerable()
.All(row => row.Field<int>("QtyRcvd") >= row.Field<int>("QtyPer"));
An advantage over your current loop is that this will stop as soon as one record doesn't match this condition. Your loop will continue until end without break.

This is a bit radical, but I'd start by not using DataTable, and instead use a simple model:
public class SomeType {
// I'd probably name these QuantityReceived etc, but... meh
public int QtyRcvd {get;set;}
public int QtyPer {get;set;}
// ... etc
}
Then I can very conveniently check properties etc. For example, to mirror Tim Schmelter's LINQ answer:
List<SomeType> someData = ...
var allReceived = someData.All(x => x.QtyRcvd >= x.QtyPer);
so now all we need is to load SomeType from the DB, which is where ORMs and micro-ORMs excel. For example, with "Dapper", this would be:
string region = "North"; // just to show parameter usage
var someData = connection.Query<SomeType>(
#"select * from SomeTable where Region=#region", new { region }).AsList();

Related

How to find the lowest ID in an array

I need to find the lowest customer id in the array and increase it by 1.
So I need the for cycle to search for the biggest id (0 by default) and increase by 1 every time ( create a new object of the customer class.
I really don't know how to implement the "i" value in order to make the cycle search for the id...
My model:
public class CustomerModel
{
public int IDCustomer {get; set;}
public string LastNameCustomer {get; set;}
public string FirstNameCustomer {get; set;}
public string AdressCustomer {get; set;}
}
My algorithm attempt:
CustomerModel[] MemoryCustomers = new CustomerModel[9];
OrderModel[] MemoryOrders = new OrderModel[9];
public string CreateCustomer(CustomerModel model)
{
CustomerModel NewCustomer = new CustomerModel();
for (int i = 0; i < 10; i++)
{
//What to put here
}
}
it is not a good practice to do that, you can use "SQL identity" if dealing with database.
or you can use type GUID to generate unique identifier for each customer.
Don't do that.
You should instead try to get the next id for a customer from some external provider. If you're using a database system, that can generate indices for you - you just need to define a counter or ID-generator or some such thing in the DBMS. How that is done depends on which DB you are using.
Otherwise, you could create your own method that you can call each time you need a new id; something like:
newCustomer.IDCustomer = GetNextId();
If doing that, make sure the method can never return any ID more than once, and you should be safe. There are several ways to do this; one is to constantly increase a number. Another is to use an UUID or GUID:
var id = Guid.NewGuid().ToString() // Generate a universally unique ID-string
The latter is especially useful if you want ID's that appear random, so that knowing one or two ID's won't make it easy to guess others (a common security problem / weakness in many apps).
Update:
Ok, ok: If you really want to simply get the next available ID as an int, based on existing int's, then try this:
private int GetNextId(){
// Create a list for easy handling with LINQ:
List<CustomerModel> customers = new List<CustomerModel)(MemoryCustomers);
// Select only ids, and only the highest:
int highestExistingId = customers
.Select(cust => cust.IDCustomer)
.Max();
return highestExistingId + 1;
}
If I've understood your question correctly now after re-reading it, I believe you want to update the lowest ID in your array, and replace it with a new customer,with the new ID - is that correct?
If so, try:
List<CustomerModel> customers = new List<CustomerModel)(MemoryCustomers);
var lowestId = customers.Select(cust => cust.IDCustomer).Min();
for (int i = 0; i < 10; i++)
{
// Identify the customer with that lowest id...
if(MemoryCustomers[i].IDCustomer == lowestId)
{
// ... replace him with the new one:
MemoryCustomers[i] = NewCustomer;
}
}
This may not be what you are searching for but it should work more consistent.
Add something like this to your Customer-Class:
private static int last_gen_id = -1;
public static int Gen_ID {
get {
last_gen_id++;
return last_gen_id;
}
}
And for every id, you want to generate, you just call Customer.Gen_ID and you got yourself a unique id. This will not work with threading, so be warned ;)

C# LINQ How to get MAX of datatable column<string> WHERE value LIKE 'N01%'

I need help, how do I get MAX datatable column value where value LIKE 'N01%'
Basically, if I convert this to SQL:
SELECT MAX(user) FROM tblUser WHERE user LIKE 'N01%'
Thank you.
You can simply do this:
string[] data = {"hello", "N01jaja", "N01ll"};
var userWithN1 = data.Where(we => we.StartsWith("N01")).Max();
StartsWith checks if the element starts with a certain string.
If there's a class then need to implement IComparable.
Sample code:
public class TestClass : IComparable<string>
{
public string Value { get; private set; }
public int CompareTo(string other) { return Value.CompareTo(other); }
}
var result = foo.tblUser.Where(u => u.user.StartsWith("N01")).Max(u => u.user));
Simply use a where statement to start your filter, use a StartsWith to emulate SQL's xx% pattern. Then use Max on a particular column. Though make sure User is something that will actually have a Max value.
In LINQ, I always find it helpful to break the problem down. Here in this case, you have a list of items, you want to narrow down that list with a WHERE clause and return the MAX of the remaining items.
Start
var myItems = db.GetMyList();
WHERE with LIKE
Assuming User is a string variable
myItems = myItems.Where(x=>x.User.StartsWith("N01"));
MAX
var maxItem = myItems.Max(x=>x.User);
All Together
var maxItem = db.GetMyList().Where(x=>x.User.StartsWith("N01")).Max(x=>x.User);
edit - Per comment below, since the search string was 'N01%', is should be starts with and not contains.

ASP.NET SqlDataReader and Lists

I am getting data via a SqlDataReader and now looping through the results and putting the results in the list. I am trying to add 2 columns to each list, but I am unsuccessful.
Here is my code:
for (int i = 0; i < reader.FieldCount; i++)
{
List<string> costs = new List<string>();
if (reader.GetName(i).ToString().Contains("TotalCost"))
{
costs.Add(reader.GetValue(i).ToString());
}
if (reader.GetName(i).ToString().Contains("SqftCost"))
{
costs.Add(reader.GetValue(i).ToString());
}
jobList.Add(costs);
}
But this puts the two columns in separate lists, I really need the 2 columns in one list.
The reason I am doing it like this is because I have columns that are called TotalCost101, SqftCost101, TotalCost102, SqftCost102, TotalCost104, SqftCost104. So each column that contains TotalCost and SqftCost should be in its own list. I hope this makes sense, anyone got any ideas on how to put these 2 columns in their own list. So at the end I will have a bunch of lists with 2 values.
I updated my code so I now use a Class instead of a List
for (int i = 0; i < reader.FieldCount; i++)
{
CostMatrix costs = new CostMatrix();
if (reader.GetName(i).ToString().Contains("TotalCost"))
{
costs.TotalCost = reader.GetValue(i).ToString();
}
if (reader.GetName(i).ToString().Contains("SqftCost"))
{
costs.sqftCost = reader.GetValue(i).ToString();
}
jobList.Add(costs);
}
Here is the current output:
<d3p1:CostMatrix>
<d3p1:TotalCost>550</d3p1:TotalCost>
<d3p1:sqftCost i:nil="true"/>
</d3p1:CostMatrix>
<d3p1:CostMatrix>
<d3p1:TotalCost i:nil="true"/>
<d3p1:sqftCost>0.41</d3p1:sqftCost>
</d3p1:CostMatrix>
What I am looking for is:
<d3p1:CostMatrix>
<d3p1:TotalCost>550</d3p1:TotalCost>
<d3p1:sqftCost>0.41</d3p1:sqftCost>
</d3p1:CostMatrix>
Honestly, I would use an object.
public class Price
{
public decimal Sqft { get; set; }
public decimal Total { get; set; }
}
You have an object that actually represents something tangible. You're clearly indicating what type of price is applicable. This will avoid confusion with other people working on the project and for you, with an expressive usage. Nothing is being obfuscated into a Tuple or string.
Then when you use the data reader, you could do something along these lines:
public static T GetValueOrNull<T>(this IDataReader reader, string column)
{
int ordinal;
if(!string.IsNullOrEmpty(column) && !reader.IsDBNull(reader.GetOrdinal(column)))
if(int.TryParse(reader.GetOrdinal(column).ToString(), out ordinal))
return (T)reader.GetValue(ordinal);
return default(T);
}
You can basically tell this, "which column" then assign it to that property. This could also be handled by some form of object relational mapper.
// Example:
List<Product> products = db.GetProducts();
var example = products.Where(o => o.Price.Sqft >= 5.00);
var sample = products.Where(o => o.Price.Total <= 5.00);
You can store Price inside of another object, allowing a web-site to filter a product based on how multiple types of price values, for instance. It has other benefits as well, it will also document your code nicely, to know how pricing may be implemented.
Not search for a collection of strings, how would that persist throughout your application? A List<string> would be hard to find all implementations for price, unless seeking a data attribute. But these are a bunch of reasons.
Hope this clarifies a bit.
Based on your update, you could do:
public class CostMatrix
{
public ConcurrentList<decimal> Total { get; set; }
public ConcurrentList<decimal> Sqft {get; set; }
}
Your object would have two separate list, then as you read through the table column by column and row by row, you could simply add. So if you used the above static method it would be:
using(var connection = new SqlConnection(dbConnection))
using(var command = new SqlCommand(query, dbConnection))
using(var reader = new SqlDataReader())
while(reader.Read())
{
Total.Add(GetValueOrNull<decimal>(reader, "TotalCost");
Sqft.Add(GetValueOrNull<decimal>(reader, "Sqft1");
}
I placed ConcurrentList because your implementation may use async. Wasn't sure, if not you can use a normal List. But a List isn't thread safe by default, you'd need a ConcurrentList.

using string.split in entity to traverse tree depth

i have the following self-referencing table
public partial class products_category
{
public long id { get; set; }
public string category_name { get; set; }
public string category_description { get; set; }
//self referencing to table id
public Nullable<long> Parent_Id { get; set; }
public string navPath {get; set; }
}
here string navpath contains all the leading parents for a child categroy, say:
"Clothes" = 1 Parent_id=null, navpath=""
"Silk" = 2 Parent_id=1 navpath="1"
"Silk Suit"=3 parent_id=2 navpath="1-2"
"Saree" =4 parent_id=3 navpath="1-2-3"
"Dress Material"=5 parent_id=1 navpath="1" and so on....
now as per this scenario i want to access the flattend tree for frther processing for a certain depth only say to level 2 or until level 4 depth of children associated with navpath.
my idea regarding this issue was to approach using linq to ef this way:
var catTrees = db.products_category.Where(pc => pc.navpath.Split('-').Length < 4).ToList();
i am using the following link to do further traversing and tree generation:
https://bitlush.com/blog/recursive-hierarchical-joins-in-c-sharp-and-linq
and it is doing a great work so far, the only issue is i dont want to pre select whole table for processing. i want to achieve paging and a certain level of depth for first iteration, so i can maintain performance in case of thousand of records. [think of this as a category hierarchy or blog/youtube comments hierarchy].
but using the above ef linq command is giving the following error:
The LINQ expression node type 'ArrayLength' is not supported in LINQ to Entities.
i checked with ef docs and other places in SO to know that string.split doesn't work with EF implicitly. but can we apply it using extension methods or can this tree selection have alternate approach without using string.split and hitting DB only ones?
please advice.
This looks like an issues with building SQL code out of your LINQ mpre specifically SQL which takes a string splits it on dash and counts the elements.
if you dont hate the idea of loading into memory then you can force anything :)
var catTrees = db.products_category.ToList().Where(pc => pc.navpath.Split('-').Length < 4).ToList();
The trick here is to force the execution of the SQL by adding the .ToList() when we want the data from the database. This is called realizing the data.
Even with that realization trick the count is faster
var catTrees = db.products_category.ToList().Where(pc => pc.navpath.Count(a => a == '-') < 3).ToList();
these solutions are essentially the same as
List<Result> filter() {
List<Result> r = new List<Result>();
foreach(var a in db.products_category) {
if(a.navpath.Count(a => a == '-') < 3) {
r.add(a);
}
}
return r;
}
When thinking about it the filter method is somewhat less memory intensive as it reads one and one and never stores everything in memory. (in theory at least, only a few really knows what the .NET compiler does in the shadows)
I would advice you against using the navpath for checking depth.
If you can change your model, you could add an additional numeric Depth field for each category and populate it according its navpath, then you could select them from your code in this way:
var catTrees = db.products_category.Where(pc => pc.Depth < 3).ToList();
There are many ways to populate that new column, but the bottom line is that you will have to do it just once (given that you keep track of it every time you modify the navpath of a category).
One possible way of populating it would be looping through all categories, something like:
var allCategories = db.products_category.ToList();
foreach(var category in allCategories)
{
var depth = category.navpath == "" ? 0 : category.navpath.Split('-').Length + 1;
category.Depth = depth;
}
allCategories.SubmitChanges();

How to get the first child value using dapper (typically querying count(*) as the only result)

I want to implement some function like this:
public static string GetResult(string sql) {
// TODO:
// result = connection.Query(....);
// return result.FirstRow().FirstChild().ToString();
}
And call like this:
string myName = GetResult("SELECT userName from tb_Users WHERE ID = 1");
// or
int totalRows = Convert.ToInt32(GetResult("SELECT count(*) FROM tb_List"));
How can I implement TODO section using Dapper ?
Dapper has ExecuteScalar[<T>] which can be used if you are reading one column, one row, one grid. So:
var name = connection.ExecuteScalar<string>("select 'abc'");
int count = connection.ExecuteScalar<int>("select 123");
There is also Query{First|Single}[OrDefault][<T>] for all the usual "sort of one row, multiple columns" scenarios.
A word of caution on your API: anything that only accepts a string of sql (and no separate parameters) makes me very nervous that you are about to cause sql injection problems.

Categories