简体   繁体   中英

How can I dynamically add inherited properties to EF datastore?

In this post , I asked and answered how to dynamically import values for classes & properties in an EF/DbSet model with no hard coding. I couldn't believe I stumbled around long enough to figure out one way to do it. But...

It revealed a problem that hours of looking through the instance hierarchy in the Locals window couldn't resolve:

While I can import data dynamically on the target class, any properties from the base class it extends from fail. The initial post above illustrates this at the end. Our problem shows itself after we've got the data to import into a DataTable. We're going through the columns of the DataTable, using the matching Types in the database to determine their Type (int, double, etc.).

To do that, one line creates the object we query that info from, and while all is well for Child() properties, as soon as we hit one from the Person() (the base class), it fails with a null reference.

Without re-posting that whole post, I will only paste the relevant bits:

foreach (DataRow dr in dt.Rows) 
{
    i = 0; // I don't like to put var instantiation in a loop...
    // each drItem is the content for the row (theObj)
    foreach (string drItem in dr.ItemArray)
    {
        string entAttrName = dt.Columns[i].ToString();
        string entAttrValue = dr[i].ToString();
        // column (property) name:
        // the value of that property to load into this class' property
        // which type of data is this property? (string, int32, double...)
        // -also has data like if nullable, etc. of use in later refinements...
        TypeInfo thisTypeInfo = theObj.GetType().GetTypeInfo();
        // All details from the property to update/set:

 >>---> PropertyInfo theProp = thisTypeInfo.GetDeclaredProperty(entAttrName);

Above, this last line fails to assign theProp a valid object, instead handing it a null, which barfs our program as soon as it queries it.

The example at the link above (where this snippet came from) works fine as long as you only import values from the Child() class. Inherited properties cause it to halt at the line above.

entAttrName is simply the string of the name of the property, taken from the DataTable's header row earlier, and (while slipping past some lines of code above), essentially does something like:

var aClass = u.CreateInstanceOf(dt.TableName, pathToAssembly);
DbSet dbs = ctx.Set(aClass.GetType());
var theObj = dbs.Create(aClass.GetType());
foreach (DataRow dr in dt.Rows)...
foreach (string drItem in dr.ItemArray)...
string entAttrName = dt.Columns[i].ToString();
string entAttrValue = dr[i].ToString();
TypeInfo thisTypeInfo = theObj.GetType().GetTypeInfo();
PropertyInfo theProp = thisTypeInfo.GetDeclaredProperty(entAttrName); ******
if (theProp.PropertyType.ToString() == "System.String")
{
    theProp.SetValue(theObj, (String)entAttrValue);
}
else if (theProp.PropertyType.ToString().Contains("System.Int32"))
{
    theProp.SetValue(theObj, int.Parse(entAttrValue));
} else if...

I cannot figure out for the life of my why Child()...

class Child : Person
{
    [Key]
    [Column(Order = 0)]
    public int Id { get; set; }
    public string sChildFoo { get; set; }
    public int iChildBar { get; set; }
    public double dChildBaz { get; set; }
}

...properties go well through that line, but any of the properties from Person()...

public abstract class Person
{
    [Key]
    public int PersonId { get; set; }
    public int PersonAge { get; set; }
    public int PersonWeight { get; set; }
    public string PersonName { get; set; }
}

...fail. Here is the full log of the program, showing where it halts. The exception is snapshotted at the original post:

2016-11-08 15:03:12.9049 INFO Starting at 3:03:12 PM
2016-11-08 15:03:12.9801 INFO Created .Name: Qeququ Qequququ
2016-11-08 15:03:12.9838 INFO Created .sParentFoo: Kakikikiki
2016-11-08 15:03:13.9918 INFO wb.WorkSheets count: 2
2016-11-08 15:03:14.0007 INFO ws.Rows count: 3
2016-11-08 15:03:14.0007 INFO dt.Rows.Count: 2
2016-11-08 15:03:14.0007 INFO dt.Name: Child
2016-11-08 15:03:14.0666 INFO aClass.FullName: DynamicEFLoading.Child
2016-11-08 15:03:14.0666 INFO Creating 'dbs' object...
2016-11-08 15:03:14.0891 INFO GetType: DynamicEFLoading.Child
2016-11-08 15:03:14.0963 INFO ================= row ==================================
2016-11-08 15:03:14.0963 INFO ================= col 0 
2016-11-08 15:03:14.1105 INFO [0] Item: sChildFoo
2016-11-08 15:03:14.1105 INFO [0] Value: Norwich
2016-11-08 15:03:14.1105 INFO theProp.DeclaringType.FullName of attr: DynamicEFLoading.Child
2016-11-08 15:03:14.1265 INFO theProp.GetSetMethod(true).ToString() of attr: Void set_sChildFoo(System.String)
2016-11-08 15:03:14.1265 INFO theProp.GetType().ToString() of attr: System.Reflection.RuntimePropertyInfo
2016-11-08 15:03:14.1265 INFO theProp.Name of attr: sChildFoo
2016-11-08 15:03:14.1424 INFO theProp.PropertyType.ToString() of attr: System.String
2016-11-08 15:03:14.1424 INFO theProp.ReflectedType.ToString() of attr: DynamicEFLoading.Child
2016-11-08 15:03:14.1424 INFO theProp.ReflectedType.ToString() of attr: System.Void
2016-11-08 15:03:14.1557 DEBUG Set System.String value: Norwich
2016-11-08 15:03:14.1557 INFO ================= col 1 
2016-11-08 15:03:14.1557 INFO [1] Item: iChildBar
2016-11-08 15:03:14.1780 INFO [1] Value: 29884
2016-11-08 15:03:14.1780 INFO theProp.DeclaringType.FullName of attr: DynamicEFLoading.Child
2016-11-08 15:03:14.1919 INFO theProp.GetSetMethod(true).ToString() of attr: Void set_iChildBar(Int32)
2016-11-08 15:03:14.2113 INFO theProp.GetType().ToString() of attr: System.Reflection.RuntimePropertyInfo
2016-11-08 15:03:14.2113 INFO theProp.Name of attr: iChildBar
2016-11-08 15:03:14.2233 INFO theProp.PropertyType.ToString() of attr: System.Int32
2016-11-08 15:03:14.2368 INFO theProp.ReflectedType.ToString() of attr: DynamicEFLoading.Child
2016-11-08 15:03:14.2368 INFO theProp.ReflectedType.ToString() of attr: System.Void
2016-11-08 15:03:14.2607 DEBUG Set System.Int32 value: 29884
2016-11-08 15:03:14.2657 INFO ================= col 2 
2016-11-08 15:03:14.2851 INFO [2] Item: dChildBaz
2016-11-08 15:03:14.2978 INFO [2] Value: 1.2
2016-11-08 15:03:14.3184 INFO theProp.DeclaringType.FullName of attr: DynamicEFLoading.Child
2016-11-08 15:03:14.3305 INFO theProp.GetSetMethod(true).ToString() of attr: Void set_dChildBaz(Double)
2016-11-08 15:03:14.3305 INFO theProp.GetType().ToString() of attr: System.Reflection.RuntimePropertyInfo
2016-11-08 15:03:14.3457 INFO theProp.Name of attr: dChildBaz
2016-11-08 15:03:14.3682 INFO theProp.PropertyType.ToString() of attr: System.Double
2016-11-08 15:03:14.3910 INFO theProp.ReflectedType.ToString() of attr: DynamicEFLoading.Child
2016-11-08 15:03:14.4098 INFO theProp.ReflectedType.ToString() of attr: System.Void
2016-11-08 15:03:14.4098 DEBUG Set System.Double value: 1.2
2016-11-08 15:03:14.4098 INFO ================= col 3 
2016-11-08 15:03:14.4382 INFO [3] Item: PersonAge
2016-11-08 15:03:14.4609 INFO [3] Value: 34

For a full, working example-with this failure-see the original post here .

===========================================================================

thisTypeInfo.GetDeclaredProperty gets properties declared by the type itself, not its supertype. Use thisTypeInfo.GetProperty . Or just

theObj.GetType().GetProperty(entAttrName);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM