SqlDbType.Xml converter converting from but not to

Posts   
 
    
Posts: 27
Joined: 16-Jul-2007
# Posted on: 05-May-2009 01:53:30   

Initially implemented as XmlFragment converter but broke it down to a kludged document converter when I ran into the save problem.

Both implementations load and present the entity property as xml object but neither call ConvertTo to get the string to stick back in the SqlDbType.Xml column.

Any ideas? I am sure I am just having a brain fart.

   public class XmlDocumentStringConverter:TypeConverter
    {
        ///<summary>
        ///</summary>
        public XmlDocumentStringConverter()
        {
        }

        
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            switch(sourceType.FullName)
            {
                case "System.String":
                    return true;
                default:
                    return false;
            }
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            switch(destinationType.FullName)
            {
                case "System.String":
                    return true;
                default:
                    return false;
            }
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            XmlDocument toReturn;

            switch(value.GetType().FullName)
            {
                case "System.DBNull":
                    return null;
                    break;
                case "System.String":
                    var doc = new XmlDocument();
                    //// need to wrap as xml column can contain fragment.
                    doc.LoadXml("<wrapper>" + value + "</wrapper>");
                    toReturn = doc;
                    break;
                default:
                    throw new NotSupportedException("Conversion from a value of type '" + value.GetType() + "' to System.String isn't supported");
            }

            return toReturn;
        }

        
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if(value==null)
            {
                return System.DBNull.Value;
            }

            if (!(value is XmlDocument))
            {
                throw new ArgumentException("Value isn't of type XmlDocument", "value");
            }
            return ((XmlDocument)value).OuterXml;
        }


        public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
        {
            var doc = new XmlDocument();
            return doc;
            
            
        }
    }
Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 05-May-2009 10:47:19   

Which LLBLGen Pro runtime library are you using?

Posts: 27
Joined: 16-Jul-2007
# Posted on: 05-May-2009 16:37:25   

Walaa wrote:

Which LLBLGen Pro runtime library are you using?

DQE 2.6.8.1114 OSC 2.6.9.305

My real question is - does the code look right? Anyone?

I have written typeconverters that have worked fine in the past but I may be having a brainfart on this one.

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 05-May-2009 17:27:41   

Looks ok to me. Does ConvertTo not get called at all..?

Posts: 27
Joined: 16-Jul-2007
# Posted on: 05-May-2009 23:25:35   

MTrinder wrote:

Looks ok to me. Does ConvertTo not get called at all..?

nope. converter is used because convert from gets called on load.

this leads me to think that IF the converter code is ok that the runtime is disregarding the xml field on save.

frans will have the exact location and behavior of this section of code inscribed on the back of his eyelid but if i don't hear from him soon i guess I will just go digging. ;p

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 06-May-2009 08:13:45   

Fields that doesn't change, will be ignored at Save. So make sure the yourEntity.Fields["yourField"].IsChanged is set to true.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 06-May-2009 13:43:38   

Walaa's suggestion is to check whether the field's Ischanged flag is set to true, not actively set it to true in every case, because that's unnecessary.

A field might not be considered 'changed' because it's set to the same value. This might happen in your case perhaps.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 27
Joined: 16-Jul-2007
# Posted on: 07-May-2009 10:43:44   

Otis wrote:

Walaa's suggestion is to check whether the field's Ischanged flag is set to true, not actively set it to true in every case, because that's unnecessary.

A field might not be considered 'changed' because it's set to the same value. This might happen in your case perhaps.

Well, I made it a point to set new data - confirmed the bucket contents are different, and no joy.

I will try explicitly setting the field dirty and see if that works. If so I have a workaround and you have a bug. Ebbybody is happy.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 07-May-2009 18:19:52   

sky.sanders wrote:

Otis wrote:

Walaa's suggestion is to check whether the field's Ischanged flag is set to true, not actively set it to true in every case, because that's unnecessary.

A field might not be considered 'changed' because it's set to the same value. This might happen in your case perhaps.

Well, I made it a point to set new data - confirmed the bucket contents are different, and no joy.

I will try explicitly setting the field dirty and see if that works. If so I have a workaround and you have a bug. Ebbybody is happy.

I'm not happy if a bug is found the code. Though the title of the thread suggests something with a typeconverter which has nothing to do with dirty/nondirty fields (as typeconverters are ran when the query is produced/fetch occurs)

The problem is that the field is of type XmlDocument. So the check whether the field is changed, is a value compare, and it simply compares XmlDocument instances. If you change the contents INSIDE the xmlDocument, it's not seen as a change, the object the xml is in is compared, which is the same. So to mark the field as 'changed' you have to either set it to a different xmldocument, or explicitly flag it as changed. The runtime has code to compare byte array contents but not to compare xmldocument contents unfortunately. It does call Equals, but XmlDocument doesn't override Equals to do an inner xml compare unfortunately...

Frans Bouma | Lead developer LLBLGen Pro
Posts: 27
Joined: 16-Jul-2007
# Posted on: 09-May-2009 00:36:19   

r.e. Happy: I was being facetious.

As usual you are correct.

Thanks for taking the time to point out something that should have been obvious to me. Naive/lazy mistake.

So Walaa's suggestion to set the field changed is right on.

Options for those trying same:

1) modify xml field and then explicitely set field changed before saving.

entity.Fields["XmlField"].IsChanged = true;

2) modify the xml field and then clone it to itself before saving. (or vice-versa)

entity.XmlField = (XmlDocument)entity.XmlField.Clone();

Problem solved. Thanks.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 09-May-2009 10:45:46   

Glad it's solved, Sky simple_smile

Frans Bouma | Lead developer LLBLGen Pro