I've hit a very interesting exception while trying to serialize a graph of EF 4 STEs.
System.IndexOutOfRangeException was caught
Message=Index was outside the bounds of the array.
Source=mscorlib
StackTrace:
Server stack trace:
at System.Runtime.Serialization.ObjectReferenceStack.EnsureSetAsIsReference(Object obj)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.OnHandleIsReference(XmlWriterDelegator xmlWriter, DataContract contract, Object obj)
at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
...
at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(Stream stream, Object graph)
My serialization code is fairly simple:
using (MemoryStream memoryStream = new MemoryStream())
{
DataContractSerializer dc = new DataContractSerializer(data.GetType());
dc.WriteObject(memoryStream, data);
memoryStream.Flush();
memoryStream.Position = 0;
StreamReader reader = new StreamReader(memoryStream);
var serializedObject = reader.ReadToEnd();
}
In my object graph, I've added a few child entities to a parent entity, and I've discovered that if I call the .AcceptChanges() extension method on the parent, everything serializes just fine.
Has anyone else encountered something like this? What could be causing it? Any ideas on how I can run down the culprit?
Update: I found a link where someone else had a similar problem. They said that System.Runtime.Serialization.ObjectReferenceStack.EnsureSetAsIsReference) is doing some cycle validation and could be finding a problem.
Update 2: I also found that setting preserveObjectReferences to true in the constructor for the DataContractSerializer clears up the exception.
Update 3: Ended up using approach described in this article to call overloaded DataContractSerializer constructor with preserveObjectReferences set to true. This fixed the issue, although I still can't explain it...
So maybe now, my question becomes: How is preserveObjectReferences on the DataContractSerializer different than having [DataContract(IsReference = true)] on all of the STE's?
Thanks!
It seems PreserveObjectReferences uses "non-standard XML constructs" for all your classes, while isReference is the standard SOAP way, but it needs to be declared on every class where it is needed. I had the same problem and it was because I had missed putting it on some classes.
The common trap is that DataContractAttribute is not inherited, so you have to redeclare it (with IsReference=true) for each inherited class.
Related
Microsoft discourages the use of BinaryFormatter because it poses security problems. See: BinaryFormatter Obsoletion Strategy.
I have a .NET 6.0 WinForms code which uses the Microsoft.Office.Interop.Access.Dao interop assembly. I need it to insert an image into the Data field the Microsoft Access' system table MSysResources. This field has an Attachment Data Type. This is a multi-valued field. Using DAO is the only way of writing to this field. My (somewhat shortened) code goes like this (note: this code did work before I migrated to .NET 6.0):
using Microsoft.Office.Interop.Access.Dao;
namespace CySoft.RibbonPro.Services;
public class AccessImageResourceLoader : IAccessImageResourceLoader
{
public void UpdateImages(string accdbFile, IEnumerable<KeyValuePair<string, Image>> images)
{
var dbe = new DBEngine(); // <====== This line throws the UnsupportedException =====
Database db = dbe.OpenDatabase(accdbFile);
Recordset rs = rs = db.OpenRecordset("SELECT * FROM MSysResources WHERE 0=1", R
ecordsetTypeEnum.dbOpenDynaset, 0, LockTypeEnum.dbOptimistic);
rs.AddNew();
rs.Fields["Type"].Value = "img";
rs.Fields["Name"].Value = name;
rs.Fields["Extension"].Value = ext;
Recordset2 rsAttachment = (Recordset2)rs.Fields["Data"].Value;
rsAttachment.AddNew();
Field2 dataField = (Field2)rsAttachment.Fields["FileData"];
dataField.LoadFromFile(imageInfo.Key);
rsAttachment.Update();
rs.Update();
rs.Close();
db.Close();
}
}
The details are for illustration only. The first code line creating the DBEngine throws the exception:
BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.
The call stack is:
at System.ComponentModel.Design.DesigntimeLicenseContextSerializer.DeserializeUsingBinaryFormatter(StreamWrapper wrappedStream, String cryptoKey, RuntimeLicenseContext context)
at System.ComponentModel.Design.DesigntimeLicenseContextSerializer.Deserialize(Stream o, String cryptoKey, RuntimeLicenseContext context)
at System.ComponentModel.Design.RuntimeLicenseContext.GetSavedLicenseKey(Type type, Assembly resourceAssembly)
at System.ComponentModel.LicenseManager.LicenseInteropHelper.GetCurrentContextInfo(Type type, Boolean& isDesignTime, String& key)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Internal.Runtime.InteropServices.LicenseInteropProxy.GetCurrentContextInfo(RuntimeTypeHandle rth, Boolean& isDesignTime, IntPtr& bstrKey)
at CySoft.RibbonPro.Services.AccessImageResourceLoader.UpdateImages(String accdbFile, IEnumerable`1 images) in C:\Users\Oli\Documents\Proj\CySoft\CySoft.RibbonPro\CySoft.RibbonPro\Services\AccessImageResourceLoader.cs:line 21
Where AccessImageResourceLoader.cs:line 21 is var dbe = new DBEngine();
Microsoft wants people to use another type of serialization like JSON or XML. This is not an option in this case, because I am not using it directly. It is Microsoft's own COM library which uses it.
Question:
How can I insert or update a record using Access' Attachment data type in .NET 6+?
My Attempts
I have tried to do it with System.Data.OleDb. I can read the Attachment with OleDb. But any attempt to write to this field using OleDb throws an exception.
Setting the <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization> tag in the project file does not help.
Settings the same configuration property in runtimeConfig.template.json does not help either.
I know that I could solve the problem by using Access automtation via an interop assembly. But it has the disadvantage to open the Microsoft Access application. Inserting the image through a database connection is much more elegant and did work before I migrated to .NET 6.0.
You can see here there is a switch to allow the binary serializer for the licenses file
https://github.com/dotnet/runtime/blob/main/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs#L20
which is being read by the GetSavedLicenseKey method here
https://github.com/dotnet/runtime/blob/main/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs#L84-L89
You can set this switch earlier on before initializing the DBEngine object by calling this:
AppContext.SetSwitch("System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization", true);
I haven't tried it myself but it should work.
This runtime switch might also be settable in the csproj file as described here
https://github.com/dotnet/runtime/blob/main/docs/workflow/trimming/feature-switches.md
Any feature-switch which defines property can be set in csproj file or on the command line as any other MSBuild property. Those without predefined property name the value can be set with following XML tag in csproj file.
<RuntimeHostConfigurationOption Include="<AppContext-Setting>"
Value="false"
Trim="true" />
Final words: There is even more detail on upgrading to .NET 6.0 at this blog which has another method for this flag explained.
https://www.textcontrol.com/blog/2021/12/21/migrate-a-windows-forms-desktop-application-to-dotnet-6/?hmsr=joyk.com&utm_source=joyk.com&utm_medium=referral
I'm trying to send a protobuf message to a child process. Deserialization works fine in the parent, like this:
var deserialized = Serializer.Deserialize(typeof(ChildProcessTestMessage), new MemoryStream(new byte[] { 8, 1 }));
[ProtoContract]
public class ChildProcessTestMessage
{
[ProtoMember(1)]
public int Int { get; set; }
}
I have hardcoded the bytes 0x0801 in this sample code and in the child process in order to make sure that the bytes are correct. I obtained these bytes by serializing a test instance.
Deserialization code in the child is different in one important way: The parent communicates the type to use for deserialization over a pipe. The type can be loaded successfully in the child. I then call protobuf-net like this:
var (assemblyName, typeName, methodName, argument) = protocol.ReadExecuteMethod();
Assembly assembly;
try
{
var name = new AssemblyName(assemblyName);
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(Directory.GetCurrentDirectory(), name.Name + ".dll"));
}
catch (Exception ex)
{
//...
}
var type = assembly.GetType(typeName);
var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
var parameterType = method.GetParameters().Single().ParameterType;
var message = Serializer.Deserialize(new MemoryStream(new byte[] { 8, 1 }), parameterType);
The child reports the following error:
Failed to invoke method 'TestMethod': ProtoBuf.ProtoException: Invalid
wire-type (Varint); this usually means you have over-written a file
without truncating or setting the length; see
Using Protobuf-net, I suddenly got an exception about an unknown wire-type at
ProtoBuf.ProtoReader.State.ThrowProtoException(String message) in
//src/protobuf-net.Core/ProtoReader.State.ReadMethods.cs:line 764
at ProtoBuf.ProtoReader.State.ThrowWireTypeException() in
//src/protobuf-net.Core/ProtoReader.State.ReadMethods.cs:line 758
at
ProtoBuf.Internal.PrimaryTypeProvider.ProtoBuf.Serializers.ISerializer<System.Type>.Read(State&
state, Type value) in
//src/protobuf-net.Core/Internal/PrimaryTypeProvider.Primitives.cs:line
292 at
ProtoBuf.ProtoReader.State.g__ReadFieldOne|102_0[T](State&
state, SerializerFeatures features, T value, ISerializer1 serializer) in /_/src/protobuf-net.Core/ProtoReader.State.ReadMethods.cs:line 1075 at ProtoBuf.ProtoReader.State.ReadAsRoot[T](T value, ISerializer1
serializer) in
//src/protobuf-net.Core/ProtoReader.State.ReadMethods.cs:line 1059
at ProtoBuf.ProtoReader.State.DeserializeRoot[T](T value,
ISerializer`1 serializer) in
//src/protobuf-net.Core/ProtoReader.State.ReadMethods.cs:line 1036
at ProtoBuf.Serializer.Deserialize[T](Stream source, T value, Object
userState, Int64 length) in
//src/protobuf-net/Serializer.Deserialize.cs:line 43 at
ChildProcess.Program.Main(String[] args) in ...\Program.cs:line 324
I have added debug output to make sure that parameterType == typeof(ChildProcessTestMessage).
I'm currently at a loss at why it's not working and how to debug this. There must be a difference in the child process somehow but I have no lead what it could be.
One idea was that maybe assembly loading is different. Could this manifest as this kind of failure?
This is on .NET Core 5.
I'd appreciate your help.
Update: I have now hardcoded a copy of that proto contract into the child. Everything coming over the wire is now ignored. The deserialization call looks like in the parent. Same error as before...
The child process exe is a project in this solution. I'm invoking it from a unit test project, and it lives in the same directory as the unit test DLL and current directory. Could this mess up the child? There are lots of files there which the child maybe didn't expect.
OK, this was a pretty nasty but interesting bug. I kept bisecting by trying to remove differences between the parent and the child. So I kept hardcoding more deserializing code pasting it as the first line in Main. That worked, yet at a later point in the program the (seemingly!) same code didn't.
When I brought the working and the non-working code closer and closer, until the lines were adjacent, I finally spotted the issue:
When you say Serializer.Deserialize(type, stream) it picks a different overload than when you say Serializer.Deserialize(stream, type). Both will compile, but the latter will use type as the "prototype" object. This will cause the library to try to deserialize stream as a System.Type instance. This is a system type that cannot be deserialized. But protobuf-net attempted to do so anyway finding that the serialized bytes and the type structure are incompatible.
So in the end, all of this was caused by switched argument order. It had nothing to do with being in a child process.
I received the following error on the deployed system:
See the end of this message for details on invoking
just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text **************
Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: S. Path '', line 0, position 0.
at Newtonsoft.Json.JsonTextReader.ParseValue()
at Newtonsoft.Json.JsonTextReader.ReadInternal()
at Newtonsoft.Json.JsonTextReader.Read()
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean hasConverter)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at JsonWrapper.DeserializeJson[T](String data, ITraceWriter tracer)
This is the simplified JsonWrapper.DerserializeJson code from the DLL:
public static T DeserializeJson<T>(string data, ITraceWriter tracer) {
try {
return JsonConvert.DeserializeObject<T>(data, new JsonSerializerSettings { TraceWriter = tracer });
} catch (JsonReaderException jrex) {
throw new CustomException("Unable to deserialize Json message.", jrex);
}
}
I know why the exception is being thrown, however I am unable to figure out why it is not being caught by the try-catch block.
This code is a DLL that is being consumed by one of our clients. I am hoping this is a fix can be done solely in the DLL.
Any insight into this would be great!
Your JsonException might be sourced in another assembly.
If your version of Newtonsoft is different from the version in the other assembly, the specific catch block won't catch it (a generic Exception catch will).
The solution is to use the same version of Newtonsoft.
This was mentioned by dbc in the comments.
I did find out that I was not catching the correct CustomException in my implementing methods; once fixed, I have not been able to get the JIT error again.
Still haven't figured out why JsonReaderException was shown as a JIT error and not my CustomException.
I am trying to create a new build definition using TFS 2013 API. The process template that I have to refer contains several custom activities and parameters. While creating the Build Definition, some of the Property value needs to be updated dynamically. So I tried to Deserialize the process parameters using below code:
IDictionary<string, object> processParams = WorkflowHelpers.DeserializeProcessParameters(defaultTemplate.Parameters);
This code always throwing below exception:
An unhandled exception of type 'System.Xaml.XamlObjectWriterException' occurred in System.Xaml.dll
Additional information: No matching constructor found on type 'System.Activities.Activity'. You can use the Arguments or FactoryMethod directives to construct this type.
This is really frustrating and I can't get rid of this error yet.
I have also tried to deserialize the process parameters using below code:
using (StringReader stringReader = new StringReader(parameterValues))
{
object obj = XamlServices.Load(
ActivityXamlServices.CreateReader(
new XamlXmlReader((TextReader)stringReader, new XamlXmlReaderSettings { LocalAssembly = System.Reflection.Assembly.GetExecutingAssembly() }
)));
}
This works but when I serialize it again using XamlServices.Save(XamlWriter writer, object instance) method, the parameters got changed which is not compatible with build workflow.
So, how can I update the build process parameters here ? Can the WorkflowHelpers class can be used in other ways ? Or is there any other way to update the process parameters so that it gets reflected in the build definition. Any help is highly appreciated.
This is working fine now.
Below is new code:
IDictionary<string, object> processParams = WorkflowHelpers.DeserializeProcessParameters(defaultTemplate.ProcessParameters);
Instead of defaultTemplate.Parameters, I need to pass defaultTemplate.ProcessParameters
In an app I am developing, I found a problem when serializing a custom class.It's a class derived from a Generic Tree class, so let's call it DerivedFromTree. The Tree class is in another assembly.
[Serializable]
class DerivedFromTree : Tree<UnderlyingTreeType>
{
...
}
To isolate my problem I created a new console project, added the DerivedFromTree files and its dependencies and added a reference to the DLL in which Tree resides. Then added the following code:
DerivedFromTree dft = new DerivedFromTree("label");
UnderlyingTreeType utt = new UnderlyingTreeType(...);
dft.AddChild(utt);
utt = new UnderlyingTreeType(...);
dft.AddChild(utt);
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, dft);
ms.Position = 0; // position stream to 0
DerivedFromTree dft_result = (DerivedFromTree)bf.Deserialize(ms);
After running this code in the sample project, dft_result contains a valid DerivedFromTree with its children. However, if I put the same code in my original application, I get an exception when calling bf.Serialize:
{System.Runtime.Serialization.SerializationException: Type 'MyAssembly.Tree`1[[UnderlyingTreeType, Assembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' from assembly 'Assembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
in System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
in System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
in System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
in System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
in System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
in System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
in System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
in System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
in Class.Function() in C:\...\FileName.cs:line XXX}
Any idea about what could be happening there?
Update: The classes Tree, DerivedFromTree, UnderlyingTreeType and all its dependencies are marked as [Serializable]. Failing to do so would result in the small console project throwing an exception. If I remove the [Serializable] attribute from Tree class, the console project throws the exact same error as the original app.
Perhaps your object has delegate fields which may or may not throw an exception when being serialized.
Make sure you mark delegates nonserializable:
[Serializable]
public class MyClass
{
[NonSerialized]
EventHandler m_MyEvent;
}
For events, you should specify field attribute qualifier when applying NonSerialized:
[Serializable]
public class MyPublisher
{
[field:NonSerialized]
public event EventHandler MyEvent;
}
Don't forget that SerializibleAttribute has it's IsInherited property set to false. If your code looked exactly as posted above (without decorating DerivedFromTree by [Serializible]) it would not work...
Request you to go through the section of
Steps in the Serialization Process
Serialization Guidelines
of article Object Serialization
http://msdn.microsoft.com/en-us/library/ms973893.aspx
and check if you haven't missed anything
The problem is fixed now. I removed the reference to the DLL containing the Tree class and added it again, and suddenly everything worked.
I think I'm going to delete the question, as it doesn't seem very useful...
Thank you all for your help.