Ways around creating an instance of a generic interface in C# - c#

I've coded myself into a corner and would like your help to dig me out again. In the right direction.
So, I've implemented a minor SQLite wrapper where I wanted the solution to be generic (don't we all). Afterwards, I now realize that the usage of these classes and interfaces aren't very intuitive nor generic.
Let's start at the bottom and work upwards. I've created a class called DataRow that works as a base class for my table rows. The class DataRow itself only has a property Id (since all rows need one). This results in the following definition: class DataRow { public int Id { get; set; } }
Using this DataRow class, is each table. And for database tables I've created one generic interface, and one generic base class. The definitions looks like this:
internal interface ITable<T>
where T : DataRow, new()
{
T Select(int id);
List<T> Select(List<int> ids);
int Insert(T t);
void Update(T t);
bool Delete(int id);
}
public class Table<T> : ITable<T>
where T : DataRow, new()
{
// Commented out to protect you from a minor case of serious brain damage.
}
This setup allows me to create concise definitions. In fact, they tend to be quite epic, really. Proud to say.
public class Car : DataRow
{
public decimal ParkingTicketDebt { get; set; }
public DateTime WhenWifeWillAllowReplacement { get; set; }
public bool CanTransformIntoSomethingAwesome { get; set; }
}
public class Cars : Table<Car> {
// Yep, that's all. You can go home now, folks. There's nothing here. Nothing at all. Especially not a great treasure of gold. Whops... I mean... there's really not. Not that I'm aware of, anyway... I mean, there could be. Not that I wouldn't say if I had any information on this great trasure of gold that might exist. But I know nothing of such an item. I really don't, so you can stop thinking about this great treasure of gold. Since I don't know anything about it, the chance that it even exist is extremely low. Miniscule. I mean, you would probably not find anything, not even if you digged for, like, a really long time. Seven years or something. Oookay. Slowly fading away...
}
As you may or may not have noticed, I'm using the class type name of Cars to determine the name of the table in the database. Likewise, I'm performing reflection on Car and use its public property names and types to get/set values in the database. And yes, I'm aware that I'm in the process of coding a stripped down version of Entity Framework. Which sounds both really stupid and quite time consuming.
Anyway, here is a usage example of the class Cars, which I must remind you that I'm proud of:
new Cars().Delete(3497); // Note that I have a great number of (expensive) cars.
Nice, eh? One slight problem. This means that I have to write strongly typed code, specific to the number of tables that exist in the database. And I don't like specific code. I like generic code.
You might start arguing here that I'm overdoing it. Then let me tell you this. You're damn right I'm overkilling! I'm intentionally flamethrowing the dead guy that was ran over by a tank. Seven times.
So I started experimenting a bit and came up with this delicate solution:
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
[WebMethod(EnableSession = true)]
public int CreateEmptyRow(string tableName)
{
var tableType = Type.GetType(tableName);
if (tableType == null)
throw new TypeLoadException("Dumbass. That table doesn't exist");
var instance = Activator.CreateInstance(tableType) as ITable<dynamic>;
if (instance == null)
throw new TypeLoadException("Idiot. That type isn't a table");
return instance.Insert(new DataRow());
}
Note that I can really understand if you have no idea why someone would want to create an empty row.
So what's wrong with this? Well, it doesn't compile, for one. Here's the error: There is no implicit reference conversion from 'dynamic' to 'DataRow'. A search on Google gave few results.
The problem is obviously Activator.CreateInstance(tableType) as ITable<dynamic>. I've tried things like Activator.CreateInstance(tableType) as ITable<Table<DataRow>>, an attempt which gave me this error: The type 'DataRow' must be convertible to 'DataRow'.

So, as ive written in the comment, Im adding an extra non-generic interface:
interface ITable
{
DataRow Select(int id);
IEnumerable<DataRow> Select(List<int> ids);
int Insert(DataRow t);
void Update(DataRow t);
}
interface ITable<T> where T : DataRow, new()
{
T Select(int id);
List<T> Select(List<int> ids);
int Insert(T t);
void Update(T t);
bool Delete(int id);
}
class Table<T> : ITable<T>, ITable where T : DataRow, new()
{
public T Select(int id)
{
return new T();
}
public List<T> Select(List<int> ids)
{
return new List<T>();
}
public int Insert(T t)
{
return 1;
}
public void Update(T t)
{
}
public bool Delete(int id)
{
return true;
}
DataRow ITable.Select(int id)
{
return this.Select(id);
}
IEnumerable<DataRow> ITable.Select(List<int> ids)
{
return this.Select(ids);
}
public int Insert(DataRow t)
{
return this.Insert(t);
}
public void Update(DataRow t)
{
this.Update(t);
}
}
and this is how im implementing the CreateEmptyRow \ Select methods:
public static int CreateEmptyRow(string tableName)
{
var tableType = Type.GetType(tableName);
if (tableType == null)
throw new TypeLoadException("Dumbass. That table doesn't exist");
var instance = Activator.CreateInstance(tableType) as ITable;
if (instance == null)
throw new TypeLoadException("Idiot. That type isn't a table");
return instance.Insert(new DataRow());
}
public static List<DataRow> Select(List<int> ids, string tableName)
{
var tableType = Type.GetType(tableName);
if (tableType == null)
throw new TypeLoadException("Dumbass. That table doesn't exist");
var instance = Activator.CreateInstance(tableType) as ITable;
if (instance == null)
throw new TypeLoadException("Idiot. That type isn't a table");
return instance.Select(ids).ToList();
}
notice that if you want such a generic solution, the select method (for example) can only return an IEnumerable \ List of DataRow, which can be solved by using the provided Cast extension method:
var myList = Select(null, "Cars").Cast<Car>();
Note: as you probably know, to instantiate the Cars class by name, you also need to provide the namespace, which i skipped here, and probably the Table<T> class should be abstract as well.

One problem is you're trying to insert a DataRow into a table which takes some subclass of DataRow, so even if you could compile it, you would still get an exception at runtime.
You need to find the generic row type to insert and insert a new instance of that type:
object instance = Activator.CreateInstance(tableType);
var tableInterface = tableType.GetInterfaces().FirstOrDefault(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(ITable<>));
if(tableInterface == null) throw new ArgumentException("Type is not a table type");
var rowType = tableInterface.GetGenericArguments()[0];
var newRow = Activator.CreateInstance(rowType);
MethodInfo insertMethod = tableInterface.GetMethod("Insert");
return (int)insertMethod.Invoke(instance, new object[] { newRow });
However it seems you could make your CreateEmptyRow method generic in the table and row type and avoid reflection altogether:
public int CreateEmptyRow<TTable, TRow>()
where TRow : DataRow, new()
where TTable : ITable<TRow>, new()
{
var table = new TTable();
return table.Insert(new TRow());
}

Related

Object Oriented Casting issue

I have a casting issue and I was wondering if anyone would be able to help me see how I can fix this issue?
Basically I'm trying to create a CSV generator console program that will take a list of machine records from the DB and put them into a CSV.
The code below is simplified but generally similar to my implementation currently:
public interface IRecord
{
}
public class MachineRecord : IRecord
{
string Name {get;set;}
string ErrorCount {get;set;}
}
public interface IRecordStore
{
string GenerateRecord(IRecord record);
}
public class CSVMachineRecordStore : IRecordStore
{
public string GenerateRecord(IRecord record)
{
var machineRecord = (MachineRecord)record;
var strBuilder = new StringBuilder();
strBuilder.Append(machineRecord.Name);
strBuilder.Append(machineRecord.ErrorCount);
return strBuilder.ToString();
}
}
So basically here I've created a class that inherits from IRecordStore, and in our case we have to create an implementation of GenerateRecord. The problem here is I have to cast the record of be of type MachineRecord or the compiler will throw a casting error out, this is due to Record not having any properties set.
Is it possible to not have the direct cast in this implementation so this line of code will be like:
public class CSVMachineRecordStore : IRecordStore
{
public string GenerateRecord(IRecord record)
{
var machineRecord = record; //we want to remove the explicit cast
var strBuilder = new StringBuilder();
strBuilder.Append(machineRecord.Name);
strBuilder.Append(machineRecord.ErrorCount);
return strBuilder.ToString();
}
}
I know I can fix this issue by adding Name and ErrorCount properties to IRecord, but IRecord is a very generic interface that can have anything.
We want to be able to create a CSV program that can handle machine records, customer records, employee records, supplier records, etc...
I'm thinking of using generics, and I've played with them a bit but I've had some trouble getting it to work too.
Use generics:
public interface IRecordStore<T> where T:IRecord
{
string GenerateRecord(T record);
}
Then implement:
public class CSVMachineRecordStore : IRecordStore<MachineRecord>
{
public string GenerateRecord(MachineRecord record)
{
var machineRecord = record; //we want to remove the explicit cast
var strBuilder = new StringBuilder();
strBuilder.Append(machineRecord.Name);
strBuilder.Append(machineRecord.ErrorCount);
return strBuilder.ToString();
}
}
I wrote this in a hurry, make sure to check for silly errors.
The type casting exception occurs when the IRecord instance is not an instance of MachineRecord.
To answer your question how to fix the casting issue, you should have a look at the as operator.
However, for me this looks like making the IRecordStore interface generic could help you prevent the type issues you're experiencing:
[Edit] I noticed since IRecordStore is an interface you could even make the generic type contravariant. This allows you to assign a variable
of type IRecordStore<IRecord> with an instance of IRecordStore<MachineRecord> or more explicitly CSVMachineRecordStore.
public interface IRecordStore<in T> where T : IRecord
{
string GenerateRecord(T record);
}
public class CSVMachineRecordStore : IRecordStore<MachineRecord>
{
public string GenerateRecord(MachineRecord record)
{
var machineRecord = record;
var strBuilder = new StringBuilder();
strBuilder.Append(machineRecord.Name);
strBuilder.Append(machineRecord.ErrorCount);
return strBuilder.ToString();
}
}

How to properly implement a generic method?

Can someone help me figure out how to implement this method generically? The compiler complains that it cannot resolve t.Id. Which makes sense but, how do I tell it that all objects that pass will have an Id property. Here is the interface I defined for T:
namespace LiveWire.Model
{
public interface ILiveWireModel
{
Guid Id { get; }
}
}
The interface for all repositories:
internal interface ILiveWireRepository<T>
{
ICacheProvider Cache { get; }
string CacheKey { get; }
SqlConnection CreateConnection();
IEnumerable<T> GetData<TD>();
IEnumerable<T> LoadData<TD>();
Dictionary<Guid, T> GetCachedData<TD>();
void ClearCache();
}
And my method:
public IEnumerable<T> GetData<TD>()
where TD : ILiveWireModel
{
var data = GetCachedData<TD>();
if (data == null)
{
data = LoadData<TD>().ToDictionary(t => t.Id);
if (data.Any())
{
Cache.Set(CacheKey, data, 30);
}
}
return data.Values;
}
I'm including the whole class here which I hope will clear some things up.
internal abstract class LiveWireRepositoryBase<T> : ILiveWireRepository<T>
{
public ICacheProvider Cache { get; private set; }
public string CacheKey { get; private set; }
internal LiveWireRepositoryBase()
{
Cache = new DefaultCacheProvider();
}
public SqlConnection CreateConnection()
{
return new SqlConnection(
ConfigurationManager
.ConnectionStrings["LiveWire4Database"]
.ConnectionString);
}
public IEnumerable<T> GetData<TD>()
where TD : ILiveWireModel
{
var data = GetCachedData<TD>();
if (data == null)
{
data = LoadData<TD>().ToDictionary(t => t.Id);
if (data.Any())
{
Cache.Set(CacheKey, data, 30);
}
}
return data.Values;
}
public IEnumerable<T> LoadData<TD>()
{
return new List<T>();
}
public Dictionary<Guid, T> GetCachedData<TD>()
{
throw new NotImplementedException();
}
public void ClearCache()
{
throw new NotImplementedException();
}
}
I'm getting this error which I don't understand. I tried using an explicit interface implementation but, that wound up making me remove my where constraint.
The constraints for type parameter 'TD' of method 'LiveWire.Data.Repositories.LiveWireRepositoryBase.GetData()' must match the constraints for type parameter 'TD' of interface method 'LiveWire.Data.Repositories.ILiveWireRepository.GetData()'. Consider using an explicit interface implementation instead. C:\projects\LiveWire\Solution\LiveWire.Data\Repositories\LiveWireRepositoryBase.cs 32 31 LiveWire.Data
You'll be able to make this method compile by changing the class's signature to
public sealed class MyCache<T> where T : ILiveWireModel
(or, if the class is in a different namespace, where T : LiveWire.Model.ILiveWireModel).
That said, I'm not sure that this change will solve your problem. I have only seen a few snippets of your project's code, so I may be wrong, and take the following with a grain of salt:
Is it really the best design to keep GUID-keyed and integer-keyed values in the same cache? Presumably, you're taking data from two different sources, one which uses GUID keys and one which uses integer keys. But in the future, what if you add a third source, which also uses integer keys? The keys from the two integer-key sources could clash, and your cache would always be wrong for some queries. Personally, I'd maintain a second table or function (maybe keep a table of mappings for integer-valued keys, just pass through the GUID-valued keys) somewhere that knows the mapping from objects to keys, and use that function whenever I need to check if an object is cached. All the rest of the time, then, your cache could work directly in terms of keys and values, and not have to mess with different types of keys.
The exception you get at this stage just says that the interface definition
IEnumerable<T> GetData<TD>();
hasn't got the same constraints (i.e. the where) for the type parameter TD as the implementation
public IEnumerable<T> GetData<TD>() where TD : ILiveWireModel
You need to put the same constraint in the interface.
public IEnumerable<T> GetData<T>() where T:LiveWire.Model.ILiveWireModel {
//.../
}
Specialization of generics.
You need to fix the declaration first
public IEnumerable<T> GetData<T>()
then, in order to know what you can use ON T, you have to tell it what T is allowed to be.
public IEnumerable<T> GetData<T>() where T : ILiveWireModel
Finally, you haven't told us what var data actually contains, that would be inside of the GetCachedData and the LoadData Functions, which you dont pass T into and we dont have any idea what it returns.
I would expect to see something like this though
public IEnumerable<T> GetData<T>() where T : ILiveWireModel
{
var data = GetCachedData<T>();
if (data == null)
{
data = LoadData<T>().ToDictionary(t => t.Id);
if (data.Any())
{
Cache.Set(CacheKey, data, 30);
}
}
return data.Values;
}

Extracting instances from a list based on a given Type variable

I fear this is going to be a big setup for a simple question. As to complexity of the answer, I fear what I might be getting into...
I am building an application that will be used to help transform data from a source database with one table structure to a target database with a different structure. The target database will contain data already, and thus the process must be able to maintain ID-based relationships from the source when inserting to the target, where the newly-inserted items will get new IDs. Assume that each source table will be transformable to a single target table.
Minimal code, with necessary class/interface structure:
public interface IDataSetStorable { }
public class InMemoryDataSet : List<IDataSetStorable>
{
public AbstractDataEntity FindEntity(string id, Type type)
{
// The question will be about this method
return new object() as AbstractDataEntity;
}
}
public class EntityList<T> : Dictionary<string, T>, IDataSetStorable where T : AbstractDataEntity
{
public void AddEntity(T entity)
{
this.Add(entity.ID, entity);
}
}
public abstract class AbstractDataEntity
{
public string ID { get; set; }
}
public abstract class DataItem<S, T> : AbstractDataEntity { }
// There will be a set of these three classes per source DB table
public class SourceType { }
public class TargetType { }
public class TransformationType : DataItem<SourceType, TargetType> { }
InMemoryDataSet holds the tables, represented by instances of (for example) EntityList<TransformationType>. There will be a TransformationType for each mapping of SourceType to TargetType, where each of those is likely to be a class from a DataContext. There will be one per source DB table, though many of those tables may map to a single target DB table.
The use of IDataSetStorable as a marker interface allows for the storage of EntityList<>s with many different subtypes within an instance of InMemoryDataSet.
During the transformation of any item from the source DB, it can only be inserted into the target DB if we know the appropriate target-DB IDs for its foreign keys. To do this the code will find all its dependencies from the source DB and transform them BEFORE attempting to transform the item under consideration. Recursively, this should ensure that the first things inserted into the target DB have no dependencies, get their new IDs, and can then be looked up when inserting things that depend on them.
An instance of InMemoryDataSet will provide the lookup facility, which should be passed an ID (from the source DB) and a parameter of type Type, representing the TransformationType which deals with transforming the type of item being looked up.
Example of that: Table1 has two fields, id and table2_id, the latter referencing Table2, and its field id. The lookup call would be (kinda pseudocode-y):
var entity = myDataSet.FindEntity([table1.table2_id], typeof(Table2TransformationType));
Then entity should be of type Table2TransformationType (inheriting eventually from AbstractDataEntity), and would represent the row from Table2 with ID matching that passed to the method.
And finally, to the question:
In the FindEntity() method, how can I find if there is an EntityList<whatever the passed type was> present? My thought was to use something like:
foreach (var entityList in this)
{
// work out if entityList is an EntityList<passed-in type>;
}
Simple question! But I don't know how I can do this last part :(
You need to check:
If the Type for the current item entityList represents a generic type
If that generic type represents EntityList<>
If the generic argument to that type is of the passed in type
Try this:
if (entityList.GetType().IsGenericType &&
entityList.GetType().GetGenericTypeDefinition() == typeof(EntityList<>) &&
entityList.GetType().GetGenericArguments()[0] == type)
{
...
}
Edit: Was getting the generic arguments off of the wrong type. Fixed.
OK, managed to make this work using a bit of Reflection. Kirk Woll got me started looking in the right places, though in the end the solution hasn't used his suggestions. There is an additional method, public T RetrieveEntity(string id), in the EntityList<T> class in order to make it easier to get a single item out of the Dictionary by key when using Type.GetMethod():
public class EntityList<T> : Dictionary<string, T>, IDataSetStorable where T : AbstractDataEntity
{
public void AddEntity(T entity)
{
this.Add(entity.ID, entity);
}
public T RetrieveEntity(string id)
{
return this[id];
}
}
Then we have the guts of the FindEntity(string id, Type type) method:
public class InMemoryDataSet : List<IDataSetStorable>
{
public AbstractDataEntity FindEntity(string id, Type type)
{
// Make an instance of the passed-in type so that invoking
// TryGetValue will throw an exception if operating on an
// EntityList which is not of the correct type.
var sample = type.GetConstructor(new Type[]{}).Invoke(new object[]{});
foreach (var entityList in this)
{
try
{
// This doesn't manage to set sample to the found entity...
bool idFound = (bool)entityList.GetType().GetMethod("TryGetValue").Invoke(entityList, new object[] { id, sample });
if (idFound)
{
// So we dig it out here with the method added to EntityList<>
sample = entityList.GetType().GetMethod("RetrieveEntity").Invoke(entityList, new object[] { id });
return (AbstractDataEntity)sample;
}
}
catch (Exception ex)
{
// Likely some kind of casting exception
}
}
return null;
}
}
At the point of calling FindEntity() we knew what the desired type was, and so casting the AbstractDataEntity that it returns is trivial.
Use Linq:
Dictionary<string, Type> a = new Dictionary<string, Type>();
var allOfMyType = a.Where(x=> (x.Value.Name == "MyType"));

How to Work Around Limitations in Generic Type Constraints in C#?

Okay I'm looking for some input, I'm pretty sure this is not currently supported in .NET 3.5 but here goes.
I want to require a generic type passed into my class to have a constructor like this:
new(IDictionary<string,object>)
so the class would look like this
public MyClass<T> where T : new(IDictionary<string,object>)
{
T CreateObject(IDictionary<string,object> values)
{
return new T(values);
}
}
But the compiler doesn't support this, it doesn't really know what I'm asking.
Some of you might ask, why do you want to do this? Well I'm working on a pet project of an ORM so I get values from the DB and then create the object and load the values.
I thought it would be cleaner to allow the object just create itself with the values I give it. As far as I can tell I have two options:
1) Use reflection(which I'm trying to avoid) to grab the PropertyInfo[] array and then use that to load the values.
2) require T to support an interface like so:
public interface ILoadValues
{
void LoadValues(IDictionary values);
}
and then do this
public MyClass<T> where T:new(),ILoadValues
{
T CreateObject(IDictionary<string,object> values)
{
T obj = new T();
obj.LoadValues(values);
return obj;
}
}
The problem I have with the interface I guess is philosophical, I don't really want to expose a public method for people to load the values. Using the constructor the idea was that if I had an object like this
namespace DataSource.Data
{
public class User
{
protected internal User(IDictionary<string,object> values)
{
//Initialize
}
}
}
As long as the MyClass<T> was in the same assembly the constructor would be available. I personally think that the Type constraint in my opinion should ask (Do I have access to this constructor? I do, great!)
Anyways any input is welcome.
As stakx has said, you can't do this with a generic constraint. A workaround I've used in the past is to have the generic class constructor take a factory method that it can use to construct the T:
public class MyClass<T>
{
public delegate T Factory(IDictionary<string, object> values);
private readonly Factory _factory;
public MyClass(Factory factory)
{
_factory = factory;
}
public T CreateObject(IDictionary<string, object> values)
{
return _factory(values);
}
}
Used as follows:
MyClass<Bob> instance = new MyClass<Bob>(dict => new Bob(dict));
Bob bob = instance.CreateObject(someDictionary);
This gives you compile time type safety, at the expense of a slightly more convoluted construction pattern, and the possibility that someone could pass you a delegate which doesn't actually create a new object (which may or may not be a major issue depending on how strict you want the semantics of CreateObject to be).
If you can create common base class for all of T ojects that you are going to pass to MyClass as type parameters than you can do following:
internal interface ILoadValues
{
void LoadValues<TKey, TValue>(IDictionary<TKey, TValue> values);
}
public class Base : ILoadValues
{
void ILoadValues.LoadValues<TKey, TValue>(IDictionary<TKey, TValue> values)
{
// Load values.
}
}
public class MyClass<T>
where T : Base, new()
{
public T CreateObject(IDictionary<string,object> values)
{
ILoadValues obj = new T();
obj.LoadValues(values);
return (T)obj;
}
}
If you cannot have common base class than I think you should go with solution proposed by itowlson.
I'm legitimately curious at how you would load the values of a class without using reflection unless you had methods hardcoded to accomplish it. I'm sure there's another answer, but I'm not too ashamed to say I do not have experience in it. As for something I wrote to auto-load data, I have two base data classes I work from: a single object and then a list. In the single object (BaseDataClass), I have this method.
public virtual void InitializeClass(DataRow dr)
{
Type type = this.GetType();
PropertyInfo[] propInfos = type.GetProperties();
for (int i = 0; i < dr.ItemArray.GetLength(0); i++)
{
if (dr[i].GetType() != typeof(DBNull))
{
string field = dr.Table.Columns[i].ColumnName;
foreach (PropertyInfo propInfo in propInfos)
{
if (field.ToLower() == propInfo.Name.ToLower())
{
// get data value, set property, break
object o = dr[i];
propInfo.SetValue(this, o, null);
break;
}
}
}
}
}
And then in the data list
public abstract class GenericDataList<T> : List<T> where T : BaseDataClass
{
protected void InitializeList(string sql)
{
DataHandler dh = new DataHandler(); // my general database class
DataTable dt = dh.RetrieveData(sql);
if (dt != null)
{
this.InitializeList(dt);
dt.Dispose();
}
dt = null;
dh = null;
}
protected void InitializeList(DataTable dt)
{
if (dt != null)
{
Type type = typeof(T);
MethodInfo methodInfo = type.GetMethod("InitializeClass");
foreach (DataRow dr in dt.Rows)
{
T t = Activator.CreateInstance<T>();
if (methodInfo != null)
{
object[] paramArray = new object[1];
paramArray[0] = dr;
methodInfo.Invoke(t, paramArray);
}
this.Add(t);
}
}
}
}
I'm open to criticism, because no one has ever reviewed this code before. I am the sole programmer where I work, so I do not have others to bounce ideas off of. Thankfully, now I've come across this website!
Edit: You know what? Looking at it now, I don't see why I shouldn't just rewrite that last method as
protected void InitializeList(DataTable dt)
{
if (dt != null)
{
Type type = typeof(T);
foreach (DataRow dr in dt.Rows)
{
T t = Activator.CreateInstance<T>();
(t as BaseDataClass).InitializeClass(dr);
this.Add(t);
}
}
}
I assume that works, although I haven't tested it. No need to use reflection on that part.

Convert DataRow to object

I've created generic List and populate with some objects. Then List I mentioned before converted into DataTable to use in DataGridView. Problem is when I want get Row from this grid I have DataRow. I wanted to convert this to my object againt but not sure how to do it. Maybe you could give some example?
Thanks
Well, if you can't or won't use an "ORM" (object-relational mapper, like Linq-to-SQL or NHibernate - that's exactly what these tools do, and do quite well for you), you'll have to do this yourself.
Converting a DataRow into a domain object model is pretty boring code, really:
public Customer ConvertRowToCustomer(DataRow row)
{
Customer result = new Customer();
result.ID = row.Field<int>("ID");
result.Name = row.Field<string>("CustomerName");
..... // and so on
return result;
}
The biggest challenge here is making this rock-solid and handling (or avoiding) all possible errors (like a field being NULL etc.).
Another possibility would be to have a constructor on your domain model object type that would take a DataRow as parameter and construct a new object from it.
Marc
Assuming you're using a class MyObject, defined as follows :
class MyObject
{
public string Foo { get; set; }
public int Foo { get; set; }
}
You could do something like that :
using System.Data.DataSetExtensions;
...
List<MyObject> list = (from row in table.AsEnumerable()
select new MyObject
{
Foo = row.Field<string>("foo"),
Bar = row.Field<int>("bar")
}).ToList();
Why not just put your objects into a BindingList<> rather than a List<>? Then you can skip the converting to DataTable and back again exercise. You may need to implement INotifyPropertyChanged on your objects, but once they are inside a BindingList, changes in the datagrid will automatically be applied to your underlying objects.
Sorting can be handled by either sorting the list manually on column header click, or by inheriting from BindingList<> and implementing the sorting functionality inside it - then clicking on a header automatically sorts the list - no code required.
Well nowadays it is easier using ORMs of course. But if still you're using the old fashion you can go with a pretty easy Extension Class to do the job for you using a little bit of reflection and generic methods and lambda as follows:
public static class MapperExtensionClass
{
public static IEnumerable<MyClassType> ToMyClassTypeEnumerable(this DataTable table)
{
return table.AsEnumerable().Select(r => r.ToMyClassType());
}
public static MyClassType ToMyClassType(this DataRow row)
{
return row.ToObject<MyClassType>();
}
public static T ToObject<T>(this DataRow row) where T: new()
{
T obj = new T();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
if (row.Table.Columns.Contains(property.Name))
{
property.SetValue(obj, property.PropertyType.ToDefault(row[property.Name]));
}
}
return obj;
}
public static object ToDefault(this Type type, object obj)
{
if (type == null)
throw new Exception("Customized exception message");
var method = typeof(MapperExtensionClass)
.GetMethod("ToDefaultGeneric", BindingFlags.Static | BindingFlags.Public);
var generic = method.MakeGenericMethod(type);
return generic.Invoke(null, new object[] { obj });
}
public static T ToDefaultGeneric<T>(object obj)
{
if (obj == null || obj == DBNull.Value)
{
return default(T);
}
else
{
return (T)obj;
}
}
}
You should also remember GridView objects can bind a lot of data source types. So it is your decision from a design point about what you should go with.

Categories

Resources