DataBind to ICollection<T> - c#

My program should read and process data from legacy flat files.
I read the data into private ICollection<transaction> transactions;
public class transaction
{
public int transactionNum;
public DateTime transactionDate;
public int orderNum;
public string customerName;
public Decimal amount;
}
How do I create a DataSource to this ICollection<T> (or a LINQ query of it), so that I can DataBind several control values to it?
Note: I have minimal LINQ knowledge. In the past, I could just drag a database table into my application.

From your comment:
On the ASP.NET form, I have a CheckBoxList named customerCheckL. Upon
loading the data file, the code should populate customerCheckL.Items
with the list of distinct customer names. How do I do that?
customerCheckL.DataSourceID = ???
That makes more sense. You could implement an EqulityComparer<transactions> class which compares by customer:
public class TransactionCustomerComparer : IEqualityComparer<transaction>
{
public bool Equals(transaction x, transaction y)
{
if (x == null || y == null) return false;
return x.customerName == y.customerName;
}
public int GetHashCode(transaction obj)
{
if (obj == null) return int.MinValue;
return obj.customerName.GetHashCode();
}
}
(Note that you can use this method in all Enumerable methods that allow to pass a custom comparer)
Then you can use Distinct to get a unique list. You just have to set the DataSource, DataTextField and DataValueField and DataBind the CheckBoxList.
var customerComparer = new TransactionCustomerComparer();
customerCheckL.DataSource = transactions.Distinct(customerComparer);
customerCheckL.DataTextField = "customerName";
customerCheckL.DataValueField = "transactionNum";
customerCheckL.DataBind();

Related

Replacing boilerplate code C# with inteligent methods (maybe by using pointers?)

We are writing a data repository and we have a data model that has quite a few fields.
And on the top of that, we have data accessors that return requested field value of the data object with the given id.
However, the problem is that we have to write a method for every single field which results in a ton of boilerplate code that literally differs in the field accessed and sometimes the return value type.
Here is the code:
[Serializable]
struct MyItem
{
public readonly int Id;
public int SomeInt;
public float SomeFloat;
// in real-life scenario there are many more fields here
}
// we need this container because our JSON parser needs one
class MyItemDataList
{
public List<MyItem> Items = new List<MyItem>();
}
class Example
{
readonly MyItemDataList _myList = new MyItemDataList();
public float GetSomeFloat(int id)
{
for (int i = 0; i < _myList.Items.Count; i++)
{
int currentId = _myList.Items[i].Id;
if (id == currentId)
return _myList.Items[i].SomeFloat;
}
return 0; // just so it compiles for the sake of this example
}
public int GetSomeInt(int id)
{
for (int i = 0; i < _myList.Items.Count; i++)
{
int currentId = _myList.Items[i].Id;
if (id == currentId)
return _myList.Items[i].SomeInt; // all of this methods differ only in the field accessed and value type returned
}
return 0; // just so it compiles for the sake of this example
}
// in real-life scenario there are many more fields here
}
One solution would be to write a switch (if-else) statement and simply in addition to id pass also the field requested but that gives us a very long switch and many comparisons (not a big deal but still).
Ideally, what I would like to achieve is some sort of a template method (maybe with use of pointers) that based on the field requested simply shift the pointer to the desired address and returns the bytes that correspond to the requested field. But how to do it in C#?
You can use generics and accept a Func<MyItem, T>. No need to use pointers, which are considered unsafe in C#.
class Example
{
readonly MyItemDataList _myList = new MyItemDataList();
public T GetSomeProperty<T>(int id, Func<MyItem, T> propertySelector)
{
for (int i = 0; i < _myList.Items.Count; i++)
{
int currentId = _myList.Items[i].Id;
if (id == currentId)
return propertySelector(_myList.Items[i]);
}
return default(T);
}
}
Usage:
For example, if you want to get SomeFloat:
GetSomeProperty(someId, x => x.SomeFloat)
The method could also be implemented with LINQ:
public T GetSomeProperty<T>(int id, Func<MyItem, T> propertySelector)
=> _myList.Items.Where(x => x.Id == id)
.Select(propertySelector)
.FirstOrDefault()
Replace the list against a dictionary.
public class MyItemDataList
{
public Dictionary<int, MyItem> Items { get;} = new Dictionary <int, MyItem>();
}
Create an extension method that accesses the dictionary for convenience
public static MyItem GetItem(this Dictionary<int, MyItem> dictionary, int id) => dictionary.TryGetValue(id, out var item)? item : null;
Usage
var #float = Items.GetItem(2)?.SomeFloat ?? 0.0;

HashSet initialized using IEnumerable contains duplicate elements

I want to be able to use HashSet constructed from IEnumerable collection of custom objects without duplicates. My custom object contains id and some other properties which aren't important for this question. I make a query to a database which returns an IEnumerable that I later use to construct a HashSet with the following code:
HashSet<Question> results = new HashSet<Question>(new QuestionComparer());
var result = await query.ExecuteNextAsync<Question>();
results.UnionWith(result);
The problem is there are duplicate records inside the result collection which I do not want.
The QuestionComparer class looks like this:
public class QuestionComparer : IEqualityComparer<Question>
{
public bool Equals(Question x, Question y)
{
return x != null && y != null && x.Id == y.Id;
}
public int GetHashCode(Question obj)
{
return obj.Id.GetHashCode();
}
}
I also tried overriding both Equals and GetHashCode methods inside the Question class but no success. I considered looping through the collection and removing the duplicates, but it seems like it may become a performance problem...
EDIT: Azure DocumentDB that I am using apparently does not currently support a "distinct" type of query.
Instead of writing a public class QuestionComparer you should override the methods of the existing Question class
public class Question
{
public string ID { get; set; }
public override int GetHashCode()
{
return ID.GetHashCode();
}
public override bool Equals(System.Object obj)
{
return (obj != null && obj is Question) ? (this.ID == ((Question)(obj)).ID) : false;
}
}
So duplicates are not possible. Sample:
HashSet<Question> qh = new HashSet<Question>();
qh.Add(new Question() { ID = "1" });
qh.Add(new Question() { ID = "1" }); //will not be added
qh.Add(new Question() { ID = "2" });
https://dotnetfiddle.net/wrFTaA

Is there a way of comparing all the values within 2 entities?

I'm using EF4.3 so I'm referring to entities, however it could apply to any class containing properties.
I'm trying to figure out if its possible to compare 2 entities. Each entity has properties that are assigned values for clarity let say the entity is 'Customer'.
public partial class Customer
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
The customer visits my website and types in some details 'TypedCustomer'. I check this against the database and if some of the data matches, I return a record from the database 'StoredCustomer'.
So at this point I've identified that its the same customer returning but I wan't to valid the rest of the data. I could check each property one by one, but there are a fair few to check. Is it possible to make this comparison at a higher level which takes into account the current values of each?
if(TypedCustomer == StoredCustomer)
{
.... do something
}
If you're storing these things in the database, it is logical to assume you'd also have a primary key called something like Id.
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
Then all you do is:
if(TypedCustomer.Id == StoredCustomer.Id)
{
}
UPDATE:
In my project, I have a comparer for these circumstances:
public sealed class POCOComparer<TPOCO> : IEqualityComparer<TPOCO> where TPOCO : class
{
public bool Equals(TPOCO poco1, TPOCO poco2)
{
if (poco1 != null && poco2 != null)
{
bool areSame = true;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object v1 = property.GetValue(poco1, null);
object v2 = property.GetValue(poco2, null);
if (!object.Equals(v1, v2))
{
areSame = false;
break;
}
});
return areSame;
}
return poco1 == poco2;
} // eo Equals
public int GetHashCode(TPOCO poco)
{
int hash = 0;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object val = property.GetValue(poco, null);
hash += (val == null ? 0 : val.GetHashCode());
});
return hash;
} // eo GetHashCode
} // eo class POCOComparer
Uses an extension method:
public static partial class TypeExtensionMethods
{
public static PropertyInfo[] GetPublicProperties(this Type self)
{
self.ThrowIfDefault("self");
return self.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where((property) => property.GetIndexParameters().Length == 0 && property.CanRead && property.CanWrite).ToArray();
} // eo GetPublicProperties
} // eo class TypeExtensionMethods
Most simple seems to use reflexion : get the properties and/or fields you want to compare, and loop through them to compare your two objects.
This will be done with getType(Customer).getProperties and getType(Customer).getFields, then using getValue on each field/property and comparing.
You might want to add custom informations to your fields/properties to define the ones that needs
comparing. This could be done by defining a AttributeUsageAttribute, that would inherit from FlagsAttribute for instance. You'll then have to retrieve and handle those attributes in your isEqualTo method.
I don't think there's much of a purpose to checking the entire object in this scenario - they'd have to type every property in perfectly exactly as they did before, and a simple "do they match" doesn't really tell you a lot. But assuming that's what you want, I can see a few ways of doing this:
1) Just bite the bullet and compare each field. You can do this by overriding the bool Equals method, or IEquatable<T>.Equals, or just with a custom method.
2) Reflection, looping through the properties - simple if your properties are simple data fields, but more complex if you've got complex types to worry about.
foreach (var prop in typeof(Customer).GetProperties()) {
// needs better property and value validation
bool propertyMatches = prop.GetValue(cust1, null)
.Equals(prop.GetValue(cust2, null));
}
3) Serialization - serialize both objects to XML or JSON, and compare the strings.
// JSON.NET
string s1 = JsonConvert.SerializeObject(cust1);
string s2 = JsonConvert.SerializeObject(cust2);
bool match = s1 == s2;

C# Select clause returns system exception instead of relevant object

I am trying to use the select clause to pick out an object which matches a specified name field from a database query as follows:
objectQuery = from obj in objectList
where obj.Equals(objectName)
select obj;
In the results view of my query, I get:
base {System.SystemException} = {"Boolean Equals(System.Object)"}
Where I should be expecting something like a Car, Make, or Model
Would someone please explain what I am doing wrong here?
The method in question can be seen here:
// this function searches the database's table for a single object that matches the 'Name' property with 'objectName'
public static T Read<T>(string objectName) where T : IEquatable<T>
{
using (ISession session = NHibernateHelper.OpenSession())
{
IQueryable<T> objectList = session.Query<T>(); // pull (query) all the objects from the table in the database
int count = objectList.Count(); // return the number of objects in the table
// alternative: int count = makeList.Count<T>();
IQueryable<T> objectQuery = null; // create a reference for our queryable list of objects
T foundObject = default(T); // create an object reference for our found object
if (count > 0)
{
// give me all objects that have a name that matches 'objectName' and store them in 'objectQuery'
objectQuery = from obj in objectList
where obj.Equals(objectName)
select obj;
// make sure that 'objectQuery' has only one object in it
try
{
foundObject = (T)objectQuery.Single();
}
catch
{
return default(T);
}
// output some information to the console (output screen)
Console.WriteLine("Read Make: " + foundObject.ToString());
}
// pass the reference of the found object on to whoever asked for it
return foundObject;
}
}
Note that I am using the interface "IQuatable<T>" in my method descriptor.
An example of the classes I am trying to pull from the database is:
public class Make: IEquatable<Make>
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Model> Models { get; set; }
public Make()
{
// this public no-argument constructor is required for NHibernate
}
public Make(string makeName)
{
this.Name = makeName;
}
public override string ToString()
{
return Name;
}
// Implementation of IEquatable<T> interface
public virtual bool Equals(Make make)
{
if (this.Id == make.Id)
{
return true;
}
else
{
return false;
}
}
// Implementation of IEquatable<T> interface
public virtual bool Equals(String name)
{
if (this.Name.Equals(name))
{
return true;
}
else
{
return false;
}
}
}
And the interface is described simply as:
public interface IEquatable<T>
{
bool Equals(T obj);
}
IQueryable<T> executes your query against the backing data store (in this case, it's SQL RDBMS). Your SQL RDBMS has no idea of IEquatable<T>*, and cannot use its implementation: the query function must be translatable to SQL, and obj.Equals(objectName) is not translatable.
You can convert IQueryable<T> to IEnumerable<T> and do the query in memory, but that would be too inefficient. You should change the signature to take a Expression<Func<TSource, bool>> predicate, and pass the name checker to it:
public static T Read<T>(Expression<Func<T,bool>> pred) {
using (ISession session = NHibernateHelper.OpenSession()) {
return session.Query<T>().SingleOrdefault(pred);
}
}
You can now use this method as follows:
Make snake = Read<Make>(x => x.Name == "snake");
* Additionally, your IEquatable<T> is not used in the method that you are showing: the Equals(string) method would qualify as an implementation of IEquatable<string>, but it is not mentioned in the list of interfaces implemented by your Make class.
One of your calls to the Equal override could be throwing an exception.
For debugging purposes, try iterating rather than using Linq and you should find out which one:
foreach(object obj in objectList)
{
bool check = obj.Equals(objectName) // set a breakpoint here
}
As far as what is causing the exception, ensure that each of your object Name and Id properties have values.

LINQ-to-XML to DataGridView: Cannot edit fields -- How to fix?

I am currently doing LINQ-to-XML and populating a DataGridView with my query just fine. The trouble I am running into is that once loaded into the DataGridView, the values appear to be Un-editable (ReadOnly). Here's my code:
var barcodes = (from src in xmldoc.Descendants("Container")
where src.Descendants().Count() > 0
select
new
{
Id = (string)src.Element("Id"),
Barcode = (string)src.Element("Barcode"),
Quantity = float.Parse((string)src.Element("Quantity").Attribute("value"))
}).Distinct();
dataGridView1.DataSource = barcodes.ToList();
I read somewhere that the "DataGridView will be in ReadOnly mode when you use Anonymous types." But I couldn't find an explanation why or exactly what to do about it.
Any ideas?
EDIT -- Here's the answer I came up with...
So I added a "Container" class (with Get and Set <-- very important!) to avoid the Anonymous types being ReadOnly issue:
public class Container
{
public string Id { get; set; }
public string Barcode { get; set; }
public float Quantity { get; set; }
}
// For use with the Distinct() operator
public class ContainerComparer : IEqualityComparer<Container>
{
public bool Equals(Container x, Container y)
{
return x.Id == y.Id;
}
public int GetHashCode(Container obj)
{
return obj.Id.GetHashCode();
}
}
and changed the LINQ statement to:
var barcodes = (from src in xmldoc.Descendants("Container")
where src.Descendants().Count() > 0
select
new Container
{
Id = (string)src.Element("Id"),
Barcode = (string)src.Element("Barcode"),
Quantity = float.Parse((string)src.Element("Quantity").Attribute("value"))
}).Distinct(new ContainerComparer());
And that's it! Thanks for the help, Glenn!
The reason that the data grid view is in ReadOnly mode when binding to anonymous types is that anonymous types are ReadOnly. You will get the same behaviour if you bind the view to a list of objects with only read only properties.
The only solution that I know of is to create a container for the data that is editable. A class with properties defining a get and set will give you what you are after.
It's probably because of limitation in C# 3 - you can't use anonymous types as return types from methods. See for example this

Categories