I Have Excel import feature with ClosedXM and meet the errror with ctivator.CreateInstance(typeof(T)). It throws:
System.MissingMethodException: 'Cannot dynamically create an instance of type
'FSH.WebApi.Domain.Catalog.Brand'.
Reason: No parameterless constructor defined.
Please help !!!
// Controller
[HttpPost("import")]
public async Task<ActionResult<int>> ImportAsync(ImportBrandsRequest request)
{
IList<Brand> result = new List<Brand>();
result = await _excelReader.ImportAsync<Brand>(request.ExcelFile, FileType.Excel);
return Ok(result.Count);
}
// Interface
public interface IExcelReader : ITransientService
{
Task<IList<T>> ImportAsync<T>(
FileUploadRequest request,
FileType supportedFileType,
string sheetName = "Sheet1");
}
// Service:
public class ExcelReader : IExcelReader
{
public async Task<IList<T>> ImportAsync<T>(FileUploadRequest request, FileType supportedFileType, string sheetName = "Sheet1")
{
var streamData = new MemoryStream(Convert.FromBase64String(base64Data));
List<T> list = new List<T>();
Type typeOfObject = typeof(T);
using (IXLWorkbook workbook = new XLWorkbook(streamData))
{
var worksheet = workbook.Worksheets.First(w => w.Name == sheetName);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
// header column texts
var columns = worksheet.FirstRow().Cells().Select((v, i) => new { v.Value, Index = i + 1 });
// indexing in closedxml starts with 1 not from 0
// Skip first row which is used for column header texts
foreach (IXLRow row in worksheet.RowsUsed().Skip(1))
{
T item = (T)Activator.CreateInstance(typeof(T));
// T item = (T)Activator.CreateInstance(typeof(T));
foreach (PropertyDescriptor prop in properties)
{
int colIndex = columns.Single(
c => c.Value.ToString() == prop.Name).Index;
if (colIndex == 0) continue;
object? val = row.Cell(colIndex).Value;
var type = prop.PropertyType;
prop.SetValue(item, Convert.ChangeType(val, type));
}
if (item != null) list.Add(item);
}
}
return await Task.FromResult(list);
}
Presumably, Brand doesn't have a constructor without parameters. So, either add a parameterless constructor to Brand or pass parameter arguments to Activator.CreateInstance.
I am not very experienced with Windows Forms and am not pretty sure how I should tackle with this task the best way possible. I have a class which looks like this:
public class VariableMapping
{
private string variableName;
private string variableText;
private string variableSelector;
public VariableMapping(string variableName, string variableText, string variableSelector)
{
this.VariableName = variableName;
this.VariableText = variableText;
this.VariableSelector = variableSelector;
}
public string VariableName
{
get { return this.variableName; }
set { this.variableName = value; }
}
public string VariableText
{
get { return this.variableText; }
set { this.variableText = value; }
}
public string VariableSelector
{
get { return this.variableSelector; }
set { this.variableSelector = value; }
}
}
I want to create a DataGridView which should be bound to a number of elements of type VariableMapping in a list. However, I want only 1 of the properties(VariableText) of every instance to be shown in the DataGridView but I want to be able to address the whole object through the DataGrid when I need to. I also need to add 2 more custom columns: a ComboBox with predefined values and a NumberBox.
It might seem a really simple task but I'm trully unexperienced in WinForms and couldn't find a solution I can use already. Thank you!
Edit: I am trying something like this but it doesn't seem to work properly:
public partial class MappingTable : Form
{
private DataGridView dataGridView1 = new DataGridView();
public MappingTable(List<VariableMapping> variableMappings)
{
InitializeComponent();
var colors = new List<string>() { "#color_k1", "#color_k2", "#color_s1" };
dataGridView1.AutoGenerateColumns = false;
dataGridView1.AutoSize = true;
dataGridView1.DataSource = variableMappings;
DataGridViewColumn titleColumn = new DataGridViewColumn();
titleColumn.DataPropertyName = "VariableText";
titleColumn.HeaderText = "Variable";
titleColumn.Name = "Variable*";
dataGridView1.Columns.Add(titleColumn);
DataGridViewComboBoxColumn colorsColumn = new DataGridViewComboBoxColumn();
colorsColumn.DataSource = colors;
colorsColumn.HeaderText = "Color";
dataGridView1.Columns.Add(colorsColumn);
DataGridViewTextBoxColumn opacityColumn = new DataGridViewTextBoxColumn();
opacityColumn.HeaderText = "Opacity";
dataGridView1.Columns.Add(opacityColumn);
this.Controls.Add(dataGridView1);
this.AutoSize = true;
}
}
I have a combobox that contains a list of scalar properties of an entity. I need to do a search on the property specified by the user in the combobox.
How do I reference the property when I have it as a string?
Something similar to DBSet<> for entity.
Example:
if (order.Firstname != null && order.Firstname.ToLower().Contains(textBoxSearch.Text.ToLower()))
In the above example, I need to replace Firstname with Surname or any other property, at runtime, depending on what the user selected.
You can always use reflection, for your case you'll need something along these lines:
static void Main(string[] args)
{
var entity = new Entity {
Height = 172,
FirstName = "Foo",
Birthday = new DateTime(1, 1, 1995)
};
var firstName = GetEntityProperty<string>(entity, "FirstName");
}
public static T GetEntityProperty<T>(object entity, string propertyName)
{
var type = entity.GetType();
var property = type.GetProperty(propertyName);
return (T)property.GetValue(entity);
}
Provided solution works, but it is not strongly-typed, so it is sensible to property renaming. A strongly-typed approach can be used, but it requires some setup. However, part of the setup is so generic that it can be reused:
public class Order
{
public String OrderNo { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String Misc { get; set; }
}
// clearly define properties that allow search
public enum OrderSearchableProp
{
OrderNo = 1,
FirstName = 2,
LastName = 3
}
class Program
{
// strongly-type requires an explicit mapping between property and its expression within the order object
static Dictionary<OrderSearchableProp, Expression<Func<Order, String>>> OrderSearchableMap = new Dictionary<OrderSearchableProp, Expression<Func<Order, string>>>()
{
{ OrderSearchableProp.OrderNo, o => o.OrderNo },
{ OrderSearchableProp.FirstName, o => o.FirstName },
{ OrderSearchableProp.LastName, o => o.LastName },
};
// this gets a PropertyInfo based from an Expression. It should be placed into a generic utility class
// credit goes to Daniel - http://stackoverflow.com/a/17116267/2780791
public static PropertyInfo GetPropertyFromExpression<T, TProp>(Expression<Func<T, TProp>> GetPropertyLambda)
{
MemberExpression Exp = null;
//this line is necessary, because sometimes the expression comes in as Convert(originalexpression)
if (GetPropertyLambda.Body is UnaryExpression)
{
var UnExp = (UnaryExpression)GetPropertyLambda.Body;
if (UnExp.Operand is MemberExpression)
{
Exp = (MemberExpression)UnExp.Operand;
}
else
throw new ArgumentException();
}
else if (GetPropertyLambda.Body is MemberExpression)
{
Exp = (MemberExpression)GetPropertyLambda.Body;
}
else
{
throw new ArgumentException();
}
return (PropertyInfo)Exp.Member;
}
public static IList<Order> getFilteredOrders(int propFilterValue, IList<Order> orders, String needle)
{
var filterValue = (OrderSearchableProp)propFilterValue;
var filterProp = OrderSearchableMap[filterValue];
var lowNeedle = needle?.ToLower() ?? String.Empty;
return orders.Where(o =>
{
var propInfo = GetPropertyFromExpression<Order, String>(filterProp);
var propValue = (String) propInfo.GetValue(o) ?? String.Empty;
return propValue.ToLower().Contains(lowNeedle);
}).ToList();
}
static void Main(string[] args)
{
// can be used to populate the combo items
// otherwise, not used in this example
OrderSearchableProp[] enumValues = (OrderSearchableProp[])Enum.GetValues(typeof(OrderSearchableProp));
// test orders
var orderList = new List<Order>()
{
new Order() { OrderNo = "1234ABC", FirstName = "George", LastName = "Taylor", Misc = "Extra information"},
new Order() { OrderNo = "AB10", FirstName = "Anonymous", LastName = "X", Misc = "No comment"}
};
// test OrderNo search
var searchProp = (int) OrderSearchableProp.OrderNo;
var foundOrders = getFilteredOrders(searchProp, orderList, "ABC");
// test FirstName search
searchProp = (int)OrderSearchableProp.FirstName;
foundOrders = getFilteredOrders(searchProp, orderList, "a");
// test LastName search with empty string
searchProp = (int)OrderSearchableProp.LastName;
foundOrders = getFilteredOrders(searchProp, orderList, "");
// empty return
searchProp = (int)OrderSearchableProp.OrderNo;
foundOrders = getFilteredOrders(searchProp, orderList, null);
}
}
Using Entity framework how can I pick any string to display in a textbox. Using myReader in the past I would do the following:
while (myReader.Read())
{
string sName = myReader.GetString(1);
txtName.Text = sName;
}
You have to load the entity (=table) you desire and than assign the property of the entity to the textbox.
Something like:
using (var myContext = new EntityContext())
{
myContext.TableName.Load(); //this will load all rows from the table
var list = myContext.TableName.Local; //.Local is a list of the entities which gets populated when you cann the Load() method
//now you can either enumarte your list like so
foreach (TableName item in list)
{
string sName = item.PropertyName; //PropertyName is the property you want to display, this would be like using not an index in myReader.GetString(1) but the columnname myReader.GetString("ColumnName")
txtName.Text = sName;
}
//or if you want to get a certain item out of your list you can use LINQ/MethodChains
//MethodChain
var item = list.First(x => x.PropertyName == "HelloWorld!");
string sName = item.PropertyName;
txtName.Text = sName;
//LINQ
var item = (from x in list
where x.PropertyName == "HelloWorld!"
select x).First();
string sName = item.PropertyName;
txtName.Text = sName;
//the methodchain and linq is a "new" technology that simplifies this code (which does the same)
TableName item = null;
foreach (TableName tempItem in list)
{
if (tempItem.PropertyName == "HelloWorld!")
{
item = tempItem;
break;
}
}
string sName = item.PropertyName;
txtName.Text = sName;
}
I have a DataGridViewComboBoxColumn being craeted at design time.But I want to polulate it at runtime. But it is not happening.
Here is the entire code
public partial class Form1 : Form
{
private List<FileInformation> FileInformationList;
public Form1()
{
InitializeComponent();
PrepareGrid();
DisplayResult();
}
private void PrepareGrid()
{
var fileNameColumn = new DataGridViewTextBoxColumn
{
Name = #"FileName",
HeaderText = "File Name",
DataPropertyName = #"FileName",
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
ReadOnly = false,
Frozen = false
};
dataGridView1.Columns.Add(fileNameColumn);
var downloadColumn = new DataGridViewLinkColumn
{
Name = #"Download",
HeaderText = #"Download",
DataPropertyName = #"Download",
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
ReadOnly = true
};
dataGridView1.Columns.Add(downloadColumn);
var dropdownColumn = new DataGridViewComboBoxColumn
{
Name = #"Test",
HeaderText = #"Country",
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
ReadOnly = true
};
dataGridView1.Columns.Add(dropdownColumn);
}
private void DisplayResult()
{
FileInformationList = LoadItems();
dataGridView1.DataSource = FileInformationList;
((DataGridViewComboBoxColumn)dataGridView1.Columns["Test"]).DataSource = GetCountryList();
((DataGridViewComboBoxColumn)dataGridView1.Columns["Test"]).ValueMember = "id";
((DataGridViewComboBoxColumn)dataGridView1.Columns["Test"]).DisplayMember = "Name";
}
private List<FileInformation> LoadItems()
{
var lstScriptInfo = new List<FileInformation>();
for (int i = 1; i <= 5; i++)
{
lstScriptInfo.Add(new FileInformation { FileName = "File" + i.ToString() + ".txt" });
}
return lstScriptInfo;
}
private DataTable GetCountryList()
{
DataTable CountryDt = new DataTable();
CountryDt.Columns.Add("id");
CountryDt.Columns.Add("Name");
CountryDt.Rows.Add("1", "Canada");
CountryDt.Rows.Add("2", "USA");
return CountryDt;
}
}
public class FileInformation
{
public string FileName { get; set; }
public string Download { get { return "Download File"; } }
}
}
The output is
Please help me to identity what has went wrong?
Two ways to achieve that:
Setting defualt null value for column (skipped null checks):
var cbColumn = (DataGridViewComboBoxColumn)dataGridView1.Columns["Test"];
var ds = GetCountryList();
cbColumn.DataSource = ds;
cbColumn.ValueMember = "id";
cbColumn.DisplayMember = "Name";
cbColumn.DefaultCellStyle.NullValue = ds.Rows[0][0];
cbColumn.DefaultCellStyle.DataSourceNullValue = ds.Rows[0][1];
Second is to iterate after DataBinding and set it manually:
private void DataGridDataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
var hasValue = row.Cells["Test"].Value != null;
if (!hasValue)
{
row.Cells["Test"].Value = 1;
}
}
}
also I'd change binding order:
private void DisplayResult()
{
var cbColumn = (DataGridViewComboBoxColumn)dataGridView1.Columns["Test"];
var ds = GetCountryList();
cbColumn.DataSource = ds;
cbColumn.ValueMember = "id";
cbColumn.DisplayMember = "Name";
cbColumn.DefaultCellStyle.NullValue = ds.Rows[0][0];
cbColumn.DefaultCellStyle.DataSourceNullValue = ds.Rows[0][1];
FileInformationList = LoadItems();
dataGridView1.DataSource = FileInformationList;
}