Я создал класс данных, который планирую использовать для отправки данных, которые будут сохранены в базе данных, и для возврата данных из базы данных строго типизированным способом. В дополнение к своим свойствам класс содержит словарь, который я заполняю в конструкторе именем и ссылкой на каждое свойство. Это делает свойства перечисляемыми и позволяет мне перебирать их, используя 'foreach'.
Это прекрасно работает при установке значений свойств и отправке объекта в базу данных. Я могу перебирать ключи словаря, получать значение каждого свойства и добавлять SqlParameter для каждого свойства, используя ключ в качестве имени параметра и значение свойства в качестве значения параметра.
Однако, идти другим путем не работает. Я могу перебирать ключи словаря и получать значение каждого столбца в каждой строке SqlDataReader, но когда я пытаюсь присвоить эти значения моему объекту данных, используя ссылку словаря на соответствующее свойство объекта, возникает любопытная вещь. Назначения выполняются успешно, НО все свойства объекта данных сохраняют свои начальные значения по умолчанию. Я могу просмотреть свойства объекта данных и увидеть эти начальные значения по умолчанию. Я также могу просматривать значения записей словаря и видеть обновленные значения, которые были прочитаны и назначены из SqlDataReader.
Это не имеет смысла. Предполагается, что Словарь обеспечивает доступ к каждому свойству (универсальному типу 'object') через его ключ (универсальный тип 'string'), но действует так, как будто он поддерживает отдельную копию каждого словаря 'KeyValuePair'.
Что дает?
Я делаю все это на C # в контексте проекта ASP.NET Core 2.1.1, работающего на macOS 10.13.6 High Sierra.
Я интенсивно искал в StackOverflow и вижу множество рекомендаций по использованию рефлексии для такого рода вещей. Я сделаю рефакторинг своего кода, чтобы при необходимости использовать рефлексию, но мне бы очень хотелось понять, где и как отключена моя ментальная модель для происходящего.
Объяснение того, что происходит и почему это будет САМОЕ ценным.
Пример класса данных со словарем свойств
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
Пример класса доступа к данным (отрывок)
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 /523774/kollektsiya-byla-izmenena-operatsiya-perechisleniya-mozhet-ne-vypolnyatsya
foreach (String key in keys)
{
tempDataObj.DataMembers[key] = reader[key];
}
dataObjList.Add(tempDataObj);
Для 'key' = 'DataObjectId', 'Guid' и т. Д. Я ожидаю, что значение tempDataObj.DataObjectId, tempDataObj.Guid и т. Д. Будет установлено равным значению, возвращенному из базы данных в 'reader [key]'.
Вместо этого он сохраняет свое начальное значение по умолчанию, установленное в конструкторе, то есть «-1». Это верно для значений и типов справочных данных.
Однако, когда я проверяю tempDataObj.DataMembers ["DataObjectId"], для него было установлено значение, возвращаемое из базы данных в 'reader [key]'.
Проверка свойств объекта и значений словаря
tempDataObj.DataMembers ["DataObjectId"] должен ссылаться на свойство tempDataObj.DataObjectId и т. Д., Но словарь, кажется, поддерживает свое собственное значение, а не предоставляет ссылку на объект на свойство DataObjectId.
Что здесь происходит? Спасибо!