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.