I've build a database using Entity Framework Code First.
I was trying to do a simple LINQ query ad display data of a table in a listbox, but an int value always results as 0.
So I tried to do a debug and in XAML.CS at this point:
var item = (from Item in db.Items
select Item ).ToList();
My "Price" value is 0 when the entity is selected, but it should be 7
EDIT
Item.cs
public int Price
{
get { return price; }
set { price = value / 100; }
}
set { price = value / 100; }
There's your problem... I don't know what that's supposed to do, but remove the / 100 and it'll work.
7 / 100 is 0, since you're doing integer division.
If you want to show prices with decimal fractions, make the property and column a decimal.
Alternatively, if you're stuck with the int column for some reason, see How to map column and entity propery of different datatypes in entity framework code first how to do that:
// An int property to map to your database:
[Column("Price")]
public int PriceInt { get; set; }
// Use this property from code.
[NotMapped]
public decimal Price
{
// Cast to decimal for decimal division.
get { return (decimal)PriceInt / 100; }
set { PriceInt = (int)(value * 100); }
}
Related
My DataGridView looks like this:
How to clear the text of duplicate cells in the DataGridView Rows?
I tried below but it's clearing all values of Cells[0].
string duplicateValue = dataGridView1.Rows[0].Cells[0].Value.ToString();
for (int i = 1; i < dataGridView1.Rows.Count; i++)
{
if (dataGridView1.Rows[i].Cells[0].Value.ToString() == duplicateValue)
{
dataGridView1.Rows[i].Cells[0].Value = string.Empty;
}
else
{
dataGridView1.Rows[i].Cells[0].Value = duplicateValue;
}
}
One way to achieve this would be to use a HashSet as follows:
var valuesFound = new HashSet<string>();
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
string cellText = dataGridView1.Rows[i].Cells[0].Value.ToString();
// Attempt to add the value to the HashSet. If it fails, then it's a duplicate.
if (!valuesFound.Add(cellText))
{
dataGridView1.Rows[i].Cells[0].Value = string.Empty;
}
}
Or if you prefer LINQ, you could do something like this:
var duplicateCells = dataGridView1.Rows.OfType<DataGridViewRow>()
.Select(r => r.Cells[0])
.GroupBy(c => c.Value.ToString())
.SelectMany(g => g.Skip(1))
.ToList();
duplicateCells.ForEach(c => c.Value = string.Empty);
Short answer:
How to clear the text of duplicate cells in the DataGridView Rows?
Apparent you consider some products to be the same. Alas you forgot to say when two products are equal. Is Product [Apple, UK, 1] equal to [Apple, UK, 2]? And if so, which one do you want to show?
Or do you want to show the sum: [Apple, UK, 3]?
And what about: [Apple, Ireland, 1]? Is that the same as [Apple, UK, 1]?
Clearly you need a method that says: this product equals that product, but that one is a different product.
For this we'll have to create an equality comparer.
class Product
{
public Name {get; set;}
public string Country {get; set;}
public int Quantity {get; set;}
...
}
IEqualityComparer<Product> productComparer = ... // TODO: implement
Once you've got this, you can get rid of duplicates:
IEnumerable<Product> productsWithDuplicates = ...
IEnumerable<Product> noDuplicates = productsWithDuplicates.Distinct(productComparer);
Or if you want to combine [Apple, UK, 1] and [Apple, UK, 2] to show the sum [Apple, UK, 3], use groupBy to make groups:
IEnumerable<Product> productsToDisplay = productsWithDuplicates
.GroupBy(product => new {product.Name, product.Country}
(key, productsWithThisKey) => new Product
{
Name = key.Name,
Country = key.Country,
Quantity = productWithThisKey.Select(product => product.Quantity).Sum(),
},
productComparer);
So the solution depends on when two products are equal, and what you want to show if you've found equal produts.
Equality Comparer for Products
class ProductComparer : EqualityComparer<Product>()
{
public static IEqualityComparer<Product> NameCountry {get;} = new ProductComparer();
public override bool Equals(Product x, Product y)
{
if (x == null) return y == null; // true if both null, false if x null, but y not
if (y == null) return false; // because x not null
if (object.ReferenceEquals(x, y) return true;
// define equality, for instance:
return x.Name == y.Name && x.Country == y.Country;
}
If you want case insensitive, add a property:
private static IEqualityComparer<string> NameComparer {get; } = StringComparer.InvariantIgnoreCase;
private static IEqualityComparer<string> CountryComparer {get;} = ...
And in Equals:
return NameComparer.Equals(x.Name, y.Name)
&& CountryComparer.Equals(x.Country, y.Country);
Now if you later decide that you want to be case sensitive when comparing Countries, or maybe want to use the current culture, you'll only have to change this on one location.
The use of the comparers, makes changing easier, but also your code: you don't have to check for null names and countries, that is handled by the comparers.
GetHashCode: only requirement: if x equals y, return same GetHashCode. if not equal, you are free to return whatever you want, but it is more efficient if you return different hashcode.
public override int GetHashCode(Product product)
{
if (product == null) return 47855249; // just a number
return NameComparer.GetHashCode(product.Name)
^ CountryComparer.GetHashCode(product.Country);
}
There's room for improvement
It is usually not a good idea to intertwine your model with the view of your model. If in future you want to change how your data is displayed, for instance you want to show it in a ListBox, or in a Graph, you'll have to change a lot.
Besides, if you have separated your model from the way that it is displayed, it will be a lot easier to unit test your model. To test your view, you won't need your original data, you can test with edge conditions, like an empty datagridview,
First of all, you need a method to fetch the products from your model:
private IEnumerable<Productm> FetchProducts(...) {...}
So now you have a unit testable method that fetches the products. The nice thing is that you even hid where you get this information from: it can be from a database, or an XML file, or even from the internet: your Form doesn't know, and doesn't have to know. Totally designed for change.
Using visual studio designer you have defined columns. Every column shows exactly the value of one property. Which property the column shows is defined in property DataGridViewColumn.DataPropertyName
columnName.DataPropertyName = nameof(Product.Name);
columnCountry.DatapropertyName = nameof(Product.Country);
...
To show the fetched Products, use property DataGridView.DataSource. If you assign a List<Product>, then changes that the operator makes (add / remove rows, change cells) are not reflected in the List. If you want to automatically update the changes that the operator made, use a BindingList
public BindingList<Product> DisplayedProducts
{
get => (BindingList<Product>)this.datagridView1.DataSource;
set => this.datagridView1.DataSource = value;
}
private IEqualityComparer<Product> ProductComparer {get;} = ProductComparer.NameCountry;
public void InitProductDisplay()
{
IEnumerable<Product> productsToDisplay = this.FetchProducts()
.Distinct(productComparer);
// or if you want to show the totals: use the GroupBy described above
this.DisplayedProducts = new BindingList<Product>(productsToDisplay.ToList());
}
Nice! If you don't want to compare on NameCountry, but differently, or if you want to Compare using current culture, if you want to show the totals of the quantity, or even if you want to show it in a graph instead of a table: there is only one place you need to change.
Now every change that the operator makes: add / remove / change is reflected in your BindingList, even if the rows are sorted.
For instance, if the operator indicates that he finished editing by clicking a button:
private void OnButtonOk_Clicked(object sender, ...)
{
var displayedProducts = this.DisplayedProducts;
// find out which products are added / removed / changed
this.ProcessEditedProducts(displayedProducts);
}
If you need to do something with selected rows, consider to add the follwing:
private Product CurrentProduct => (Product)(this.datagridView1.CurrentRow?.DataBoundItem);
private IEnumerable<Product> SelectedProducts = this.datagridView1.SelectedRows
.Cast<DataGridViewrow>()
.Select(row => row.DataBoundItem)
.Cast<Product>();
I am quite new to MongoDb (and MongoDb C# Drivers) and lately, we are trying to implement an update wherein we use the value of a field and a variable (method parameter) to update several fields.
Basically, our doc is something like this
public class Inventory {
public string _id;
public decimal Quantity;
public decimal TotalCost;
}
What we want to do is to update the quantity and totalCost based on a passed value (qty).
(1) TotalCost -= (qty * (TotalCost / Quantity))
(2) Quantity -= Qty
The logic behind this is that we want to retain the average cost of our item.
Take note: the value of quantity field in step (1) should use the original value and not the result of step (2).
We can implement this using 2 queries but in our case, we need to execute this logic in one call only as there are different threads that will update a single item.
I have read the docs about Aggregate and Projection (and using Expressions) but I cannot seem to figure out how to use or combine the result of projection into the aggregate update.
Tried this projection to return the value that should be deducted from totalCost
Builders.Projection.Expression(e => (e.totalCost / e.quantity) *
-qty);
Thank you and hope you guys can point us in the right direction.
Here is an equivalent of what we are trying to achieve in mongo shell, provided that qty = 500.
db.inventory.updateOne(
{ _id: "1" },
[
{ "$set": { "TotalCost": { "$add": ["$TotalCost", { "$multiply": [-500, { "$divide": ["$TotalCost", "$Quantity"] }] }] } }
}
] )
There's currently no type-safe helper methods for creating update pipelines. However, you can just use the same json-ish syntax like you do with the console. Here's bit of example C# code.
// Some id to filter on
var id = ObjectId.GenerateNewId();
var db = client.GetDatabase("test");
var inventory = db.GetCollection<Inventory>("inventory");
var filter = Builders<Inventory>.Filter
.In(x => x.Id, id);
var update = Builders<Inventory>.Update.Pipeline(
new PipelineStagePipelineDefinition<Inventory, Inventory>(
new PipelineStageDefinition<Inventory, Inventory>[]
{
#"{ ""$set"": { ""TotalCost"": { ""$add"": [""$TotalCost"", { ""$multiply"": [-500, { ""$divide"": [""$TotalCost"", ""$Quantity""] }] }] } } }",
}));
await inventory.UpdateOneAsync(filter, update)
.ConfigureAwait(false);
I'm struggling to find the best way to store and represent the data I have in SQL (MySQL DB) and C# windows form.
My data when mapped to classes which looks like this;
public class Parent
{
public string UniqueID { get; set; } //Key
public DateTime LoadTime { get; set; }
public string Reference { get; set; }
private List<Child> Elements { get; set; }
}
public class Child
{
public int MemberCode { get; set; } //Composite key
public int ElementCode { get; set; } //Composite key
public Object Data { get; set; }
}
My data is very dynamic. So a parent record can have any number of child records.
In the child record then the MemberCode and ElementCode are actually foreign keys to other tables/classes, which when a look-up is performed gives me details of what the data actually is. For example
MemberCode = 1 & ElementCode = 1 means data is a Date
MemberCode = 1 & ElementCode = 3 means data is a telephone number
MemberCode = 2 & ElementCode = 11 means data is a Product Code
MemberCode = 2 & ElementCode = 12 means data is a Service Code
etc
These effectively combine to indicate what the column name is, and these are always the same (so MemberCode = 1 & ElementCode = 1 will always be a Date no matter which child object it is associated with).
At the moment these are references/lookups but I could also put the data in a variable in the class as that might make it easier. Then it would be more like a Key Value Pair.
At the moment in my DB I have these stored as two tables, with the child record also containing the UniqueID from the parent. But I'm, not sure that this is the best way as I will explain.
My tables are created as such
CREATE TABLE `PARENT` (
`ID` INT(11) NOT NULL AUTO_INCREMENT,
`LOADTIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`REFERENCE` VARCHAR(100) NOT NULL,
PRIMARY KEY (`ID`)
)
CREATE TABLE `CHILD` (
`ID` INT(11) NOT NULL,
`MEMBER_CODE` INT(11) NOT NULL,
`ELEMENT_CODE` INT(11) NOT NULL,
`DATA` VARCHAR(4000) NULL DEFAULT NULL,
PRIMARY KEY (`ID`, `MEMBER_CODE`, `ELEMENT_CODE`),
CONSTRAINT `fk_ID` FOREIGN KEY (`ID`) REFERENCES `Parent` (`ID`)
)
Now what I want to do is to flatten out this data so that I can display a single parent record with all child records as a single row. I ideally want to display it in an ObjectListView (http://objectlistview.sourceforge.net/cs/index.html) but can consider datagrid if it makes life easier.
Because my data is dynamic, then I'm struggling to flatten this out and if I select 10 parent records then each can have different number of child elements, and each can have different MemberCodes and ElementCode, which means that they are effectively different columns.
So my data could look like the following (but on a larger scale);
But because of the dynamic nature of the data, then I struggling to do this. Either in SQL or in Objects in my code. Maybe there is even another way to store my data which would suit it better.
After many many days working on this then I have managed to resolve this issue myself. What I done was the following;
In my original child class then the MemberCode and ElementCode make a unique key that basically stated what the column name was. So I took this a step further and added a "Column_Name" so that I had
public class Child
{
public int MemberCode { get; set; } //Composite key
public int ElementCode { get; set; } //Composite key
public string Column_Name { get; set; } //Unique. Alternative Key
public Object Data { get; set; }
}
This was obviously reflected in my database table as well.
My SQL to extract the data then looked like this;
select p.UniqueID, p.LoadTime, p.reference, c.MemberCode, c.ElementCode , c.column_name, c.Data
from parent as p, child as c
where p.UniqueID = c.UniqueID
//aditional filter criteria
ORDER BY p.UniqueID, MemberCode, ElementCode
ordering by the UniqueID first is critical to ensure the records are in the right order for later processing.
The I would use a dynamic and a ExpandoObject() to store the data.
So I iterate over the result to the convert the sql result into this structure as follows;
List<dynamic> allRecords = new List<dynamic>(); //A list of all my records
List<dynamic> singleRecord = null; //A list representing just a single record
bool first = true; //Needed for execution of the first iteration only
int lastId = 0; //id of the last unique record
foreach (DataRow row in args.GetDataSet.Tables[0].Rows)
{
int newID = Convert.ToInt32(row["UniqueID"]); //get the current record unique id
if (newID != lastId) //If new record then get header/parent information
{
if (!first)
allRecords.Add(singleRecord); //store the last record
else
first = false;
//new object
singleRecord = new List<dynamic>();
//get parent information and store it
dynamic head = new ExpandoObject();
head.Column_name = "UniqueID";
head.UDS_Data = row["UniqueID"].ToString();
singleRecord.Add(head);
head = new ExpandoObject();
head.Column_name = "LoadTime";
head.UDS_Data = row["LoadTime"].ToString();
singleRecord.Add(head);
head = new ExpandoObject();
head.Column_name = "reference";
head.UDS_Data = row["reference"].ToString();
singleRecord.Add(head);
}
//get child information and store it. One row at a time
dynamic record = new ExpandoObject();
record.Column_name = row["column_name"].ToString();
record.UDS_Data = row["data"].ToString();
singleRecord.Add(record);
lastId = newID; //store the last id
}
allRecords.Add(singleRecord); //stores the last complete record
Then I have my information stored dynamically in the flat manner that I required.
Now the next problem was the ObjectListView I wanted to use. This could not accept such dynamic types.
So I had the information stored within my code as I wanted, but I could still not display it as was required.
The solution was that was to use a variant of the ObjectListView known as the DataListView. This is effectively the same control but can be data bound.
Another alternative would also be to use a DatagridView, but I wanted to stick to the ObjectListView for other reasons.
So now I had to convert my dynamic data into a Datasource. This I done as follows;
DataTable dt = new DataTable();
foreach (dynamic record in allRecords)
{
DataRow dr = dt.NewRow();
foreach (dynamic item in record)
{
var prop = (IDictionary<String, Object>)item;
if (!dt.Columns.Contains(prop["Column_name"].ToString()))
{
dt.Columns.Add(new DataColumn(prop["Column_name"].ToString()));
}
dr[prop["Column_name"].ToString()] = prop["UDS_Data"];
}
dt.Rows.Add(dr);
}
Then I simply assign my datasource to the DataListView, generate the columns, and hey presto I now have my dynamic data extracted, flattened and displayed how I require.
I have a dropdownlist on a web form that has an item name and a price associated with it (which is not visible to the user). I am using selecteditem.Text and selectedvalue to capture the item name and the price. To combat duplicate entries for the selectedvalue I am storing entries like so
Signed Cap 10.0
Signed Glove 10.1
Signed Shirt 10.2
Bat Shavings .50
Hat Brim .50
Then parsing it out by using the below
String[] str = dropdownlist1.SelectedValue.ToString().Split('.');
String itemprice = str[0].Trim();
My syntax works great, EXCEPT for the decimal values! On Bat Shavings and Hat Brim I need to retain the decimal value! What should I alter or how should I set up my syntax to allow duplicate selected values or to keep the decimals? I understand that using str[0] is what is causing me to loose the decimals, BUT how can I work around it for the 2 (possibly more in the future) scenarios where they need to be remain in tact?
Its hard to tell from your posting how your getting your data, but I would load my data from the database into a data object, then bind that object to the drop down list.
Here is the Inventory Class I used to store the data from the database:
public class Inventory
{
public int ProductID { get; set; }
public string ProductDescription { get; set; }
public decimal ProductPrice { get; set; }
public Inventory(int ID, string Description, decimal Price)
{
this.ProductID = ID;
this.ProductDescription = Description;
this.ProductPrice = Price;
}
public string DDLValue
{
get
{
return string.Format("{0}|{1}|{2}", ProductID, ProductDescription, ProductPrice);
}
}
public string DDLText
{
get
{
return string.Format("{0} [{1}]", ProductDescription, ProductPrice.ToString("C"));
}
}
}
Here is a sample of how to configure the page control:
<asp:DropDownList ID="ddlProducts" runat="server" DataValueField="DDLValue" DataTextField="DDLText" />
In the page code behind, load your data into the drop down:
protected void LoadProductsFromDatabase()
{
System.Collections.Generic.List<Inventory> My_DDL_Datasource = new System.Collections.Generic.List<Inventory>();
// write your code to pull database values
// populating list with sample data for stackoverflow
// make sure to use a replace statement to remove any delimiter characters that may be in the description
My_DDL_Datasource.Add(new Inventory(1, "Product 1".Replace("|", ""), 0.50m));
My_DDL_Datasource.Add(new Inventory(2, "Product 2".Replace("|", ""), 1.50m));
My_DDL_Datasource.Add(new Inventory(3, "Product 3".Replace("|", ""), 0.50m));
My_DDL_Datasource.Add(new Inventory(4, "Product 4".Replace("|", ""), 10.50m));
ddlProducts.DataSource = My_DDL_Datasource;
ddlProducts.DataBind();
}
In the page code behind, create a method to get your drop down list selected value:
protected Inventory GetSelectedProduct()
{
try
{
if (ddlProducts.Items.Count == 0)
{
// do nothing, fall thru will return null
}
else
{
string[] DDLValue = ddlProducts.SelectedValue.Split('|');
int ivalue = 0;
int.TryParse(DDLValue.GetValue(0).ToString(), out ivalue);
decimal dvalue = 0.00m;
decimal.TryParse(DDLValue.GetValue(2).ToString(), out dvalue);
// only return object if the productid and product price were successfully parsed.
// this logic assumes no products are free
if (ivalue > 0 && dvalue > 0.00m)
{
return new Inventory(ivalue, DDLValue.GetValue(1).ToString(), dvalue);
}
}
}
catch { }
return null;
}
In the page code behind, do something with your selected value:
protected void DoSomethingWithValue()
{
Inventory MyInventoryItem = GetSelectedProduct();
if (MyInventoryItem != null)
{
// selected item successfully parsed
// do something with it.
Response.Write(
string.Format("Your selected product:<br />{0}<br />UniqueID: {1}<br />Price: {2}",
Server.HtmlEncode(MyInventoryItem.ProductDescription),
MyInventoryItem.ProductID,
MyInventoryItem.ProductPrice.ToString("C")
));
}
else
{
// error parsing information stored in drop down list value
}
}
You can split on space, and always take the last entry using linq:
dropdownlist1.SelectedValue.ToString().Split(' ').Last();
Note you should be using a hidden ItemId as the selectedvalue, instead of item name and price, and use a lookup table of:
ItemId|Name|Price
1|Hat|.50
2|Bat Shavings|.50
...
When the selected id is submitted you can lookup the name and price more directly. Also, the price being hidden in a form doesn't prevent the user from manipulating the price.
You need to remove/replace all alpha characters from string and keep only numeric.
Regex rgx = new Regex("[^a-zA-Z]");
str = rgx.Replace(str, "").Trim();
decimal prc;
Decimal.TryParse(str, out prc);
Am new to this guys need your help! Am trying to multiply two field and the answer to that will be the result of my computed field. One field is from another table..
partial void SubTotal_Compute(ref decimal result)
{
// Set result to the desired field value
result = this.Quantity * this.Rate.PulaPerUnit;
}
Every-time i try to add a new record to the table i get a Null Reference exception
Rate is likely to be null. You should add a test for it before performing your calculation, e.g.
if (Rate != null)
{
result = Quantity * Rate.PulaPerUnit;
}
You should probably ensure that Rate has been set before you call Subtotal_Compute.