2

In my WPF application, I have a DataGrid which grabs values from a database, these values are used as configuration parameters for the robotic components of a machine.

I am trying to add a feature to Import/Export the values in the grid to/from an XML Document using XML Serialization (which I have never used before) and I am running into a few issues, which I cannot comprehend for the life of me.

I have an Export() method which should take care of exporting the values to an XML Document:

public void Export()
    {
        XmlSerializer serialiser = new XmlSerializer(MasterDataGrid.ItemsSource.GetType());

        using (TextWriter writer = new StreamWriter("output.txt"))
        {
            serialiser.Serialize(writer, MasterDataGrid.ItemsSource);
            writer.Close();
        }


    }

And here is the class which defines the contents of MasterDataGrid:

[Serializable]
[XmlRoot]
public class CmcdConfigurationParameter : INotifyPropertyChanged, IDisposable
{
    [XmlElement(ElementName = "Configuration Parameter ID")]
    [Key] public Guid ConfigurationParameterID { get; set; }

    [XmlElement(ElementName = "Owning Device")]
    [ForeignKey("OwningDevice")] public Guid DeviceDefinitionID { get; set; }
    [XmlElement]
    public virtual CmcdDeviceDefinition OwningDevice { get; set; }

    [XmlElement(ElementName = "Name")]
    public string Name { get; set; }

    [XmlElement(ElementName = "Type Assembly Qualified Name")]
    public string TypeAssemblyQualifiedName { get; set; }

    private string _storedValue;
    [XmlElement(ElementName = "Stored Value")]
    public string StoredValue
    {
        get { return _storedValue;}
        set
        {
            _storedValue = value;
        }
    }

    [NotMapped]
    [XmlElement(ElementName = "Value")]
    public string Value
    {
        get { return StoredValue; }
        set { SetValue(value); }
    }


    private string _temporaryValueFromUser;
    [NotMapped]
    [XmlElement(ElementName = "Temporary Value")]
    public string TemporaryValueFromUser
    {
        get { return _temporaryValueFromUser; }
        set { _temporaryValueFromUser = value; }
    }

    public string Minimum { get; set; }
    public string Maximum { get; set; }
    public string Units { get; set; }
    public string DefaultValue { get; set; }
    public string Description { get; set; }
    public string DisplayName { get; set; }

    public EmcdConfigurationParameterType ConfigurationParameterType { get; set; }

    public virtual List<CmcdConfigurationParameterChangeLog>
         ChangeLogs
    {
        get;
        set;
    } //Need to confirm what is required to set up this relationship, is foreign key relationship needed?

    public CmcdConfigurationParameter(string strName)
    {
        ConfigurationParameterID = Guid.NewGuid();
        Name = strName;
    }

    public CmcdConfigurationParameter()
    {
    }

    private void ConfirmValueAboveMin<T>(T objNewValue)
    {
        if (Minimum != null)
        {
            var min = Convert.ChangeType(Minimum, Type.GetType(TypeAssemblyQualifiedName));

            var iCompareAgainstMinimumResult = ((IComparable) objNewValue).CompareTo((IComparable) min);

            if (iCompareAgainstMinimumResult < 0)
                throw new ArgumentException(
                    "CmcdConfigurationParameter.SetValue() failed because argument was below the minimum allowed.");
        }
    }

    private void ConfirmValueBelowMax(IComparable objNewValue)
    {
        if (Maximum != null)
        {
            var max = Convert.ChangeType(Maximum, Type.GetType(TypeAssemblyQualifiedName));

            var iCompareAgainstMaximumResult = objNewValue.CompareTo((IComparable) max);

            if (iCompareAgainstMaximumResult > 0)
                throw new ArgumentException(
                    "CmcdConfigurationParameter.SetValue() failed because argument was above the maximum allowed.");
        }
    }

    private bool IsValueSameAsExisting(string objNewValue)
    {
        if (Value != null)
        {
            var iCompareAgainstCurrentValue = ((IComparable) objNewValue).CompareTo(Value);

            if (iCompareAgainstCurrentValue == 0)
                return true;
        }

        return false;
    }

    public void SetValue(string objNewValueAsString, string changedBy = "unknown")
    {
        if (!IsValueSameAsExisting(objNewValueAsString))
        {

            if (TypeAssemblyQualifiedName == null)
            {
                StoredValue = objNewValueAsString;
                return;
            }

            try
            {
                var objNewValue = Convert.ChangeType(objNewValueAsString, Type.GetType(TypeAssemblyQualifiedName));
            }
            catch
            {
                throw new ArgumentException(string.Format(
                    "CmcdConfigurationParameter.SetValue was unable to convert the string value to its actual type. Expected type was {0}.",
                    TypeAssemblyQualifiedName));
            }

            ConfirmValueAboveMin(objNewValueAsString);
            ConfirmValueBelowMax(objNewValueAsString);


            try
            {
                if (ChangeLogs == null)
                    ChangeLogs = new List<CmcdConfigurationParameterChangeLog>();
            }
            catch (ObjectDisposedException e)
            {
                Console.WriteLine(e);
            }


            ChangeLogs?.Add(new CmcdConfigurationParameterChangeLog
            {
                ConfigurationParameterChangeLogID = Guid.NewGuid(),
                DateChanged = DateTime.Now,
                OwningConfigParameter = this,
                OldValue = Value,
                NewValue = objNewValueAsString,
                ChangedBy = changedBy
            });
            //TemporaryValueFromUser = Convert.ToString(objNewValueAsString);
            //ORIGINAL
            StoredValue = Convert.ToString(objNewValueAsString);
        }
    }

    public dynamic GetValue<T>()
    {
        if (Type.GetType(TypeAssemblyQualifiedName).IsEnum)
            return Enum.Parse(Type.GetType(TypeAssemblyQualifiedName), Value);
        return Convert.ChangeType(Value, Type.GetType(TypeAssemblyQualifiedName));
    }

    public override string ToString()
    {
        return Name;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string property = "")
    {
        var handler = PropertyChanged;
        handler?.Invoke(this, new PropertyChangedEventArgs(property));
    }

    public void Dispose()
    {

    }

    //This is required for Entity Framework to map the private property StoredValue (by default EF will not map non-public properties)
    public static readonly Expression<Func<CmcdConfigurationParameter, string>> StoredValueExpression =
        p => p.StoredValue;
}

Sorry to include the whole class. I know it is a lot, but I have absolutely no clue how to mark the class for serialization, so I figured I would post all of it so I don't accidentally leave something important out. I am new to Serialization and Attributes in general, so I think that class might be the root of the problem.

So on calling the Export() method, I get the following error:

"The type System.Data.Entity.DynamicProxies.CmcdConfigurationPar_66DFCBBA0ADF57C8F10B1E857261CD31C86C9556A2A09017262FF2C56200C356 was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."

And I can't make heads or tails of what that means. I thought maybe the long string of alphanumeric characters was a key for one of the Parameters in my database, but I couldn't find anything. I thought that maybe the serializer needed to operate on individual elements rather than a list, but I got the same error when trying to serialize a specific element of the datagrid.

I am incredibly lost here, so any guidance would be greatly appreciated.

EDIT

I think I turned off Dynamic Proxies/Lazy Loading, and now the error is different.

"The type "CmcdPickerDefinition" was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."

I can't show the full path of that class, but it is one of my classes. I have not added any attributes to that class, but I did not think that it was part of the control I am working on.

Cubemaster
  • 507
  • 1
  • 6
  • 20
  • You may need to turn off dynamic proxies, see [XML serialization errors when trying to serialize Entity Framework objects](https://stackoverflow.com/q/4912214) and [C# XmlSerializer from Entity Framework](https://stackoverflow.com/q/47497769). (You might have additional problems beyond this, your class is complex.) – dbc Oct 03 '18 at 14:38
  • Yeah, and not terribly well written. Sadly I didn't write it so I don't know the ins and outs in order to change it – Cubemaster Oct 03 '18 at 14:40
  • I fixed that error, but ran into another one, see above – Cubemaster Oct 03 '18 at 14:55
  • Then in that case one of the properties of your class (just guessing from the name that it might be `CmcdDeviceDefinition`) is referring to an object that is a subclass of the property's declared return type. To handle this see [Using XmlSerializer to serialize derived classes](https://stackoverflow.com/q/1643139/3744182). – dbc Oct 03 '18 at 20:56
  • Does this answer your question? [Use the XmlInclude or SoapInclude attribute to specify types that are not known statically](https://stackoverflow.com/questions/11886290/use-the-xmlinclude-or-soapinclude-attribute-to-specify-types-that-are-not-known) – codeMonkey Apr 15 '22 at 15:58
  • 1
    @codeMonkey Thanks for the comment! This problem occurred over 3 years ago, at my old job. I honestly don't remember if or how I ended up solving it, but hopefully your reference helps anyone who might stumble upon this question in the future. – Cubemaster Apr 15 '22 at 16:02
  • Glad you escaped that situation – codeMonkey Apr 15 '22 at 16:10

0 Answers0