Using Reflection to create extensible charts for a website - c#

I’m trying to implement a website where new charts can be added by just dropping a DLL into folder. At the time of writing it is not clear what charts are going to be needed and this provides a simple way of deploying new charts without having to redeploy the website in its entirety. I’m using Google Charts to provide the Google functionality and each chart will be displayed
Each type of chart inherits from the following Interface
public enum ChartType
{
BAR,
COLUMN,
PIE,
TABLE
}
public class DataColumn
{
public String ColumnType { get; set; }
public String ColumnValue { get; set; }
}
public interface IChart
{
/// <summary>
/// Dictionary of Columns, Each column is defined as a type and title
/// </summary>
List<DataColumn> Columns { get; set; }
/// <summary>
/// ChartType, What type of Chart; possible values BAR, COLUMN, PIE, TABLE
/// </summary>
ChartType ChartType { get; }
/// <summary>
/// Data - data for the chart
/// </summary>
String Data { get; }
/// <summary>
/// Name of the chart, must be unique used to identify each chart stub
/// </summary>
String Name { get; }
/// <summary>
/// Title - the title that will be displayed above the chart
/// </summary>
String Title { get; }
/// <summary>
/// What position will the legend of there is one.
/// </summary>
String LegendPosition { get; }
}
The following uses the above interface
public class ReferralDownloadCount : IChart, IDisposable
{
List<ChartDemo.DataColumn> columns = null;
public ReferralDownloadCount()
{
columns = new List<ChartDemo.DataColumn>()
{
new ChartDemo.DataColumn() { ColumnType = "String" , ColumnValue = "Date" },
new ChartDemo.DataColumn() { ColumnType = "Number" , ColumnValue = "Referral Count"}
};
}
/// <summary>
/// Returns the chart data
/// </summary>
public String Data
{
get
{
String sql = String.Empty;
String jsonResult = String.Empty;
DataSet ds = null;
DataTable dt = null;
List<ReferralCountData> results = null;
JsonSerializer serializer = null;
try
{
sql = "Select * From[Portal].[ReferralCount] Where DATEDIFF(d, [Download Date], Convert(Date, GETDATE())) < 8 Order By[Download Date] Asc";
ds = DataToolbox.Execute(new SqlConnection(Properties.Settings.Default.DataConnection), sql, CommandType.Text);
if (ds.Tables.Count > 0)
{
dt = ds.Tables[0]; // we really are only expecting one table
results = new List<ReferralCountData>();
serializer = new JsonSerializer();
serializer.Converters.Add(new JavaScriptDateTimeConverter());
foreach ( DataRow dr in dt.Rows)
{
using (ReferralCountData rcd = new ReferralCountData()
{
Label = ((System.DateTime)dr.ItemArray[0]).ToString("dd/MM/yyyy"),
Value = Convert.ToInt32(dr["Referral Count"])
})
{
results.Add(rcd);
}
}
jsonResult = JsonConvert.SerializeObject(results);
}
}
catch ( System.Exception ex)
{
throw ex;
}
finally
{
}
return jsonResult;
}
}
public List<ChartDemo.DataColumn> Columns {
get
{
return columns;
}
set
{
columns = value;
}
}
public ChartType ChartType => ChartType.COLUMN;
public string Name => "REFERRALCOUNT";
public string Title => "Referral Download Count";
public string LegendPosition => "None";
public void Dispose()
{
}
}
The site traverses a directory containing DLLs, and searches for any classes such as the one above to create Charts
Classes which have inherited from IChart are then extracted as each DLL is checked by GetChartPlugins
static List<IChart> GetChartPlugins(List<Assembly> assemblies)
{
List<Type> availableTypes = new List<Type>();
List<Type> alertList = null;
try
{
foreach (Assembly currentAssembly in assemblies)
availableTypes.AddRange(currentAssembly.GetTypes());
alertList = availableTypes.FindAll(delegate (Type t)
{
List<Type> interfaceTypes = new List<Type>(t.GetInterfaces());
return interfaceTypes.Contains(typeof(IChart));
});
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (exFileNotFound != null)
{
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
}
catch ( System.Exception ex)
{
throw ex;
}
finally
{
}
// convert the list of Objects to an instantiated list of ICalculators
return alertList.ConvertAll<IChart>(delegate (Type t) { return Activator.CreateInstance(t) as IChart; });
}
However when this runs the attempt to Call currentAssembly.GetTypes() falls over throwing a ReflectionTypeLoadException
Which eventually becomes;
Method 'get_Columns' in type 'ReferralManagementCharts.ReferralDownloadCount' from assembly 'ReferralManagementCharts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
Can anyone see why since the Columns property of ReferralDownlodCount
public List<ChartDemo.DataColumn> Columns {
get
{
return columns;
}
set
{
columns = value;
}
}
Does have a get.

Related

Create default config for each tenant in C#

I have basic CRUD for a class ConfigSystem.
I have also two methods in the basic ConfigSystem Service.
public Type Get<Type>(string key, string userId)
{
//get config system
var configEntry = this.repository.GetSingleAsync(cs => cs.Key == key && cs.UserId == userId).Await();
if (configEntry == null)
{
configEntry = this.repository.GetSingleAsync(cs => cs.Key == key).Await();
}
if (typeof(Type).Name == configEntry.Type)
{
return JsonConvert.DeserializeObject<Type>(configEntry.Value);
//invalid config type exception
}
else
{
throw new InvalidCastException($"Cannot get the type of this particular key! ");
}
}
public async Task Set<T>(string key, string userId, T value)
{
CheckKey(key, userId);
// find key and then update its value
var configEntry = await this.repository.GetSingleAsync(cs => cs.Key == key && cs.UserId == userId);
//configEntry.Key = key;
if (configEntry == null)
{
configEntry = await this.repository.GetSingleAsync(cs => cs.Key == key);
}
configEntry.Type = value.GetType().Name;
configEntry.Value = JsonConvert.SerializeObject(value);
await this.repository.UpdateAsync(configEntry);
}
I have a static file Config:
public static Dictionary<string, object> ConfigurationList = new Dictionary<string, object>()
{
{"Currency", Enums.Currency.Lev },
{"Measure", Enums.Unit.LinearMetre },
{"DailyDraftLimit", 6},
{"DeleteDraftPeriod", 5},
{"NotificationStartTime", new TimeSpan(03, 00, 00) }, //give exact time 3AM.},
//TODO: Exclude weekends
{"NotificationPeriod", 24},//24 hours
{"PaymentNotificationPeriod", 24},
{"PaymentReceivers", null}, //what do I put here? if I dont have the ids/ -> we put null if we are not sure about the info or the info is not static!
{"VisitsNotificationPeriod", 24},
{"VisitsInternalReceivers", null},
{"VisitsExternalReceivers", null},
{"TermsNotificationPeriod", 24},
{"TermsReceivers", null }
}
and this is my ConfigSystem class:
public class ConfigSystem : AuditEntity<long>
{
/// <summary>
/// Gets or sets the key.
/// </summary>
public string Key { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
public string Type { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
public string Value { get; set; }
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
public string UserId { get; set; }
}
This is my method in the Startup.
private void CreateDefaultConfig(ApplicationDbContext context, IServiceScope serviceScope)
{
var defaultConfig = DefaultTenantConfig.ConfigurationList;
if (context.ConfigSystems.Count() == 0)
{
var configSystemService = serviceScope.ServiceProvider.GetRequiredService<IConfigSystemService>();
//foreach (var entry in defaultConfig)
//{
// var entityToSaveInDb = configSystemService.CreateAsync()
//}
//create for each record a config system entry in the database
}
//check if in the context of the database there are no configs
}
I have no idea what to do with the object, but I had to keep it in object because I had different type of entries. I need to create them for each tenant and it is a default config if they are not created in the database.

How can I create properties in a class dynamically

For importing an Excel sheet in SQL Table I used this code, it uses SqlBulkCopy.
How can I Remove the mapper class and create the columns dynamically?
using SQL;
namespace Reader
{
Public partial class Form1 :Form
{
/// <summary>
/// Import Excel document into the SQL Database and Datagridview
/// </summary>
private void ImportExcel_Click(object sender, EventArgs e)
{
try
{
using (OpenFileDialog ImportExcelFileDialog = new OpenFileDialog() { Filter = "Excel Workbook|*.xlsx|Excel 97 -2003 Workbook|*.xls" })
{
if (ImportExcelFileDialog.ShowDialog() == DialogResult.OK)
{
using (var stream = File.Open(ImportExcelFileDialog.FileName, FileMode.Open, FileAccess.Read))
{
using (IExcelDataReader Reader = ExcelReaderFactory.CreateReader(stream))
{
DataSet result = Reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (_) => new ExcelDataTableConfiguration() { UseHeaderRow = true }
});
SqlConnection Connection = new SqlConnection(SQL_Commands._Connectionstring);
Connection.Open();
//SqliteDatabase[3] will give the table name that is used in SQLExpress
sqlcommands.DeleteTable(SqliteDatabase[3]);
//this is created from a SQL Query file there is only one column and that is ID
sqlcommands.RecreateDatabase(Connection);
//Get result from Excel file and create a Table from it.
tableCollection = result.Tables;
DataTable dt = tableCollection[SqliteDatabase[3]];
//Create columns in SQL Database
foreach(DataColumn column in dt.Columns)
{
if(column.ColumnName != "ID")
{
string columnName = "[" + column.ColumnName + "]";
sqlcommands.AddColumn(columnName, SQLite.SqliteDatabase[3], "Text");
}
}
//write already the values to datagridview
InstrumentsBindingSource.DataSource = dt;
//Convert Datatable to IEnumerable(Instruments is a Mapper class)
var parts = BulkHelper.DataTableToIEnumerable<Instruments>(dt);
sqlcommands.ImportToExcel(parts, Connection);
sqlcommands.UpdateTableTotal(SQLite.SqliteDatabase[3], InstrumentsBindingSource, dataGridView1);
}
}
}
}
}
catch (Exception EX)
{
MessageBox.Show(EX.ToString(), "Import Excel Sheet", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
Convert DataTable to IEnumerable
namespace SQL
{
public static class BulkHelper
{
public static IEnumerable<T> DataTableToIEnumerable<T>(this DataTable table) where T : class, new()
{
try
{
var objType = typeof(T);
ICollection<PropertyInfo> properties;
lock (_Properties)
{
if (!_Properties.TryGetValue(objType, out properties))
{
properties = objType.GetProperties().Where(property => property.CanWrite).ToList();
_Properties.Add(objType, properties);
}
}
var list = new List<T>(table.Rows.Count);
foreach (var row in table.AsEnumerable().Skip(1))
{
var obj = new T();
foreach (var prop in properties)
{
try
{
var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
var SafeValue = row[prop.Name] == null ? null : Convert.ChangeType(row[prop.Name], propType);
prop.SetValue(obj, SafeValue, null);
}
catch
{
// ignored
}
}
list.Add(obj);
}
return list;
}
catch
{
return Enumerable.Empty<T>();
}
}
}
}
Create BulkData and write it to SQL Table
namespace SQL
{
public class SQL_Commands
{
public void ImportToExcel(IEnumerable<Instruments> Parts, SqlConnection connection)
{
try
{
var bulkcopy = new SqlBulkCopy(connection);
bulkcopy.DestinationTableName = "Instruments";
bulkcopy.ColumnMappings.Add("Tag", "Tag");
bulkcopy.ColumnMappings.Add("Area", "Area");
bulkcopy.ColumnMappings.Add("Number", "Number");
bulkcopy.ColumnMappings.Add("Tag_Name", "Tag_Name");
bulkcopy.ColumnMappings.Add("Component_Description", "Component_Description");
bulkcopy.ColumnMappings.Add("Function", "Function");
bulkcopy.ColumnMappings.Add("Brand", "Brand");
bulkcopy.ColumnMappings.Add("Type", "Type");
bulkcopy.ColumnMappings.Add("M_Connection", "M_Connection");
bulkcopy.ColumnMappings.Add("E_Connection", "E_Connection");
bulkcopy.ColumnMappings.Add("Range", "Range");
bulkcopy.ColumnMappings.Add("Remark", "Remark");
bulkcopy.ColumnMappings.Add("Ordering_Code", "Ordering_Code");
bulkcopy.ColumnMappings.Add("Panel", "Panel");
bulkcopy.ColumnMappings.Add("DI", "DI");
bulkcopy.ColumnMappings.Add("DO", "DO");
bulkcopy.ColumnMappings.Add("AI", "AI");
bulkcopy.ColumnMappings.Add("AO", "AO");
bulkcopy.ColumnMappings.Add("Ethernet", "Ethernet");
bulkcopy.ColumnMappings.Add("ASI", "ASI");
using (var datareader = new ObjectDataReader<Instruments>(Parts))
{
bulkcopy.WriteToServer(datareader);
}
}
catch (Exception EX)
{
MessageBox.Show(EX.ToString(), "InsertBulk", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
Mapper Class
namespace SQL
{
public class Instruments
{
public string Tag { get; set; }
public string Area { get; set; }
public string Number { get; set; }
public string Tag_Name { get; set; }
public string Component_Description { get; set; }
public string Function { get; set; }
public string Brand { get; set; }
public string Type { get; set; }
public string M_Connection { get; set; }
public string E_Connection { get; set; }
public string Range { get; set; }
public string Remark { get; set; }
public string Ordering_Code { get; set; }
public string Panel { get; set; }
public string DI { get; set; }
public string DO { get; set; }
public string AI { get; set; }
public string AO { get; set; }
public string Ethernet { get; set; }
public string ASI { get; set; }
}
}
I tried to search on the web, but I could not find a good solution for it.
I changed it to this and it works. Thanks
/// <summary>
/// Import Excel document into the SQL Database and Datagridview
/// </summary>
private void ImportExcel_Click(object sender, EventArgs e)
{
try
{
using (OpenFileDialog ImportExcelFileDialog = new OpenFileDialog() { Filter = "Excel Workbook|*.xlsx|Excel 97 -2003 Workbook|*.xls" })
{
if (ImportExcelFileDialog.ShowDialog() == DialogResult.OK)
{
using (var stream = File.Open(ImportExcelFileDialog.FileName, FileMode.Open, FileAccess.Read))
{
using (IExcelDataReader Reader = ExcelReaderFactory.CreateReader(stream))
{
DataSet result = Reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (_) => new ExcelDataTableConfiguration() { UseHeaderRow = true }
});
SqlConnection Connection = new SqlConnection(SQL_Commands._Connectionstring);
Connection.Open();
sqlcommands.DeleteTable(SqliteDatabase[3]);
//this is created from a SQL Query file there is only one column and that is ID
sqlcommands.RecreateDatabase(Connection);
//Get result from Excel file and create a Table from it.
tableCollection = result.Tables;
DataTable dt = tableCollection[SqliteDatabase[3]];
// Create new List
List<string> ListColums = new List<string>();
//Create columns in SQL Database
foreach(DataColumn column in dt.Columns)
{
if(column.ColumnName != "ID")
{
string columnName = "[" + column.ColumnName + "]";
sqlcommands.AddColumn(columnName, SQLite.SqliteDatabase[3], "Text");
//Add Column Names to List<string>
ListColums.Add(column.ColumnName);
}
}
//write already the values to datagridview
InstrumentsBindingSource.DataSource = dt;
//Create a connection
sqlcommands.ImportFromExcel(Connection,dt, ListColums);
sqlcommands.UpdateTableTotal(SQLite.SqliteDatabase[3], InstrumentsBindingSource, dataGridView1);
}
}
}
}
}
catch (Exception EX)
{
MessageBox.Show(EX.ToString(), "UpdateTableTotal", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
public void ImportFromExcel(SqlConnection connection,DataTable _dt,List<string> ColumnNames )
{
try
{
// Get the DataTable
DataTable dtInsertRows = _dt;
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(connection.ConnectionString, SqlBulkCopyOptions.KeepIdentity))
{
bulkcopy.DestinationTableName = "Instruments";
bulkcopy.BatchSize = _dt.Rows.Count;
foreach (string Column in ColumnNames)
{
var split = Column.Split(new[] { ',' });
bulkcopy.ColumnMappings.Add(split.First(), split.Last());
}
bulkcopy.WriteToServer(dtInsertRows);
}
}
catch (Exception EX)
{
MessageBox.Show(EX.ToString(), "InsertBulk", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}

.net mvc 5 C# with crystal report

I use .net mvc 5 c# repository pattern with database first approach, In my Service layer i calculate and apply group by condition on there and pass this data into viewmodel and razor view,
my question is can i used this viewmodel (with data) for creating the crystal report from this viewmodel ? crystal report is installed on visual studio (2015). code information are
code on controller are
public ActionResult Top20SupplierReport()
{
var AllSupplier = _supplier.Top20Supplier();
}
Service layer code are
public List<GroupBySupplierVM> Top20Supplier()
{
var AllSupplier = //code for get all supplier list from database
var groupByData = from sup in AllSupplier
group sup by sup .cf02supplier_Name into g
let TotalVol = g.Sum(x => x.cf08collection_Received_Volume)
let TotalAmount = g.Sum(x => x.cf08collection_Balance)
orderby TotalVol descending
select new GroupBySupplierVM
{
Key = g.Key,
Values = g.ToList(),
TotalReceivedVolume = Convert.ToDouble(TotalVol),
TotalBalance = TotalAmount
};
return groupByData.Take(20).ToList();
}
ViewModel are
public class GroupBySupplierVM
{
public string Key;
public List<SupplierVM> Values;
[Display(Name = "Total")]
public double TotalReceivedVolume { get; set; }
public double? TotalBalance { get; set; }
}
and
public class SupplierVM
{
public int cf02supplier_Id { get; set; }
public string cf02supplier_Address { get; set; }
public string cf02supplier_Name { get; set; }
public string cf02supplier_City_Id { get; set; }
public string cf02supplier_Telephone { get; set; }
public string cf02supplier_MobileNo { get; set; }
public decimal cf02supplier_Balance { get; set; }
......
// other Entity are also there
}
can i create crystal report from the GroupBySupplierVM ? if yes how to use on crystal report and how to show on view page ?
anybody have knowledge about this how to use on crystal report. Please help me...
Yes you can.
-Create a Data Table
Sample DataTable
-and my code
//domResult -> List of your View Model
DataTable dt = ReportHelper.ToDataTable(domResult);
// LCDraft_Domestic--> Crystal Report
LCDraft_Domestic rpt = new LCDraft_Domestic();
//My Data Table
rpt.Database.Tables["DraftData"].SetDataSource(dt);
Use to Convert List to DataTable
-- My ReportHelper --
Create a static class a put this code
public static class ReportHelper
{
public static DataTable ToDataTable<T>(this IList<T> items)
{
var tb = new DataTable(typeof(T).Name);
PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in props)
{
Type t = GetCoreType(prop.PropertyType);
tb.Columns.Add(prop.Name, t);
}
foreach (T item in items)
{
var values = new object[props.Length];
for (int i = 0; i < props.Length; i++)
{
values[i] = props[i].GetValue(item, null);
}
tb.Rows.Add(values);
}
return tb;
}
/// <summary>
/// Determine of specified type is nullable
/// </summary>
public static bool IsNullable(Type type)
{
return !type.IsValueType || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
}
/// <summary>
/// Return underlying type if type is Nullable otherwise return the type
/// </summary>
public static Type GetCoreType(Type type)
{
if (type != null && IsNullable(type))
{
if (!type.IsValueType)
{
return type;
}
else
{
return Nullable.GetUnderlyingType(type);
}
}
else
{
return type;
}
}
static TableLogOnInfo crTableLogonInfo;
static ConnectionInfo crConnectionInfo;
static Tables crTables;
static Database crDatabase;
public static void ReportLogin(ReportDocument crDoc, string Server, string Database, string UserID, string Password)
{
crConnectionInfo = new ConnectionInfo();
crConnectionInfo.ServerName = Server;
crConnectionInfo.DatabaseName = Database;
crConnectionInfo.UserID = UserID;
crConnectionInfo.Password = Password;
crDatabase = crDoc.Database;
crTables = crDatabase.Tables;
foreach (CrystalDecisions.CrystalReports.Engine.Table crTable in crTables)
{
crTableLogonInfo = crTable.LogOnInfo;
crTableLogonInfo.ConnectionInfo = crConnectionInfo;
crTable.ApplyLogOnInfo(crTableLogonInfo);
}
}
//No Login
public static void ReportLogin(ReportDocument crDoc, string Server, string Database)
{
crConnectionInfo = new ConnectionInfo();
crConnectionInfo.ServerName = Server;
crConnectionInfo.DatabaseName = Database;
crConnectionInfo.IntegratedSecurity = true;
crDatabase = crDoc.Database;
crTables = crDatabase.Tables;
foreach (CrystalDecisions.CrystalReports.Engine.Table crTable in crTables)
{
crTableLogonInfo = crTable.LogOnInfo;
crTableLogonInfo.ConnectionInfo = crConnectionInfo;
crTable.ApplyLogOnInfo(crTableLogonInfo);
}
}}
Crystal reports really only understands DataSets or DataTables. You have to convert your object collection to DataTable or DataSet and then setting it as the report's data source. see .NET - Convert Generic Collection to DataTable and Convert generic List/Enumerable to DataTable? for tips on creating your table.
Once you have your dataset/table, you need to create the xml schema required to design your report. This can be done with the WriteXml method. You only need this while designing the report. Locate this xml file, and create your report.
var table = groupByData.ToDataTable(); // using your extension method
// snippet for creating schema from table
using (var fs = new StreamWriter(xmlFile)) // XML File Path
{
table.WriteXml(fs, XmlWriteMode.WriteSchema);
}
As for Crystal and MVC, not really. Crystal Reports is WebForms technology, so you'll have to have an aspx page to host the report viewer in your MVC application. You can use Microsoft.Aspnet.FriendlyUrl and routes.EnableFriendlyUrls(); to hide the extension.
The actual databinding is quite simple:
var table = groupByData.ToDataTable(); // using your extension method
report.SetDataSource(table);
viewer.ReportSource = report;

Error while converting List of objects to Datatable

Hello I have the following method.
public static DataTable ConvertToDataTable<T>(List<T> lstData)
{
PropertyDescriptorCollection objPropertiesCollection = TypeDescriptor.GetProperties(typeof(T));
DataTable dtResult = new DataTable();
foreach (PropertyDescriptor objProperty in objPropertiesCollection)
{
dtResult.Columns.Add(objProperty.Name, Nullable.GetUnderlyingType(objProperty.PropertyType) ?? objProperty.PropertyType);
}
foreach (T item in lstData)
{
DataRow dr = dtResult.NewRow();
foreach (PropertyDescriptor objProperty in objPropertiesCollection)
{
dr[objProperty.Name] = objProperty.GetValue(item) ?? DBNull.Value;
}
dtResult.Rows.Add(dr);
}
return dtResult;
}
and I have the following Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ClientesPagos.Clases
{
[Serializable]
public class Servicio
{
public int IdServicio;
public string Nombre;
public string Descripcion;
public string Imagen;
public decimal Precio;
public int Cantidad;
public decimal Total;
public Servicio()
{
}
public Servicio(int id, string nombre, string descripcion, string img, decimal precio, int cantidad)
{
IdServicio = id;
Nombre = nombre;
Descripcion = descripcion;
Imagen = img;
Precio = precio;
Cantidad = cantidad;
Total = cantidad * precio;
}
}
}
now the problem is on the first line of the method
PropertyDescriptorCollection objPropertiesCollection =
TypeDescriptor.GetProperties(typeof(T));
It does not fill objPropertiesCollection and therefore the following instructions don't work.
Is there something wrong on the class, that it may not be working on the first Method?
You are trying to get properties an you should change your fields to properties.
Those are properties:
public int IdServicio {get; set;}
public string Nombre { get; set; }
public string Descripcion { get; set; }
public string Imagen { get; set; }
public decimal Precio { get; set; }
public int Cantidad { get; set; }
public decimal Total { get; set; }
So you want to convert "everything" to a DataTable? Then i would use the CopyToDataTable approach on MSDN with it's ObjectShredder class.
How to: Implement CopyToDataTable Where the Generic Type T Is Not a DataRow
public class ObjectShredder<T> {
private System.Reflection.FieldInfo[] _fi;
private System.Reflection.PropertyInfo[] _pi;
private System.Collections.Generic.Dictionary<string, int> _ordinalMap;
private System.Type _type;
// ObjectShredder constructor.
public ObjectShredder() {
_type = typeof(T);
_fi = _type.GetFields();
_pi = _type.GetProperties();
_ordinalMap = new Dictionary<string, int>();
}
/// <summary>
/// Loads a DataTable from a sequence of objects.
/// </summary>
/// <param name="source">The sequence of objects to load into the DataTable.</param>
/// <param name="table">The input table. The schema of the table must match that
/// the type T. If the table is null, a new table is created with a schema
/// created from the public properties and fields of the type T.</param>
/// <param name="options">Specifies how values from the source sequence will be applied to
/// existing rows in the table.</param>
/// <returns>A DataTable created from the source sequence.</returns>
public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options) {
// Load the table from the scalar sequence if T is a primitive type.
if (typeof(T).IsPrimitive) {
return ShredPrimitive(source, table, options);
}
// Create a new table if the input table is null.
if (table == null) {
table = new DataTable(typeof(T).Name);
}
// Initialize the ordinal map and extend the table schema based on type T.
table = ExtendTable(table, typeof(T));
// Enumerate the source sequence and load the object values into rows.
table.BeginLoadData();
using (IEnumerator<T> e = source.GetEnumerator()) {
while (e.MoveNext()) {
if (options != null) {
table.LoadDataRow(ShredObject(table, e.Current), (LoadOption)options);
} else {
table.LoadDataRow(ShredObject(table, e.Current), true);
}
}
}
table.EndLoadData();
// Return the table.
return table;
}
public DataTable ShredPrimitive(IEnumerable<T> source, DataTable table, LoadOption? options) {
// Create a new table if the input table is null.
if (table == null) {
table = new DataTable(typeof(T).Name);
}
if (!table.Columns.Contains("Value")) {
table.Columns.Add("Value", typeof(T));
}
// Enumerate the source sequence and load the scalar values into rows.
table.BeginLoadData();
using (IEnumerator<T> e = source.GetEnumerator()) {
Object[] values = new object[table.Columns.Count];
while (e.MoveNext()) {
values[table.Columns["Value"].Ordinal] = e.Current;
if (options != null) {
table.LoadDataRow(values, (LoadOption)options);
} else {
table.LoadDataRow(values, true);
}
}
}
table.EndLoadData();
// Return the table.
return table;
}
public object[] ShredObject(DataTable table, T instance) {
FieldInfo[] fi = _fi;
PropertyInfo[] pi = _pi;
if (instance.GetType() != typeof(T)) {
// If the instance is derived from T, extend the table schema
// and get the properties and fields.
ExtendTable(table, instance.GetType());
fi = instance.GetType().GetFields();
pi = instance.GetType().GetProperties();
}
// Add the property and field values of the instance to an array.
Object[] values = new object[table.Columns.Count];
foreach (FieldInfo f in fi) {
values[_ordinalMap[f.Name]] = f.GetValue(instance);
}
foreach (PropertyInfo p in pi) {
values[_ordinalMap[p.Name]] = p.GetValue(instance, null);
}
// Return the property and field values of the instance.
return values;
}
public DataTable ExtendTable(DataTable table, Type type) {
// Extend the table schema if the input table was null or if the value
// in the sequence is derived from type T.
foreach (FieldInfo f in type.GetFields()) {
if (!_ordinalMap.ContainsKey(f.Name)) {
// Add the field as a column in the table if it doesn't exist
// already.
DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name]
: table.Columns.Add(f.Name, f.FieldType);
// Add the field to the ordinal map.
_ordinalMap.Add(f.Name, dc.Ordinal);
}
}
foreach (PropertyInfo p in type.GetProperties()) {
if (!_ordinalMap.ContainsKey(p.Name)) {
// Add the property as a column in the table if it doesn't exist
// already.
DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name]
: table.Columns.Add(p.Name, p.PropertyType);
// Add the property to the ordinal map.
_ordinalMap.Add(p.Name, dc.Ordinal);
}
}
// Return the table.
return table;
}
}
Now you can add these extensions:
public static class CustomLINQtoDataSetMethods {
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source) {
return new ObjectShredder<T>().Shred(source, null, null);
}
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source,
DataTable table, LoadOption? options) {
return new ObjectShredder<T>().Shred(source, table, options);
}
}
Now CopyToDataTable works with any kind of IEnumerable<T>(even anonymous types) not only with DataRows.

Logging state of object. Getting all its property values as string

public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
......
var emp1Address = new Address();
emp1Address.AddressLine1 = "Microsoft Corporation";
emp1Address.AddressLine2 = "One Microsoft Way";
emp1Address.City = "Redmond";
emp1Address.State = "WA";
emp1Address.Zip = "98052-6399";
Consider above class and later its initialization. Now at some point I want to log its state when error occurs. I would like to get the string log somewhat like below.
string toLog = Helper.GetLogFor(emp1Address);
sting toLog should look something like below.
AddressLine1 = "Microsoft Corporation";
AddressLine2 = "One Microsoft Way";
City = "Redmond";
State = "WA";
Zip = "98052-6399";
And then I will log toLog string.
How can I access all the property names and property values of an object within Helper.GetLogFor() method?
Solution that I implemented:-
/// <summary>
/// Creates a string of all property value pair in the provided object instance
/// </summary>
/// <param name="objectToGetStateOf"></param>
/// <exception cref="ArgumentException"></exception>
/// <returns></returns>
public static string GetLogFor(object objectToGetStateOf)
{
if (objectToGetStateOf == null)
{
const string PARAMETER_NAME = "objectToGetStateOf";
throw new ArgumentException(string.Format("Parameter {0} cannot be null", PARAMETER_NAME), PARAMETER_NAME);
}
var builder = new StringBuilder();
foreach (var property in objectToGetStateOf.GetType().GetProperties())
{
object value = property.GetValue(objectToGetStateOf, null);
builder.Append(property.Name)
.Append(" = ")
.Append((value ?? "null"))
.AppendLine();
}
return builder.ToString();
}
public static string GetLogFor(object target)
{
var properties =
from property in target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
select new
{
Name = property.Name,
Value = property.GetValue(target, null)
};
var builder = new StringBuilder();
foreach(var property in properties)
{
builder
.Append(property.Name)
.Append(" = ")
.Append(property.Value)
.AppendLine();
}
return builder.ToString();
}
static void Log(object #object)
{
foreach (var property in #object.GetType().GetProperties())
Console.WriteLine(property.Name + ": " + property.GetValue(#object, null).ToString());
}
You can access the property name using reflection
like following
Type t = emp1Address.GetType();
PropertyInfo [] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
{
//You can get the value (using GetValue() method) and name (p.Name) here.
}

Categories