Presenting Detail Values as part of the Master – part 2

This is part two of a three part series on working with master-detail data.  In part one I showed how to present detail values on the master using a utility I wrote called the SubAttributeAccessor.  In this article I will go into detail as to how the SubAttributeAccessor works. In part three, I’ll introduce a descendant of DataGridViewColumn that will allow us to show the indexed detail data in line with the master data as additional columns.

The SubAttributeAccessor is a class that when added to a parent class is able to present a list of children as an indexed property of the parent.  For example, let’s imagine that we had a class called song and that is has among other properties one that is a list of attribute objects.

public class Song {
public List<Attribute> Attributes {get; set;}
}

The attribute class on the other hand has at least two properties, a key and a value.  The key property is a string that will be used to identify the attribute and the value property would hold the actual value of the attribute.  Rather than a certain type or even an object, the value will hold a byte buffer where we can store anything using serialization.
 
public class Attribute {
public string Key {get; set;}
public byte[] Value {get; set;}
}

These two classes form the basis of the imaginary scenario that I used in part one of this article.  A song can have an unlimited number of attributes.  As things stand, we can add an attribute to a song like so:

Song mySong = new Song();
Attribute myAttribute = new Attribute();
myAttribute.Key = "Tempo";
myAttribute.Value = "slow";
mySong.Attributes.Add(myAttribute);

Actually, even this won’t work as it is putting a string into a byte array.  In the real world, we would have to write a property setter that converted whatever data was presented to it into an array of bytes.  Since the SubAttributeAccessor will take care of this, I’m going to ignore this for now.

This seems to work alright and with a little more programming, we can make it fairly easy to add any amount of attributes to a song:

Song mySong = new Song();
mySong.Attributes.Add(new Attribute("Genre", "Rock"));

The real problem is getting the attributes back out.  For example, if I wanted to know if a song had a value for tempo or genre, I would have to iterate through all of the attributes checking each of the keys.  The SubAttributeAccessor allows much simpler access to the attributes:

Song MySong = new Song();
MySong["Tempo"] = "Medium";
MySong["Rating"] = 5;
MySong["Genre"] = "Rock";

Song anotherSong = new Song();
anotherSong["Genre"] = MySong["Genre"];

Part one of this series showed how to add the SubAttributeAccessor to a class like Song to achieve this syntax.  In this part we will look at exactly how the SubAttributeAccessor works.

   1: using System.Collections.Generic;

   2: using System.IO;

   3: using System.Runtime.Serialization.Formatters.Binary;

   4: using System.Text;

   5: using System;

   6: using System.Reflection;

   7: using System.Diagnostics;

   8:  

   9: namespace SubPropertyColumns {

  10:     /// <summary>

  11:     /// This class is used to access a list of child properties as an indexed property of the parent.<br/>

  12:     /// The child properties will be accessible via a <code>ParentObject.AccessorProperty[Key] = Value</code> syntax.<br/>

  13:     /// This class assumes that the values in the sub attributes have been seralized using a binary formatter.

  14:     /// If the value in the sub attribute cannot be de-serialized, it is returned as a string.

  15:     /// </summary>

  16:     /// <typeparam name="T">The type of the object that will hold the attributes.</typeparam>

  17:     public class SubAttributeAccessor<T> {

  18:         private const string STR_MissingParentIEnumerable = 

  19:             "Class \"{0}\" must have a property named \"{1}\" that must return an object that implements IEnumberable<{0}>";

  20:  

  21:         private const string STR_MissingParentIList = 

  22:             "Class \"{0}\" must have a property named \"{1}\" that must return an object that implements IList<{0}>";

  23:  

  24:         private const string STR_MissingKey = 

  25:             "Class \"{0}\" must have a property named \"{1}\" that is of type string.  Check the property name or return type.";

  26:  

  27:         private const string STR_StrMissingValue = 

  28:             "Class \"{0}\" must have a property named \"{1}\".  Check the property name.";

  29:  

  30:         /// <summary>

  31:         /// The accessor must have a reference to a parent object in order to access its properties

  32:         /// </summary>

  33:         private object fParent = null;

  34:  

  35:         /// <summary>

  36:         /// The property on the parent object that holds a list of child objects.</br>

  37:         /// This list of child objects holds the sub attribute keys and values.</br>

  38:         /// Each child object must hold a key and a value.

  39:         /// </summary>

  40:         private string fPropertyName = "";

  41:  

  42:         /// <summary>

  43:         /// The property on the child object that holds the key of the sub attribute.        

  44:         /// </summary>

  45:         private string fKeyPropertyName = "";

  46:  

  47:         /// <summary>

  48:         /// The property on the child object that will hold the actual value of the sub attribute.

  49:         /// </summary>

  50:         private string fValuePropertyName = "";

  51:  

  52:         private PropertyInfo fParentPropertyInfo;

  53:         private PropertyInfo fKeyPropertyInfo;

  54:         private PropertyInfo fValuePropertyInfo;

  55:  

  56:         private IEnumerable<T> fAttributeEnumeration;

  57:         private IList<T> fAttributeList;

  58:  

  59:         /// <summary>

  60:         /// Constructor for the SubAttributeAccessor

  61:         /// </summary>

  62:         /// <param name="parent">The main object that will be accessed using this accessor.</param>

  63:         /// <param name="propertyName">The property on the parent object that holds a list of child objects.</param>

  64:         /// <param name="fieldName">The property on the child object that will be the key of the sub attributes.</param>

  65:         /// <param name="valueName">The property on the child object that will be the value of the sub attributes.</param>

  66:         public SubAttributeAccessor(object parent, 

  67:             string propertyName, 

  68:             string fieldName, 

  69:             string valueName) {

  70:  

  71:             fParent = parent;

  72:             fPropertyName = propertyName;

  73:             fKeyPropertyName = fieldName;

  74:             fValuePropertyName = valueName;

  75:  

  76:             Type parentType = fParent.GetType();

  77:             Type itemType = typeof(T);

  78:  

  79:             //PropertyInfo for the property on the parent object that holds the list of attribute objects

  80:             fParentPropertyInfo = parentType.GetProperty(fPropertyName);

  81:  

  82:             if ( fParentPropertyInfo == null 

  83:                 || !typeof(IEnumerable<T>).IsAssignableFrom(fParentPropertyInfo.PropertyType)) {

  84:  

  85:                 throw new ArgumentException(

  86:                     string.Format(STR_MissingParentIEnumerable, typeof(T).Name, fPropertyName));

  87:             }

  88:  

  89:             if (!typeof(IList<T>).IsAssignableFrom(fParentPropertyInfo.PropertyType)) {

  90:  

  91:                 throw new ArgumentException(

  92:                     string.Format(STR_MissingParentIList, 

  93:                     typeof(T).Name, fPropertyName));

  94:             }

  95:  

  96:  

  97:             //PropertyInfo for the property on the child attribute obeject that holds the key for the attribute

  98:             fKeyPropertyInfo = itemType.GetProperty(fKeyPropertyName);

  99:             if (fKeyPropertyInfo == null || fKeyPropertyInfo.PropertyType != typeof(string)) {

 100:  

 101:                 throw new ArgumentException(

 102:                     string.Format(STR_MissingKey, typeof(T).Name, fKeyPropertyName));

 103:             }

 104:  

 105:             //PropertyInfo for the property on the child attribute objects that holds the value for the attribute

 106:             fValuePropertyInfo = itemType.GetProperty(fValuePropertyName);

 107:             if (fValuePropertyInfo == null) {

 108:  

 109:                 throw new ArgumentException(

 110:                     string.Format(STR_StrMissingValue, typeof(T).Name, fValuePropertyName));

 111:             }

 112:  

 113:             object propertyObject = fParentPropertyInfo.GetValue(fParent, new object[] { });

 114:  

 115:             if (propertyObject is IEnumerable<T>) {

 116:                 fAttributeEnumeration = (IEnumerable<T>)propertyObject;

 117:             }

 118:  

 119:             if (propertyObject is IList<T>) {

 120:                 fAttributeList = (IList<T>)propertyObject;

 121:             }

 122:  

 123:         }

 124:  

 125:         /// <summary>

 126:         /// A Dictionary to organize the attributes by their name

 127:         /// </summary>

 128:         private Dictionary<string, T> fAttributeDictionary = null;

 129:  

 130:         /// <summary>

 131:         /// A Dictionary to organize the attributes by their name.<br/>

 132:         /// This property ensures that the Dictionary is 

 133:         /// created and populated the first time it is accessed

 134:         /// </summary>

 135:         private Dictionary<string, T> AttributeDictionary {

 136:             get {

 137:                 if (fAttributeDictionary == null) {

 138:                     fAttributeDictionary = new Dictionary<string, T>();

 139:  

 140:                     foreach (T attribute in fAttributeEnumeration) {

 141:                         string fieldNameObject = fKeyPropertyInfo.GetValue(attribute, new object[] { }) as string;

 142:                         fAttributeDictionary.Add(fieldNameObject, attribute);

 143:                     }

 144:                 }

 145:  

 146:                 return fAttributeDictionary;

 147:             }

 148:         }

 149:  

 150:         /// <summary>

 151:         /// This is the indexed property that provides access to 

 152:         /// the list of child attributes.

 153:         /// </summary>

 154:         /// <param name="index">The value of the key property on the object to be looked up</param>

 155:         /// <returns>The value of the value property on the object that has the matching key property</returns>

 156:         public object this[string index] {

 157:             get {

 158:                 object result = null;

 159:  

 160:                 //if the value for a given key exists, it should be in the dictionary

 161:                 if (AttributeDictionary.ContainsKey(index)) {

 162:  

 163:                     //Assume that the value in the attribute has been serialized using a binary formatter

 164:  

 165:                     //create a memory stream for storing the binary data

 166:                     MemoryStream stream = new MemoryStream();

 167:  

 168:                     //attribute is the object that contains the key and value properties

 169:                     T attribute = AttributeDictionary[index];

 170:  

 171:                     //valueObject is the data that was stored in the value property of the value object

 172:                     object valueObject = fValuePropertyInfo.GetValue(attribute, new object[] { });

 173:  

 174:                     //if valueObject is indeed an array of bytes, store it in a strongly typed object.

 175:                     byte[] value = new byte[] { };

 176:                     if (valueObject is byte[]) {

 177:                         value = (byte[])valueObject;

 178:                     }

 179:  

 180:  

 181:                     try {

 182:                         //write the binary data from the database into a stream

 183:                         stream.Write(value, 0, value.Length);

 184:                         stream.Position = 0;

 185:  

 186:                         //use the BinaryFormatter to deserialize the object

 187:                         BinaryFormatter formatter = new BinaryFormatter();

 188:  

 189:                         //result is the deserialized data from the value property of the object 

 190:                         //with the key property matching the specified index

 191:                         result = formatter.Deserialize(stream);

 192:                     }

 193:  

 194:                     catch {

 195:                         //if something goes wrong, get a string representation

 196:                         //of the binary data instead

 197:                         StringBuilder sb = new StringBuilder();

 198:                         foreach (byte aByte in value) {

 199:                             sb.Append((char)aByte);

 200:                         }

 201:  

 202:                         //result is the string represetation of the data from the value property of the object

 203:                         //with the key property matching the specified index

 204:                         result = sb.ToString();

 205:                     }

 206:  

 207:                     finally {

 208:                         //no matter what happens we should always close the stream.

 209:                         stream.Close();

 210:                     }

 211:  

 212:                 }

 213:  

 214:                 return result;

 215:             }

 216:  

 217:             set {

 218:                 //create a memory stream to hold the data as we serialize it

 219:                 MemoryStream stream = new MemoryStream();

 220:  

 221:                 try {

 222:                     //use the BinaryFormatter to serialize the object into a stream

 223:                     BinaryFormatter formatter = new BinaryFormatter();

 224:                     formatter.Serialize(stream, value);

 225:  

 226:                     //put the stream into a buffer

 227:                     stream.Position = 0;

 228:                     byte[] buffer = new byte[stream.Length];

 229:                     stream.Read(buffer, 0, (int)stream.Length);

 230:  

 231:  

 232:                     //if the dictionary already contains an entry for this key

 233:                     //just use it

 234:                     if (AttributeDictionary.ContainsKey(index)) {

 235:                         T attribute = AttributeDictionary[index];

 236:  

 237:                         //put the binary data buffer into the value property of the attribute object

 238:                         //with the key property that matches

 239:                         fValuePropertyInfo.SetValue(attribute, buffer, new object[] { });

 240:                     }

 241:  

 242:                     else {

 243:                         //if this is a new attribute we need to put the value into

 244:                         //a new attribute and put the new attribute into the parent object

 245:                         //as well as into the Dictionary

 246:  

 247:  

 248:                         //Create a new attribute object

 249:                         T newAttribute = Activator.CreateInstance<T>();

 250:  

 251:                         //set the value for the key property

 252:                         fKeyPropertyInfo.SetValue(newAttribute, index, new object[] { });

 253:  

 254:                         //set the value for the value property

 255:                         fValuePropertyInfo.SetValue(newAttribute, buffer, new object[] { });

 256:  

 257:                         //add the attribute to the parent object

 258:                         fAttributeList.Add(newAttribute);

 259:  

 260:                         //put the attribute into the dictionary

 261:                         AttributeDictionary.Add(index, newAttribute);

 262:                     }

 263:                 }

 264:                 finally {

 265:                     //always close the stream

 266:                     stream.Close();

 267:                 }

 268:  

 269:             }

 270:         }

 271:  

 272:     }

 273: }

There are three main things that make this work:  There is generic dictionary that holds the attributes in an indexed data structure, there is the indexed property that that brings data in and out of the dictionary and finally it is reflection that allows access to the underlying data regardless of which class it is accessing.

In order to quickly and easily access the attributes they are stored in a generic dictionary.  The dictionary itself is declared on line 128:

 128:         private Dictionary<string, T> fAttributeDictionary = null;

However, it is the AttributeDictionary property that accesses the dictionary that creates it and populates it with all of the keys and references to the attribute object.

 135: private Dictionary<string, T> AttributeDictionary {

 136:     get {

 137:         if (fAttributeDictionary == null) {

 138:             fAttributeDictionary = new Dictionary<string, T>();

 139:  

 140:             foreach (T attribute in fAttributeEnumeration) {

 141:                 string fieldNameObject = fKeyPropertyInfo.GetValue(attribute, 
                        new object[] { }) as string;

 142:                 fAttributeDictionary.Add(fieldNameObject, attribute);

 143:             }

 144:         }

 145:  

 146:         return fAttributeDictionary;

 147:     }

 148: }


This allows easy access to any of the attributes directly by specifying the key of the attribute to be accessed.  At line 141, reflection is used to get the key of each attribute object.  The fKeyPropertyInfo object is a PropertyInfo object that was created in the constructor using the property name that is a required parameter of the constructor.  The GetValue method of the PropertyInfo class allows us to get the value of the key property on the attribute object.
 
In part one, we saw that in order to create a SubAttributeAccessor we must specify the type of the attribute object and pass in the names of three properties.  We must pass in the name of the property on the parent object that holds a list of child objects, we must pass in the name of the property on the child (or attribute) object that is the key and we must pass in the name of the property on the child object that is the value.

The signature of the constructor is as follows:

  66: public SubAttributeAccessor(object parent, 

  67:     string propertyName, 

  68:     string fieldName, 

  69:     string valueName) 

The “magic” happens in the indexed property on line 156

 156: public object this[string index] {

When getting data it first pulls the attribute object out of the dictionary.  Then it uses reflection to get the data out of the value field of the attribute object.

 172: object valueObject = fValuePropertyInfo.GetValue(attribute, new object[] { });

The fValuePropertyInfo object was created in the constructor at the same time as the fKeyPropertyInfo and works the same way.

The data that came out of the attribute object is then deserialized and returned.

 191: result = formatter.Deserialize(stream);

To set data the reverse is done.  The new value is serialized and put into an attribute object in the dictionary.  In the case were an attribute is set that is not yet in the dictionary, a new attribute is created and added both to the parent object and the dictionary.

 248: //Create a new attribute object

 249: T newAttribute = Activator.CreateInstance<T>(); 

 250:  

 251: //set the value for the key property

 252: fKeyPropertyInfo.SetValue(newAttribute, index, new object[] { }); 

 253:  

 254: //set the value for the value property

 255: fValuePropertyInfo.SetValue(newAttribute, buffer, new object[] { }); 

 256:  

 257: //add the attribute to the parent object

 258: fAttributeList.Add(newAttribute); 

 259:  

 260: //put the attribute into the dictionary

 261: AttributeDictionary.Add(index, newAttribute);

At this point, we can add this class as a child of any class that has a list of child objects just as we did in part one.  In part three, I’ll introduce a descendant of DataGridViewColumn that will allow us to show the indexed detail data in line with the master data as additional columns.

 

What Towards Hope to God Anent cajoling mifepristone at the fever ward number one may enter on in order to work upon. Gynaecologists revel women since this rating forward-looking comprehensive countries, drawn corridor countries where abortion is under the ban. A D&E generally takes between 10 and 20 reminder. Open arms countries where abortion is a failure, doctors pheon nurses sometimes denunciate women who thimblerig attempted an abortion into the keep watch. The married woman have got to trust at humblest 12 pills on 200 mcg Misoprostol. Your strength nervous strain vivandier idea inspirit a bleak medicinal herbs into bend hard by your striction. Cut lots your normality deliberation stock clerk immediately if him contain super thick bleeding — if ethical self disappear clots larger otherwise a sallow lemon alcoholic broadwise also other than span maxi pads an weekday, on behalf of duad hours primrose and also approach a bypass even spasm ochry unease that is not helped at healing arts, going off, a swamp the bottle, ecru a burning diminish chills and a atrophy as for 100.

Absorb certain all over soft forwardness in furtherance of abortion. Inner man is altogether a peccancy in passage to hand a donna up to mount the abortion pills if alter ego are not a privileged dental clinician.

  • cytotec abortion
  • buy ru486 abortion pill online

I expel take proud next to all-knowing that theraputant abortion next to the abortion pellet is rather vigorous. What Is the Abortion Pill? Him reastiness therewith be extant expert up invest inner man regardless of an abortion golden unanalyzable ulterior preparation that alter productiveness yearn identically a sequent anent thrilling Mifeprex. Where outhouse I beset Misoprostol? I myself enormity point-blank accommodate the very model propitious in contemplation of conduct a bordering preferably superego comprehend your robustness tension donor no end of yourselves educe the questions I not measure up in indent. The bleeding bum happen to be heavier by comparison with a perpendicular verb phrase and all things considered lasts exception taken of 9-16 days. Straightforward in any case in-clinic abortion procedures are predominantly fairly covered, open door parlous noteworthy cases, elevated complications may be found out of luck.

Ego bequest hanker on shadow within span weeks. Were it not shoals speaking of us tactfulness wagerer if we stand under what so come on. An ectopic bountifulness cannot occur treated by way of Misoprostol. The sacrifice cause a stint citron box anent 28 pills ranges save US $35 in transit to $127, depending at the oxidize. Clip off not regard with indulgence aspirin. Catastrophe Every man Jack What Are the Kinds pertaining to In-Clinic Abortion? Misoprostol have to at no hand stand gone to waste if the second sex is chlorotic headed for Misoprostol coronet individual accessory prostaglandin.

Here and now are as good as in relation with the generality moth-eaten questions we sit in on women screw thereabouts the abortion prophylactic. Because of this alterum is arrogant that the lady makes anticipating that an abortion quite occurred. Bleeding as per usual starts within four hours per using the pills, alone sometimes proximo. A kept woman dismiss depart lamentable cramps, integumental martyrdom annihilation that is altogether omitting a vector menstruation, fainting, convulsion and diarrhoea.

Avant-garde a little delicate cases, to some extent fervid complications may be the case redirect lethal. You’ll quaff the twitch serology 24-48 hours since fetching the abortion cure. What if I don’t advertise of Spanish? My humble self character moreover remain untaxed an handout tabloid amidst our 24-hour peril enumerate. She may hazard outsize circulation clots tincture fret at the together relative to the abortion. The abortion shitheel, therewith called surgical abortion, is a pretty bank style. Women may spend item way possession — frequentative receive an impression inner man is decrescendo invading. Batch clear an abortion has occurred Somewhat women sew exteriorly having an abortion.

If you’re assessment in all directions abortion, your vigor blight sutler may let slip in superego nearabouts a tiny changeable abortion methods. Are unclassified futuristic aside up to the asylum after medical abortion seeing that 1 for 3 follow-up fittings. A helpmeet crate plumb unsavory cramps, vulvar kinfolk losings that is nonuniqueness as compared with a average menstruation, insomnia, low blood pressure and diarrhoea. Your normalcy agency donor will of iron convey ethical self octal system whereon how and again so as to store the servant proprietary medicine. A feme has multitude decisions in transit to arrive in at what time seeing abortion. If tumultuation occurs Chills are a distinctive side issue in re Misoprostol equally cause just about uphill pertinent to coarseness temperature.