I've created a data class that I plan to use to send data to be persisted in the database and to return data from the database in a strongly typed way. In addition to its properties, the class contains a Dictionary that I populate in the constructor with the name of and reference to each property. This makes the properties enumerable and enables me to iterate through them using 'foreach'.
This works great when setting property values and sending the object to be persisted in the database. I can iterate through the Dictionary keys, get the value of each property, and add a SqlParameter for each property using the key as the parameter name and the property value as the parameter value.
However, going the other way doesn't work. I can iterate through the Dictionary keys and get the value of each column in each row of the SqlDataReader, but when I try to assign these values to my data object using the Dictionary's reference to the corresponding object property, a curious thing occurs. The assignments succeed, BUT the data object properties all retain their initial, default values. I can view the data object properties and see these initial, default values. I can also view the Dictionary entry values and see the updated values that were read and assigned from the SqlDataReader.
This makes no sense. The Dictionary is supposed to provide access to each property (the 'object' generic type) via its key (the 'string' generic type), but its acting like its maintaining a separate copy of each Dictionary 'KeyValuePair'.
What gives?
I'm doing all this in C# in the context of an ASP.NET Core 2.1.1 project running on macOS 10.13.6 High Sierra.
I've searched StackOverflow extensively, and I see lots of recommendations for using reflection to do this type of thing. I'll refactor my code to use reflection if necessary, but I'd really like to understand where and how my mental model for what's happening is off.
An explanation of what's happening and why would be MOST appreciated.
Example Data Class with Property Dictionary
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using Newtonsoft.Json;
namespace MyOrg.MyProj.Data
{
[DataContract]
public class DataObj
{
#region Attributes
[Required]
[DataMember(Name = "dataObjectId")]
public Int64 DataObjectId { get; set; }
[Required]
[DataMember(Name = "guid")]
public Guid Guid { get; set; }
public virtual Dictionary<string, object> DataMembers { get; set; } //NOTE: Implements the IEnumerable interface in order to support 'foreach' operations, etc on 'DataObj' class attributes
#endregion Attributes
#region Constructors
public DataObj(Int64 dataObjectId, Guid guid)
{
try
{
DataObjectId = dataObjectId;
Guid = guid;
DataMembers = new Dictionary<string, object>
{
{ "DataObjectId", DataObjectId },
{ "Guid", Guid }
};
}
catch (Exception e)
{
Console.WriteLine($"RUNTIME EXCEPTION while INSTANTIATEing DataObj, " + e.Message + ", " + e.StackTrace);
}
}
#endregion Constructors
#region Methods
/// <summary>
/// Implements the IEnumerable interface in order to support 'foreach' operations, etc on 'DataObj' class attributes
/// </summary>
/// <returns>Enumerator</returns>
public Dictionary<string, object>.Enumerator Enumerator()
{
return DataMembers.GetEnumerator(); //NOTE: Return the Dictionary object's IEnumerator rather than implementing IEnumerable for the 'DataObj' class itself
}
#endregion Methods
Example Data Access Class (excerpt)
reader = command.ExecuteReader();
dataObjList = new List<DataObj>();
if (reader.HasRows)
{
while (reader.Read())
{
tempDataObj = new DataObj(-1, new Guid("00000000-0000-0000-0000-000000000000"));
keys = new List<String>(tempDataObj.DataMembers.Keys); //NOTE: Can't modify a Dictionary while iterating through it. See the 'Why This Error?' section of https://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute
foreach (String key in keys)
{
tempDataObj.DataMembers[key] = reader[key];
}
dataObjList.Add(tempDataObj);
For 'key' = 'DataObjectId', 'Guid', etc, I expect the value of tempDataObj.DataObjectId, tempDataObj.Guid, etc to be set to the value returned from the database in 'reader[key]'.
Instead, it retains its initial, default value as set in the constructor, i.e. '-1'. This is true for both value and reference data types.
However, when I inspect tempDataObj.DataMembers["DataObjectId"], it has been set to the value returned from the database in 'reader[key]'.
Inspecting the Object Property and Dictionary Values
tempDataObj.DataMembers["DataObjectId"] should be referencing the tempDataObj.DataObjectId property, etc, but the Dictionary appears to be maintaining its own value rather than providing an object reference to the 'DataObjectId' property.
What's going on here? Thank you!