C# convert list to Generic List - c#

I am using vs2022 and C# language and oracle as backend. oracle procedure gets input as facility type and based on type it returns the list of data and each facility type have common columns with few additional columns per type. on the below code, MaptoList is an extension class method which maps the reader column and value to class. the below logic is not working and says cannot convert from non generic list to generic. please help me to solve the compile error.
public class Common
{
public string Name { get; set; }
public string Address { get; set; }
public string State{ get; set; }
}
public class Employee : Common
{
public string SSN{ get; set; }
}
public class Vendor : Common
{
public string TaxNumber{ get; set; }
}
public async Task<List<T>> GetFacility(string Facility_Type)
{
using (EMPDB dbConnect = new EMPDB())
{
DbCommand dbCommand = dbConnect.Database.GetDbConnection().CreateCommand();
dbCommand.CommandType = CommandType.StoredProcedure;
dbCommand.CommandText = "GetFacilityReport";
dbCommand.BuildSqlParameter("Facility_Type", OracleDbType.Varchar2, Facility_Type, ParameterDirection.Input);
List<T> lstFacility_Response= new List<T>();
if (dbCommand.Connection.State != ConnectionState.Open)
{
dbCommand.Connection.Open();
}
using (var reader = await dbCommand.ExecuteReaderAsync())
{
switch (Facility_Type)
{
case "Employee":
lstFacility_Response= reader.MapToList<Employee>();
break;
case "Vendor":
lstFacility_Response= reader.MapToList<Vendor>();
break;
}
}
}
return lstFacility_Response
}
public static List<T> MapToList<T>(this DbDataReader dr) where T : new()
{
List<T> RetVal = null;
var Entity = typeof(T);
var PropDict = new Dictionary<string, PropertyInfo>();
try
{
if (dr != null && dr.HasRows)
{
RetVal = new List<T>();
var Props = Entity.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropDict = Props.ToDictionary(p => p.Name.ToUpper(), p => p);
while (dr.Read())
{
T newObject = new T();
for (int Index = 0; Index < dr.FieldCount; Index++)
{
if (PropDict.ContainsKey(dr.GetName(Index).ToUpper()))
{
var Info = PropDict[dr.GetName(Index).ToUpper()];
if ((Info != null) && Info.CanWrite)
{
var Val = dr.GetValue(Index);
Info.SetValue(newObject, (Val == DBNull.Value) ? null : Val, null);
}
}
}
RetVal.Add(newObject);
}
}
}
catch (Exception)
{
throw;
}
return RetVal;
}

If generics are appropriate here then this:
public async Task<List<T>> GetFacility(string Facility_Type)
should be this:
public async Task<List<T>> GetFacility<T>()
and then your switch statement should be replaced with this:
lstFacility_Response = reader.MapToList<T>();
You would then call it something like so:
var list = await GetFacility<Employee>();
When declaring the method, the method itself has to be generic and then you should be able to simply use the generic type parameter as a type in the method. If you can't do that then the method almost certainly shouldn't be generic in the first place.
If you want to limit T to be only type Common or its derived types then you can add a constraint:
public async Task<List<T>> GetFacility<T>() where T : Common
Given that your MapToList method has the new constraint, this method would have to as well, in order to call MapToList<T>():
public async Task<List<T>> GetFacility<T>() where T : Common, new()

Related

How to get concrete type in generic method

I have a method that returns a List of objects that implement an interface:
private List<IFoo> GetData(string key)
{
...returns a different concrete implementation depending on the key
switch (key)
{
case "Bar":
return new List<Bar>();//Bar:IFoo
break;
case "Foo":
return new List<Foo>();//Foo:IFoo
break;
case "FooBar":
return new List<FooBar>();//FooBar:IFoo
break;
//etc etc - (quite a lot of these)
}
}
And I want to convert the result to a DataTable:
var result = GetData("foobar");
return ConvertToDataTable(result)
and my implementation of ConvertToDataTable looks something like this:
private DataTable ConvertToDataTable<T>(IEnumerable<T> data)
{
//problem is typeof(T) is always IFoo - not FooBar
PropertyInfo[] properties = typeof(T).GetProperties();
DataTable table = new DataTable();
foreach (var prop in properties)
{
table.Columns.Add(prop.DisplayName, prop.PropertyType);
}
//etc..
}
How can I get the underlying type in the generic ConvertToDataTable method?
Replace typeof which is evaluated at compileTime by .GetType which is evaluated at runtime and you will get the coorect type, not the interface:
private DataTable ConvertToDataTable<T>(IEnumerable<T> data)
{
Type dataType;
if (data != null && data.Count() != 0)
{
//problem is typeof(T) is always IFoo - not FooBar
//typeof(T) will always return IFoo
//Will return the correct type
dataType = data.First().GetType();
}
else
{
return new DataTable();
//or throw ?
}
PropertyInfo[] properties = dataType.GetProperties();
DataTable table = new DataTable();
foreach (var prop in properties)
{
table.Columns.Add(prop.DisplayName, prop.PropertyType);
}
//etc..
}
GetType() is what gets you the concrete class at runtime. The answer you accepted is a good solution for the question you asked.
Now, from the point of view of what you're trying to accomplish, I wanted to offer that creating your DataTable doesn't really require that RTTI. Here's an implementation of your ConvertToDataTable method that "doesn't care" what T is, as long as it implements IFoo.
private static DataTable ConvertToDataTable<T>(IEnumerable<T> data)
{
// Reflect the properties from T which is IFoo
PropertyInfo[] properties = typeof(T).GetProperties();
DataTable table = new DataTable();
// Add columns
foreach (var prop in properties)
{
table.Columns.Add(
prop.Name,
prop.PropertyType
).DataType = prop.PropertyType;
}
Console.WriteLine("Inside the generic method: ");
// Add rows
foreach (var item in data)
{
// RE: For "the question you asked": Use GetType() for object info.
Console.WriteLine("...the concrete Type is " + item.GetType().Name);
// I would ask, though, do you really need it for anything here?
// But for "the thing you're trying to accomplish" (making a DataTable)
// - This goes by the public properties declared in the interface IFoo.
// - It pulls properties GENERICALLY for ANY class that implements IFoo.
object[] values =
properties.Select(property => property.GetValue(item)).ToArray();
table.Rows.Add(values);
}
return table;
}
It picks up whatever is declared in the IFoo interface:
internal interface IFoo
{
int ID { get; }
string Name { get; }
string Text { get; set; }
}
It works to pass in IEnumerable containing completely different classes because they both implement IFoo:
class FooA : IFoo
{
public int ID { get; } = 1;
public string Name { get; } = "I am Foo A";
public string Text { get; set; }
}
class FooB : IFoo
{
public int ID { get; } = 2;
public string Name { get; } = "I am Foo B";
public string Text { get; set; }
}
Console Output:
Inside the generic method:
...the concrete Type is FooA
...the concrete Type is FooB
D I S P L A Y P O P U L A T E D T A B L E
ID Name Text
1 I am Foo A
2 I am Foo B
You can download from our GitHub if you want to try it out.
using System;
using System.Collections.Generic;
using System.Data;
namespace StackOverflow001
{
class Program
{
static void Main(string[] args)
{
var data = GetData("Foo");
var table = ConvertToDataTable(data);
data = GetData("Bar");
table = ConvertToDataTable(data);
data = GetData("FooBar");
table = ConvertToDataTable(data);
}
static IEnumerable<FooBase> GetData(string key) =>
key switch
{
"Foo" => new List<Foo>(),
"Bar" => new List<Bar>(),
"FooBar" => new List<FooBar>(),
_ => throw new ArgumentException(nameof(key)),
};
static DataTable ConvertToDataTable(IEnumerable<FooBase> data)
{
var properties = data switch
{
List<Foo> _ => typeof(Foo).GetProperties(),
List<Bar> _ => typeof(Bar).GetProperties(),
List<FooBar> _ => typeof(FooBar).GetProperties(),
_ => throw new ArgumentException(nameof(data)),
};
DataTable table = new DataTable();
foreach (var prop in properties)
{
table.Columns.Add(prop.Name, prop.PropertyType);
}
return table;
}
}
interface IFoo {}
abstract class FooBase : IFoo { }
class Foo : FooBase { public int FooProp { get; set; } }
class Bar : FooBase { public int BarProp { get; set; } }
class FooBar : FooBase { public int FooBarProp { get; set; }}
}
I think that using interface and generic methods is a bad idea in this situation. Using inheritance can make your code much easier and cleaner.

Implementing an SQL wrapper with entity mapping

I was trying to implement a wrapper in C# for SQL Server.
The normal workflow without wrapper is fetching the data into a datatable using direct SQL query and then mapping the columns by names into entities.
But as a wrapper is better to accept a mapping function which describes which column maps to which fields of an enumerable.
So, something like this :
public class UserInfo
{
public string FirstName{ get; set; }
public string LastName{ get; set; }
}
enumerableList = dbManager.Execute("** sql query **", /* some method to specify mapping */);
The enumerable will then contain the result from the database, mapped by the execute method. But I am unsure how to specify the mapping?
Even if I do then how to deal with the different data types for each column in the mapping?
If I correct understand, you want something like this:
public static List<T> ReadRows<T>(this SqlHelper sql, string query, SqlParameter[]
parameters, Func<SqlDataReader, T> projection)
{
var command = GetSqlCommand(query, CommandType.StoredProcedure, parameters);
return sql.ExecuteReader(command, reader => reader.Select(projection).ToList());
}
And use like:
var members = _unitOfWork.SqlHelper.ReadRows("spGetMembersByUserCompanies", parameters, _memberProjection);
readonly Func<SqlDataReader, MemberVm> _memberProjection = (r) => new MemberVm
{
InvitationId = r.Get<int?>("InvitationId"),
UserName = r.Get<string>("UserName"),
RoleName = r.Get<string>("RoleName"),
InvitationStatus = (InvitationStatus)r.Get<int>("InvitationStatus"),
LogoUrl = r.Get<string>("LogoUrl")
};
It is a piece of my code. I hope it is start to resolve your problem.
Implementing such a wrapper from bare bones is not that easy. But it is possible. There is an ADO wrapper library in Github : ADOWrapper
The implementation is pretty straightforward.
Short Answer
How to specify mapping between columns? - Use Func
How to deal with the different data types? You can write an extension
Long Answer
Make a generic method that takes as input the query and a Func Delegate (and optional third parameter to pass query parameters as dictionary)
public ICollection<T> Execute<T>(string query, Func<IDataReader, T> map, IDictionary<string, object> parameters = null)
{
ICollection<T> collection = new List<T>();
using (SqlConnection connection = CreateConnection())
{
connection.Open();
using (SqlCommand command = CreateCommand(connection, query, parameters))
{
using (IDataReader reader = await command.ExecuteReader())
{
while(reader.Read())
{
collection.Add(map.Invoke(reader));
}
}
}
connection.Close();
}
return collection;
}
Implementation of AddParameter AND CreateCommand:
private void AddParameter(IDbCommand command, string parameter, object value)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameter;
param.Value = value;
command.Parameters.Add(param);
}
private SqlCommand CreateCommand(SqlConnection connection, string query,
IDictionary<string, object> parameters = null)
{
SqlCommand command = connection.CreateCommand();
command.CommandText = query;
if(parameters != null && parameters.Count > 0)
{
foreach(KeyValuePair<string, object> parameter in parameters)
{
AddParameter(command, parameter.Key, parameter.Value);
}
}
return command;
}
You can call the method like this :
public class UserInfo
{
public string FirstName{ get; set; }
public string LastName{ get; set; }
}
var enumerableList = manager.Execute("** query **",
(reader) =>
{
return new UserInfo()
{
FirstName = reader.Get<string>("FirstName"),
LastName = reader.Get<string>("LastName "),
};
})
The Get method makes it easy to manage different data types being fetched from column. But it is not an inbuilt method. So you need to write an extension for Data Reader:
public static class DataReaderExtension
{
public static T Get<T>(this IDataReader reader, string column) where T : IComparable
{
try
{
int index = reader.GetOrdinal(column);
if (!reader.IsDBNull(index))
{
return (T)reader[index];
}
}
catch (IndexOutOfRangeException) { throw new Exception($"Column, '{column}' not found."); }
return default(T);
}
public static IEnumerable<string> GetColumns(this IDataReader reader)
{
IEnumerable<string> columns = new List<string>();
if (reader != null && reader.FieldCount > 0)
{
columns = Enumerable.Range(0, reader.FieldCount)
.Select(index => reader.GetName(index))
.ToList();
}
return columns;
}
}

Generic function to get data from SqlDataReader

I'm creating a generic application to get all the data from different SQL Server tables.
I have a generic function that convert the SqlDataReader to a list:
public static List<T> MapToList<T>(this SqlDataReader dr) where T : new()
{
List<T> RetVal = null;
var Entity = typeof(T);
var PropDict = new Dictionary<string, PropertyInfo>();
try
{
if (dr != null && dr.HasRows)
{
RetVal = new List<T>();
var Props = Entity.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropDict = Props.ToDictionary(p => p.Name.ToUpper(), p => p);
while (dr.Read())
{
T newObject = new T();
for (int Index = 0; Index < dr.FieldCount; Index++)
{
if (PropDict.ContainsKey(dr.GetName(Index).ToUpper()))
{
var Info = PropDict[dr.GetName(Index).ToUpper()];
if ((Info != null) && Info.CanWrite)
{
var Val = dr.GetValue(Index);
Info.SetValue(newObject, (Val == DBNull.Value) ? null : Val, null);
}
}
}
RetVal.Add(newObject);
}
}
}
catch (Exception)
{
throw;
}
return RetVal;
}
Now suppose I have this class for my data:
public partial class User
{
public int Id { get; set; }
public string Name { get; set; }
}
I can fetch my data from the table something like this:
const string GetAreasQuery = "select id, name from dbo.user";
SqlDataReader dr = DoQueryToDB(GetAreasQuery);
List<User> userList = dr.MapToList<User>();
Now, I have n different classes like User (Classroom and so on) and I don't want to write the code above for each class I have. I would like to create a generic GetData to retrieve those information:
public List<T> GetData<T> (string Query_)
{
SqlDataReader dr = DataReader(Query_);
List<T> data = new List<T>();
data = dr.MapToList<T>();
return data;
}
where T can be User, Classroom and so on...
I tried this solution but I always have to specify the type:
public object GetData(string Query_, Type type)
{
SqlDataReader dr = DataReader(Query_);
if (type == typeof(User))
{
List<User> data = new List<User>();
data = dr.MapToList<User>();
return data;
}
else if (..)
{}
return null;
}
I'm trying different possibilities but I always obtain an error in GetData<T> function. More precisely in MapToList<T> like: T should be a non abstract type or public constructor without parameters.
You should add a contraint to the method GetData in order to achieve the same constraint level that is found in MapToList, which requires T to have a empty constructor
public List<T> GetData<T>(string Query_) where T : new()
{
SqlDataReader dr = DataReader(Query_);
return dr.MapToList<T>();
}

How to instantiate and set properties of dynamic type using System.Linq.Expressions

I'm writing an SQL Data Mapper that would map queries to dynamic types (I've already experienced mapping sql to strongly typed objects through reflection)
This time I want to make a sql call then return an Ienumerable.
This is what I have so far:
public class DynamicMapper
{
private class ReaderColumnMap
{
public string Column { get; }
public Type Type { get; }
public ReaderColumnMap(string column, Type type)
{
Column = column;
Type = type;
}
}
private Func<IDataReader, dynamic> MappingMethod;
public DynamicMapper(IDataReader schemaSource)
{
MappingMethod = CreateMapper(schemaSource);
}
private Func<IDataReader, dynamic> CreateMapper(IDataReader reader)
{
IDictionary<int, ReaderColumnMap> propertyMappingsCache = new Dictionary<int, ReaderColumnMap>();
//The schema of sql table involves the column name and data type for each columns.
DataTable schema = reader.GetSchemaTable();
DataColumnCollection dataColumns = schema.Columns;
//Gets the column name ordinal and data type ordinal
int nameColumn = dataColumns["ColumnName"].Ordinal;
int typeColumn = dataColumns["DataType"].Ordinal;
int ordinalColumn = dataColumns["ColumnOrdinal"].Ordinal;
DataRowCollection schemaRows = schema.Rows;
int schemaRowCount = schemaRows.Count - 1;
//Populates the property mappings
for (int i = schemaRowCount; i != -1; --i)
{
DataRow row = schemaRows[i];
string name = row[nameColumn].ToString();
Type type = Type.GetType(row[typeColumn].ToString());
int ordinal = Convert.ToInt32(row[ordinalColumn]);
propertyMappingsCache.Add(ordinal, new ReaderColumnMap(name, type));
}
//Use expression tree here to create dynamic object (new() { Prop1 = "prop1", Prop2 = prop2... })
//Dictionary sample:
//Id = System.Int64
//Name = System.String
//Birthdate = System.DateTime
//Target Expression:
//dynamic instance = new (){
// Id = (System.Int64)reader[0];
// Name = (System.String)reader[1];
// Birthdate = (System.DateTime)reader[2];
//}
var expressions = new List<Expression>();
var fxParameterExpression = Expression.Parameter(typeof(IDataReader), "reader");
//Compiler error: the typeof operator cannot be used on the dynamic type
var variableExpression = Expression.Variable(typeof(dynamic), "instance"); //dynamic instance = new ()
//Compiler error: the typeof operator cannot be used on the dynamic type
var instantiationExpression = Expression.New(typeof(dynamic));
var assignExpression = Expression.Assign(variableExpression, instantiationExpression);
var indexer = typeof(IDataReader).GetProperty("Item", new[] { typeof(int) }); // []
expressions.Add(assignExpression);
int propertyCount = propertyMappingsCache.Count - 1;
for(int i = propertyCount; i != -1; --i)
{
//Get property details from mappingsCache (string property name and Type of property)
//to create property
}
return null;
}
public dynamic CreateMappedInstance(IDataReader reader)
{
return MappingMethod.Invoke(reader);
}
}
But the compiler is not letting me to instantiate dynamic type: the typeof operator cannot be used on the dynamic type
I plan to call it in sql module like:
public class SqlCaller
{
private readonly ISqlProvider sqlProvider;
public SqlCaller(ISqlProvider sqlProvider)
{
this.sqlProvider = sqlProvider ?? throw new ArgumentNullException("sqlProvider");
}
public DataTable Query(string queryString)...
public DataTable Query(DbCommand command)...
public DataTable GetSchema(string queryString)...
public int ExecuteNonQuery(DbCommand command)...
public int ExecuteNonQuery(string commandString)...
public object ExecuteScalar(string queryString)...
public object ExecuteScalar(DbCommand command)...
public bool ExecuteTransaction(IEnumerable<DbCommand> commands)...
[Obsolete]
public IEnumerable<T> Get<T>(Func<IDataReader, List<T>> mappingMethod, string query)...
[Obsolete]
public IEnumerable<T> Get<T>(Func<IDataReader, List<T>> mappingMethod, DbCommand command)...
public IEnumerable<T> Get<T>(string query) where T : class, new()...
public IEnumerable<T> Get<T>(DbCommand command) where T : class, new()...
public IEnumerable<T> Get<T>(IDataMapper<T> dataMapper, DbCommand command) where T : class, new()...
public IEnumerable<dynamic> GetDynamic(string commandString)
{
return GetDynamic(sqlProvider.CreateCommand(commandString));
}
public IEnumerable<dynamic> GetDynamic(DbCommand command)
{
List<dynamic> temp = new List<dynamic>();
using (DbConnection connection = sqlProvider.CreateConnection())
{
command.Connection = connection;
try
{
command.Connection.Open();
IDataReader reader = command.ExecuteReader();
var watch = Stopwatch.StartNew();
DynamicMapper mapper = new DynamicMapper(reader);
while(reader.Read()) temp.Add(mapper.CreateMappedInstance(reader));
watch.Stop();
}
catch
{
throw;
}
finally
{
command.Connection.Close();
}
}
return temp;
}
}
How to instantiate a dynamic type inside an expression tree
How to set property values of a dynamic type inside an expression tree

C# - How to pass generic type with type of "this" class

I have a User class that has a GetQueryable method. Another method, Select(), calls GetQueryable(). I want to use the Select method without passing the type User to the Select method, because I have it in this but I can't use it.
Type type = this.GetType();
???
var x = this.GetQueryable< ??? >().ToList();
class Program
{
static void Main(string[] args)
{
var acc = new User();
acc.Select();
}
}
public partial class User
{
public DB_Test001Entities context;
public User()
{
context = new DB_Test001Entities();
}
public void Select()
{
Type type = this.GetType();
var x = this.GetQueryable< **???** >().ToList();
}
public IQueryable<TEntity> GetQueryable<TEntity>(List<string> includes = null) where TEntity : class
{
IQueryable<TEntity> items = context.Set<TEntity>();
if (includes != null && includes.Any())
includes.Where(i => i != null).ToList().ForEach(i => { items = items.Include(i); });
return items;
}
}
You can do it using reflection. The following sample works smoothly. In program you can use Clerk or Manager, just any instance derived from User to call Select. You can improve your program with this.
class Clerk : User { }
class Manager : User { }
internal class User
{
public User() { }
public string Name { get; set; }
public void Select()
{
var list = new List<string>() {"Jack", "Martin"};
Type thisType = GetType();
MethodInfo method = thisType.GetMethod("GetQueryable").MakeGenericMethod(thisType);
method.Invoke(this, new object[] {list});
}
public IQueryable<TEntity> GetQueryable<TEntity>(List<string> includes = null) where TEntity : User, new()
{
if(includes != null)
{
Console.WriteLine(typeof(TEntity));
var entity = new List<TEntity>(includes.Count);
entity.AddRange(includes.Select(item => new TEntity {Name = item}));
return entity.AsQueryable();
}
return null;
}
}
class Program
{
static void Main()
{
User usr = new Manager();
usr.Select();
}
}

Categories

Resources