Unanswered Saving data using a custom type wrapper

  • Monday, January 30, 2012 3:14 PM
     
      Has Code

    I've successfully implemented the using of a single custom data type, similar to that at the top of  http://msdn.microsoft.com/en-us/healthvault/bb968869

    I'm now trying to implement the wrapper alternative (the bottom half of the page at the above link) to allow for more than one custom type to be recorded.

    The single custom data type is along the lines of 

        public partial class MyAppCustomData : HealthRecordItem
        {
            public double A;
            public HealthServiceDateTime m_when;
    
            public MyAppCustomData() : base(new Guid("a5033c9d-08cf-4204-9bd3-cb412ce39fc0"))
            {
            }
        
            //parse and write xml not shown
        }
    

    and data is written by

                ItemTypeManager.RegisterTypeHandler(new Guid("a5033c9d-08cf-4204-9bd3-cb412ce39fc0"), typeof(MyAppCustomData), true);
                
                MyAppCustomData test1 = new MyAppCustomData
                {
                    A = 0.1,
                    m_when = new HealthServiceDateTime(System.DateTime.Now)
                };
    
                PersonInfo.SelectedRecord.NewItem(test1); 
    

    However, I'm a bit confused by how to use the wrapper for multiple custom data types.

    Are the classes defined from the base class HealthRecordItemCustomBase? Eg.

        public partial class MyAppCustomData : HealthRecordItemCustomBase
        {
            public double A;
            public HealthServiceDateTime m_when;
        }
    


    Do I still need public MyAppCustomData() : base(new Guid("a5033c9d-08cf-4204-9bd3-cb412ce39fc0")) as part of the class definition or is that now handled in the wrapper?

    The wrapper has 'get' and 'set' - how are they used?

    The single custom data item example had the xml structure set in ParseXml and WriteXml. The wrapper versions of those methods seem to cover the basic xml structure, e.g. format-appid and format-tag - is the only difference that summary needs to be passed in rather than the explicit values?

    I've seen the wrapper type described as 'fairly easy to use' - which I'm sure it would be if I knew what was going on - I'm hoping I get a 'lightbulb' moment when it all suddenly becomes clear...

All Replies

  • Monday, January 30, 2012 11:02 PM
     
     

    Here are the answers to your questions:

     

    Are the classes defined from the base class HealthRecordItemCustomBase?

    The wrapper inherits from the HealthRecordItem but the base class used for all of your custom objects does not use inheritence.

     

    Do I still need public MyAppCustomData() : base(new Guid("a5033c9d-08cf-4204-9bd3-cb412ce39fc0")) as part of the class definition or is that now handled in the wrapper?

    Yes, this is set in a constructor for the wrapper class by using the constant, ApplicationCustomTypeID in the linked example

     

    The wrapper has 'get' and 'set' - how are they used?

    They are used as expected and pass the custom data type object as indicated

     

    The single custom data item example had the xml structure set in ParseXml and WriteXml. The wrapper versions of those methods seem to cover the basic xml structure, e.g. format-appid and format-tag - is the only difference that summary needs to be passed in rather than the explicit values?

    Based on the information in the linked example, there are minor changes for both the writing and parsing XML methods. 

    From the section on the WriteXML method - First, if there's a wrapped object, you write out the actual type name, and then when it comes to writing out the custom type data, you delegate that to the wrapped object.

    From the section on the ParseXML method - The big change is that if there is a type name stored in format-tag, you create an instance of that type, find the custom data for it, and then call ParseXml() on that object. Note that it's possible that that format-tag isn't a type that you know about, so you have to deal with those cases.

     

    I agree with you that although the wrapper type can be fairly easy to use, you do need to get that "light bulb" moment, especially when using the wrapper class for multiple custom types and then the pieces will fall into

    Thank you.

    -Suzanne

  • Tuesday, January 31, 2012 11:20 AM
     
      Has Code

    Thank you very much for the help.

    Working on the (rather large!) assumption that I've got the HealthRecordItemCustomBase and CustomHealthTypeWrapper classes correctly defined, would the following be a reasonable custom data type class? (It's simply trying to record one value.)

        public partial class MyAppCustomData1 : HealthRecordItemCustomBase
        {
            private const string APPCUSTOMDATA = "appcustomdata";
            
            private double _A;
            public double A
            {
                get { return _A; }
                set { _A = value; }
            }
            
            public override void WriteXml(System.Xml.XmlWriter writer)
            {
                writer.WriteStartElement(APPCUSTOMDATA);
                {
                    writer.WriteStartElement("myappdata1");
                    {
                        writer.WriteValue(this._A);
                    }
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }
    
            public override void ParseXml(System.Xml.XPath.IXPathNavigable typeSpecificXml)
            {
                XPathNavigator navigator = typeSpecificXml.CreateNavigator().SelectSingleNode(APPCUSTOMDATA);
    
                if (navigator == null)
                {
                    throw new ArgumentNullException("null navigator");
                }
    
                XPathNavigator navigatorPhms = navigator.SelectSingleNode("myappdata1");
                if (null != navigatorPhms)
                {
                    XPathNavigator navigatorName = navigatorPhms.SelectSingleNode("myappdata1");
                    _A = navigatorName.ValueAsDouble;
                }
            }
    
        } 
    


    Actually using the class and saving the data I'm currently having rather more trouble with figuring out.

    The non-wrapper version of saving the data was along the lines of -

    ItemTypeManager.RegisterTypeHandler(new Guid("a5033c9d-08cf-4204-9bd3-cb412ce39fc0"), typeof(MyAppCustomData1), true);            
    MyAppCustomData1 test1in = new MyAppCustomData1 ();
    test1in.A = 0.1;
    PersonInfo.SelectedRecord.NewItem(test1in);
    
    

    but for the wrapper version I'm having a bit of trouble understanding "The wrapper type is fairly easy to use—instead of saving an instance of the custom item, you create a wrapper around it, and then pass the wrapper to NewItem(), or whatever method you're calling.". I.e. how do I create the wrapper? - everything I've tried so far has resulted in one or more errors. - or no errors at all when I don't think I'm actually saving the data (the last line in the codeblock above is omitted).

    On reading the data back out again,

                List<MyAppCustomData1> customItems = new List<MyAppCustomData1>();
    
                foreach (HealthRecordItem item in items)
                {
                    CustomHealthWrapper wrapper = (CustomHealthTypeWrapper)item;
                    MyCustomObject custom = wrapper.WrappedObject as MyAppCustomData1;
                    if (custom != null)
                    {
                        customItems.Add(custom);
                    }
                }
    
                AText.Text = customItems[0].A.ToString;
    

    Where do I get "items" from - on line 3 in the above codeblock.

    Also, the last line returns an error but I'm thinking it's a fairly simple syntax error I haven't corrected yet. Please let me know if it's instead a fundamental misunderstanding of what I should be doing! I'm fairly sure 'get' should be involved in there somewhere.

    Thanks.

     

  • Tuesday, January 31, 2012 1:50 PM
     
      Has Code

    Progress, or maybe not - at least the errors are in different places!

    I'm using this code to try and save a value into the custom data type -

     

            void InitData()
            {
                MyAppCustomData1 test1in = new MyAppCustomData1 ();
                test1in.A = 0.1;
    
                CustomHealthTypeWrapper myappwrapper = new CustomHealthTypeWrapper(test1in);
                PersonInfo.SelectedRecord.NewItem(myappwrapper);
            }
    

     

    This was giving me an error in the wrapper WriteXML on the line   m_when.WriteXml("when", writer);    as m_when was reported as being null. I've got around this error by changing private HealthServiceDateTime m_when;   to private HealthServiceDateTime m_when = new HealthServiceDateTime();   in CustomHealthTypeWrapper - I'm assuming this is because I'm not recording the date time for now, just a test value.

    The value is being pulled out again (assuming it was saved in the first place - I'm not sure!) by - 

     

           void RetrieveData()
            {
                HealthRecordSearcher searcher = PersonInfo.SelectedRecord.CreateSearcher();
                HealthRecordFilter filter = new HealthRecordFilter(new Guid("a5033c9d-08cf-4204-9bd3-cb412ce39fc0"));
                searcher.Filters.Add(filter);
                HealthRecordItemCollection items = searcher.GetMatchingItems()[0];
                
                List<MyAppCustomData1> customItems = new List<MyAppCustomData1>();
    
                foreach (HealthRecordItem item in items)
                {
                    CustomHealthTypeWrapper wrapper = (CustomHealthTypeWrapper)item;
                    MyAppCustomData1 custom = wrapper.WrappedObject as MyAppCustomData1;
                    if (custom != null)
                    {
                        customItems.Add(custom);
                    }
                }
    

     

    But I'm currently getting a cast error on CustomHealthTypeWrapper wrapper = (CustomHealthTypeWrapper)item;   and that's where I'm now stuck.

     


    • Edited by Jardee Tuesday, January 31, 2012 2:24 PM put not instead of now
    •  
  • Tuesday, January 31, 2012 6:08 PM
     
     

    Did you call to make sure it's been registered?

    1. ItemTypeManager.RegisterTypeHandler(
    2.         new Guid(ApplicationCustomTypeID),
    3.         typeof(CustomHealthTypeWrapper), true);

  • Wednesday, February 01, 2012 1:42 PM
     
     

    Yes, I did have the call.

    It's working now, thank you all for the help. I'm still a bit concerned about having to amend the provided code to ensure the date (m_when) was initialised, as I don't understand how it was going to work without it.

    I've not had the bolt of lightning regarding understanding it, maybe that will strike during an investigation of extending data type which I'm moving onto next!

  • Wednesday, February 01, 2012 4:44 PM
     
     
    From looking at the example, it looks like the m_when is incorrectly written there since it doesn't have context to the wrapped item.  When is a required field for any health record item.  It seems placing the When on the HealthRecordItemBase makes more sense and using that during the WriteXml portion.