Closed Vasar007 closed 3 years ago
Additional context
Note: in my code I use extension methods:
using System.Xml;
using QuikGraph;
using QuikGraph.Serialization;
public static class GraphExtensions
{
public static void SerializeToGraphML<TVertex, TEdge>(this IEdgeListGraph<TVertex, TEdge> graph,
string filePath)
where TEdge : IEdge<TVertex>
{
graph.SerializeToGraphML<TVertex, TEdge, IEdgeListGraph<TVertex, TEdge>>(filePath);
}
public static void SerializeToGraphML<TVertex, TEdge>(this IEdgeListGraph<TVertex, TEdge> graph,
XmlWriter xmlWriter)
where TEdge : IEdge<TVertex>
{
graph.SerializeToGraphML<TVertex, TEdge, IEdgeListGraph<TVertex, TEdge>>(xmlWriter);
}
}
The reason I wrote them is that SerializeToGraphML
method requires to write full list of the type arguments almost always (I have my custom vertex type) which is a bit of annoying.
So, is there any option to help compiler to infer type arguments?
(UPD: I mixed up with my configurations, so, error is still raised :))
Hello,
I was able to test the behavior your describing in a sample project under Unity 2019.4.21f1. From what I observed using both QuikGraph
2.3.0 (target net45) and QuikGraph.Serialization
2.3.0 (target net40), when I run the game using the play button and a script like the following:
TestGraph = new AdjacencyGraph<int, Edge<int>>();
TestGraph.AddVertex(1);
TestGraph.AddVertex(2);
TestGraph.AddVertex(3);
TestGraph.AddEdge(new Edge<int>(1, 2));
TestGraph.AddEdge(new Edge<int>(3, 1));
TestGraph.AddEdge(new Edge<int>(3, 2));
TestGraph.SerializeToGraphML<int, Edge<int>, AdjacencyGraph<int, Edge<int>>>("./graph.graphml");
I'm indeed able to see that target file is generated properly with the given content:
Then I tried to File > Build and Run, as you said, and I'm not really an expert of Unity so I was not able to see the callstack your joining but I noticed that the content of the file in such situation is different. If you have any help for me on the subject ;-). I'm getting the following result:
Which seems to be the result of being unable to write vertices.
Using this page as resource, even if it does not really match my Unity version. So I moved the scripting runtime version from .NET Standard 2.0 to .NET 4.x
By doing so, running again the File > Build and Run, allowed me to have a working version of the serialization. I know it sounds more like a workaround but until now I was not able to figure out why such difference. Will certainly require more knowledge on Unity ecosystem. BTW it certainly better fits for a usage of QuikGraph
.NET Framework assemblies. I also tried using .NET Standard 2.0 version of QuikGraph
assemblies but was unable to make them working 😢. I observed the same behavior as before.
Concerning the other point to reduce the number of template parameter needed, then it's indeed because those methods are heavily templated and so deduction is quite not possible. But to make it more friendly to use you can indeed move to methods with much more specific types that will feed template parameters and so allow template deduction, or at least help. Note that you can create your own graph structure inheriting from the one you're currently using in order to make it less verbose.
As an example if you use AdjacencyGraph<CampaignMapNode, Edge<CampaignMapNode>>
, you may create a class like:
public class CompaignGraph : AdjacencyGraph<CampaignMapNode, Edge<CampaignMapNode>>
{
}
And so it will certainly help in several points.
I used this code to get a stacktrace:
try
{
// Serialize graph.
GraphSerializer.SaveGraph(graph);
// Deserialize graph.
graph = GraphSerializer.LoadGraph<AdjacencyGraph<CampaignMapNode, Edge<CampaignMapNode>>>();
}
catch (Exception ex)
{
Debug.LogError(ex.ToString());
}
And then you can find stacktrace in the Unity log file.
I know it sounds more like a workaround but until now I was not able to figure out why such difference.
I prefer to stay on .NET Standard if it is possible.
As for stacktrace:
System.TypeInitializationException: The type initializer for 'WriteDelegateCompiler' threw an exception. ---> System.PlatformNotSupportedException: Operation is not supported on this platform.
at QuikGraph.Serialization.GraphMLSerializer`3+WriteDelegateCompiler[TVertex,TEdge,TGraph].CreateWriteDelegate (System.Type nodeType, System.Type delegateType) [0x00042] in <9ecb0810e6744db493d1d934627823d5>:0
at QuikGraph.Serialization.GraphMLSerializer`3+WriteDelegateCompiler[TVertex,TEdge,TGraph]..cctor () [0x00000] in <9ecb0810e6744db493d1d934627823d5>:0
It seems like exception was thrown here:
So, we can see that serializer writes footer and header but some error occurs in the WriteGraphHeader
method.
I think one of Reflection method/class in the highlighted code cannot be used on .NET Standard + Mono.
I also tried using .NET Standard 2.0 version of QuikGraph assemblies but was unable to make them working
Did you try to reproduce this issue on the Mono runtime? Because Unity uses exactly Mono runtime.
Hum so I installed Mono on Windows to make some tests. Here is my version:
& 'C:\Program Files\Mono\bin\mono.exe' --version
Mono JIT compiler version 6.12.0 (Visual Studio built mono)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
TLS: __thread
SIGSEGV: normal
Notification: Thread + polling
Architecture: amd64
Disabled: none
Misc: softdebug
Interpreter: yes
LLVM: supported, not enabled.
Suspend: preemptive
GC: sgen (concurrent by default)
Then I compiled with Visual Studio a console application (target .NET 5.0, so referencing .NET Standard 2.0 QuikGraph libraries) doing just:
var testGraph = new AdjacencyGraph<int, Edge<int>>();
testGraph.AddVertex(1);
testGraph.AddVertex(2);
testGraph.AddVertex(3);
testGraph.AddEdge(new Edge<int>(1, 2));
testGraph.AddEdge(new Edge<int>(3, 1));
testGraph.AddEdge(new Edge<int>(3, 2));
try
{
testGraph.SerializeToGraphML<int, Edge<int>, AdjacencyGraph<int, Edge<int>>>("./graph.graphml");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
And then ran the program like:
& 'C:\Program Files\Mono\bin\mono.exe' .\TestConsoleApp.dll
But it seems I don't get any issue in this case :-s
After other searches I found this issue related to Unity itself. The problem you spotted is with the usage of DynamicMethod. This API is present in .NET since a long time for .NET Framework but it's not available for .NET Standard before 2.1. In QuikGraph
there is a reference to nuget System.Reflection.Emit.Lightweight
for the .NET Standard 2.0 build. Since you only have the choice of .NET Standard 2.0 in Unity project settings I'm not sure you will be able to run it using the standard.
Note that the mentionned issue seems to maybe provide a way to workaround.
A dummy test I made is just declaring a DynamicMethod
in a script and let Unity compile it and when project is configured to use .NET 4.x I have no error, and if I keep the .NET Standard 2.0 then you get the compilation error:
This seems to simply explain the reason why depending on the project setting you use you will have error or not.
Hmm, but I use Unity 2020.3 and project compiles without any errors. There is only this serialization issue in runtime.
Somehow all works in the Debug mode and I do not know why :)
However, when I added NuGet packages System.Reflection.Emit.Lightweight
and System.Reflection.Emit.ILGeneration
I caught NotSupportedException
even in the Debug mode.
So, there is no option to fix this issue, isn't it? I should only change target framework, right?
If that is true, I think you can write about such pitfall in the documentation.
Finally, is there another way to serialize a graoh? Will XML or .NET serialization work in the same configuration as I use? I don't have enough time to check these ways right now.
The support of NET Standard in Unity is in my opinion an ongoing task which they are working on. But I'm not aware about the effort they are putting in. So I'm afraid for now you will not have other option to use .NET 4.x support to make it working in Unity.
You're right I can mention that point in the documentation to warn future users, at least for versions under or equal 2020.3.
In QuikGraph.Serialization
you will indeed find other possible serialization ways, if there are fitting your needs. I don't know if graphml serialization was a requirement for you or not.
All my following tests are using the following graph:
testGraph = new AdjacencyGraph<int, Edge<int>>();
testGraph.AddVertex(1);
testGraph.AddVertex(2);
testGraph.AddVertex(3);
testGraph.AddEdge(new Edge<int>(1, 2));
testGraph.AddEdge(new Edge<int>(3, 1));
testGraph.AddEdge(new Edge<int>(3, 2));
I tested keeping the Unity project setting to NET Standard 2.0 (File > Build and Run also tested) and used XML serialization which is working:
var settings = new XmlWriterSettings { Indent = true, IndentChars = " " };
using (XmlWriter xmlWriter = XmlWriter.Create("./graph.xml", settings))
{
testGraph.SerializeToXml(
xmlWriter,
v => v.ToString(),
testGraph.GetEdgeIdentity(),
"graph",
"vertex",
"edge",
"");
}
Or Binary serialization which is also working:
using (var writer = new FileStream("./graph.bin", FileMode.OpenOrCreate))
{
testGraph.SerializeToBinary(writer);
}
Okay, thanks! I think I can try to use XML serialization. I don't have any requirenments because I just work on my own pet project :)
So, you are free to close this issue. But I think note in the documentation could help for other users in the future.
Yep I will close this issue and make a mention in the QuikGraph
wiki/documentation.
Thanks for your feedback that has been instructive and will be helpful for others!
Describe the bug
I used
QuickGraph
in oen of my Unity project. I have a GraphML serialization logic which perfectly works in Debug mode. However, when I run project in Release mode (see steps to reproduce), serialization always fails with stacktrace:To Reproduce
Steps to reproduce the behavior:
QuickGraph
andQuikGraph.Serialization
packagesExpected behavior
Serialization will work. Also you can make a note in documentation instead that some platforms (which ones?) are not supported in the
QuikGraph.Serialization
package.