Dynamically bind to a custom business object at run time

I am working on a Web Forms project that loads Sql query results into DataTable s.

These DataTable are passed in front of the interface, where we bind them to Repeater web control.

This works great. However, now we want to bind to our own custom classes instead of a DataTable . Unfortunately, what I thought was the obvious answer did not work (implementing IDictionary<string, object> in our class).

What do we need to bind Eval to Datum without creating a specific property for each binding? Obviously, DataRow does not have to specifically implement every property that we bind. So, somehow it seems that Eval can look up property values ​​by name in a DataRow.

Here is a custom class

 public class Datum: IDictionary<string, object> { private Dictionary<string, object> _entries; public Datum() { _entries = new Dictionary<string, object>(); } public object this[string s] { get { return this._entries[s]; } } ... } 

This is where the DataSource is installed in the aspx.cs file

 rptTags.DataSource = new[] { new Datum { {"Count", 1} }, new Datum { {"Count", 2 } }; 

And here is the binding in aspx file

 <asp:Repeater ID="rptTags" runat="server"> <ItemTemplate> <%# (int)Eval("Count") > </ItemTemplate> </asp:Repeater> 

Using the above example, we get an error message saying that this property does not exist, which is true, but it also does not exist in the DataRow. How to do it like System.Data.DataRow?

+6
source share
2 answers

This morning I came with new eyes and spent several hours going through the .Net Framework with ILSpy. Finally, I was able to understand this riddle and implement a working solution. I listed everything that I learned that relate to the solution, and then detail my implementation.

  • When you bind a DataTable to a Repeater, each RepeaterItem does not bind to a DataRow, as I expected, but to a DataRowView. This is really not important, except that the DataRowView implements the ICustomTypeDescriptor interface, which we must implement in our class.
  • Although the MSDN documentation says that the Eval method uses reflection to perform late binding, and your expression must evaluate to a public property, this is simply not true. The Eval statement uses the GetProperties () method of ICustomTypeDescriptor to evaluate your expression.

With this in mind, I need to follow the steps that I had to take to create my own custom type, which I could dynamically bind in the same way as a DataTable.

  • Create your own custom class that inherits from PropertyDescriptor.
  • Implement all abstract elements of a PropertyDescriptor. For dynamic binding, GetValue () is most important. Here you determine how to get the values ​​from the class to which you are bound.
  • In the class you are attached to, it inherits from CustomTypeDescriptor. This is a generic class that implements ICustomTypeDescriptor.
  • Override the GetProperties () method of CustomTypeDescriptor and return a list of your PropertyDescriptors (created in steps 1-2) for each value that you would like to bind.
  • Make sure that the Name property is set in your PropertyDescriptions. This .Net property compares your Eval expression to determine which PropertyDescription to use when binding.
  • Define your CustomTypeDescriptor object (created in steps 3-5) as the WebControl data source and use Eval () to dynamically bind to it
+1
source

The bindable DataSource must implement either IEnumerable or IListSource .

Regarding the dynamic aspect: keep in mind that part of the data binding magic happens using reflection.

0
source

Source: https://habr.com/ru/post/908468/


All Articles