Reputation: 8310
Suppose you need to store some values in a table formed by several columns (such as a relational database): for example, int
for the first column, string
for the second one, DateTime
for the third one, etc..
If one or more column are like a primary key, then we could use an IDictionary
, whose Key
would be the set of fields that acts as a primary key. The remaining fields represent the Value
. In this case I could create one or two classes/structures. For example, if the primary key is simply an existing type, we will only have to create a class/struct like the following:
public class MyValue
{
public int Field1;
public double Field2;
public string Field3;
}
The dictionary could be a Dictionary<int, MyValue>
, while the table could be the following.
public class MyTable
{
private Dictionary<int, MyValue> _table;
...
}
Obviously, according to the application domain there may be ranges and/or rules of validity for the above fields: for example, Field1
may have to be positive, and so on...
As a first alternative I could design the MyValue
class so that it throws some exceptions in order to identify the "violation of rules". But perhaps this approach might be excessive, especially if the class MyValue
is used only internally to MyTable
: in this case I would write code like the following and handle validity error within MyTable
class: that is, MyTable
class checks the data fields before inserting them into the dictionary, so MyValue
would be a "stupid" class...
namespace MyNamespace
{
class MyValue
{
// only public fields
}
public class MyTable
{
private Dictionary<int, MyValue> _table;
...
public void Add(int key, int field1, double field2, string field3)
{
// some code to check validity range for fields
...
}
}
}
Is this solution correct? Or should I avoid this approach? Should I always define a class/struct in a complete manner (exceptions handling, Equals
, GetHashCode
methods?
Upvotes: 0
Views: 998
Reputation: 112342
I would let the values check their consistency themselves. If you implement the fields as properties, the setter can do checks and throw an exception is the value is out of range for instance.
I also would suggest a somewhat more flexible approach. Define an interface that your table records must implement
public interface IRecord<PK>
{
PK ID { get; }
}
Records must have an ID, which will be used as primary key. We use a generic primary key type PK
.
Now you can define a value class like this
public class MyValue : IRecord<int>
{
private int _field1;
public int Field1
{
get { return _field1; }
set
{
if (value < 0 || value > 1000) {
throw new ArgumentException("Value of 'Field1' must be between 0 and 1000");
}
_field1 = value;
}
}
public double Field2;
#region IRecord<int> Members
public int ID { get { return Field1; } }
#endregion
}
As you can see, it implements IRecord<int>
and returns Field1
as ID
. Field1
checks the values passed to it. If the primary key is made up of several fields you can use a type like Tuple
(.NET 4.0) as primary key type. Like IRecord<Tuple<int,double>>
.
Now you can define a generic table
public class Table<T, PK>
where T : IRecord<PK>
{
private Dictionary<PK, T> _table = new Dictionary<PK, T>();
public void Add(T item)
{
_table.Add(item.ID, item);
}
}
You can define specific tables like this
public class MyTable : Table<MyValue, int>
{
}
UPDATE:
Alternatively, you could let records implement an explicit error handling
public interface IRecord<PK> {
PK ID { get; }
bool IsValid { get; }
string[] Errors { get; }
}
Upvotes: 2
Reputation: 88064
What you've done looks overly complicated, brittle and will ultimately lead to maintainability nightmares.
I'd say you have two options.
Option one: use a DataTable. It doesn't have to be backed by an actual database and you can define it's layout (including column definitions) at runtime.
Option two: Define a class that implements your "record" logic. Define a Collection to hold that class. Use LINQ to "query" the collection for whatever you need.
The primary difference between the two options is that a DataTable is very limited in the type of rules you can employ. Option 2, OTOH, allows you to build precisely what you want.
Upvotes: 4