JSON output of lprun8 does not work properly for some cases for DumpContainer, Hyperlinq and Word
JSON output of lprun8 does not show DumpContainer and Hyperlinq for some cases at all.
Also, it does not show Hyperlinq and WordRun for some cases properly.
For the following LINQPad query:
Util.WordRun(withGaps: true, "two", "lines").Dump(); new DumpContainer("DumpContainer is displayed").Dump(); new Hyperlinq("Hyperlinq is displayed").Dump(); new Hyperlinq(() => {}, "Hyperlinq with action inside is NOT displayed").Dump(); new DumpContainer(new Hyperlinq("Hyperlinq inside a DumpContainer is displayed")).Dump(); new DumpContainer(new Hyperlinq(() => {}, "Hyperlinq with action inside a DumpContainer is NOT displayed")).Dump(); Util.OnDemand("OnDemand (which is Hyperlinq with action inside a DumpContainer) is NOT displayed", () => 5).Dump(); new { DumpContainer = new DumpContainer("DumpContainer inside something is NOT displayed") }.Dump(); new { Hyperlinq = new Hyperlinq("Hyperlinq inside something is displayed as UNDERLYING STRUCTURE") }.Dump();
this is the output:
two lines DumpContainer is displayed Hyperlinq is displayed Hyperlinq inside a DumpContainer is displayed {} { "Hyperlinq": { "Uri": "Hyperlinq inside something is displayed as UNDERLYING STRUCTURE", "Text": "Hyperlinq inside something is displayed as UNDERLYING STRUCTURE", "Query": null, "Action": null, "RunOnNewThread": false, "EditorRow": null, "EditorColumn": null, "QueryLanguage": 0, "CloseWhenComplete": false, "CssClass": "reference", "IsValid": false } }
Comments
Also, the following code when run with
lprun8
:throws a
JsonSerializationException
with messageSelf referencing loop detected for property 'dc' with type 'LINQPad.DumpContainer'. Path 'Foo.Elements[1].Content.Action.Target'.
:What are you trying to achieve here? LPRun is not about UI rendering and user interactivity.
I am aware of that LPRun is not about user interactivity; however, it displays (by default) a JSON representation of the dumped objects. LPRun should also display the text of
OnDemand
, and most of all, it should not throw any exceptions.The following code displays the text on the UI, and allows user interactivity. It should also display the text in LPRun's output (of course, in this case there is no user interactivity).
Why?
OnDemand
is about user interaction which triggers lazy evaluation.Exception was thrown by
JsonSerializer
due to cyclic reference. I don't think it will be fixed, causeOnDemand
usage in LPRun is questionable.The question still remains: what are you trying to achieve? What is the real problem you are working on?
I run my LINQPad script both in LINQPad UI and by LPRun.
It shows information which can be quickly calculated, and I want this information to be displayed both in LINQPad UI and in LPRun output.
Furthermore, if I run the script in LINQPad UI, I want to be able to click on the displayed information in order to display a more detailed, slowly calculated view.
Something like this:
Unfortunately, it does not display the description of
OnDemand
in LPRun's output.So I tried to work around this problem (hoping that eventually the original OnDemand will work as well):
I tried this workaround, which is a little bit ugly, but it throws the
JsonSerializationException
that I mentioned earlier:The following workaround works in LPRun, but its output is extremely ugly (in LINQPad UI the description is shown together with the
[...]
text, and in LPRun's output the description is shown andOnDemand
is omitted, but at least I get no exceptions):https://www.linqpad.net/lprun.aspx
The CMD symbol
topic:Yes, I know the
CMD
symbol feature. However, it is not a proper solution for me because of the following reasons.First, I would still need to use the
OnDemandWorkaround
method (although with a better implementation), and pay attention to always use it instead ofOnDemand
. This is error-prone, because if I accidentally useOnDemand
somewhere, LPRun will fail.Second, I need to create and save a textual version of some of the important dumped output both when running in LINQPad UI and in LPRun.
Unfortunately,
TextOutputWriter
is internal, so I need to use reflection here. I would prefer to use e.g.Util.CreateTextOutputWriter()
, similar to the existingUtil.CreateXhtmlWriter()
.When I call
DumpImportant
then it will dump to the output (either LINQPad UI or LPRun output) and to theMessageWriter
which will be saved later.Thus, my problem is that
TextOutputWriter
(which is used both by me directly and by LPRun to render its output) does not handleOnDemand
properly.One might think that I could use my own JSON serialization logic to save my important output, but I do not want to create a textual serializer from scratch to handle all of the cases (
WordRun
,VerticalRun
,Hyperlinq
, etc.) especially whenTextOutputWriter
already exists, it just would need to be fixed. And even if I used my own JSON serialization logic for save, I would still need to useOnDemandWorkaround
instead ofOnDemand
because LPRun demands it in order to work around the faulty operation ofTextOutputWriter
.In summary: LPRun should display the same output in textual form as LINQPad UI displays in HTML form (except images, of course); and what is even worse, LPRun currently fails with exception for some cases which is clearly a bug.
Just fix the errors and move on. I wonder why you are so in desperate need for
OnDemand
?An how LPRun should render
Lazy
andOnDemand
which requires user interaction and might be expensive to evaluate (or side-effecting)? And what's the point to use in in LPRun instead of LINQPad? Just save results as JSON, etc.Lazy an OnDemand
LINQPad sample:Thanks, but I know the behavior of
Lazy
andOnDemand
in LINQPad very well.I just have told you in great detail: the need to avoid
OnDemand
everywhere in the code, and usingOnDemandWorkaround
or some other workaround is clumsy, fragile and error-prone. I need to avoid it now, and I need to remember every time I change or extend the code to avoid it. And not just I need to avoid it, but my coworkers as well who are also editing the code.I have also elaborated on this:
OnDemand
may not just show a "Click me" text (which has purpose only when interaction is possible), but it may show a brief information which can be quickly calculated, and I want this information to be displayed both in LINQPad UI and in LPRun output.Of course, if I run the script in LINQPad UI, I want to be able to click on the displayed information in order to display a more detailed, slowly calculated view. LINQPad behaves so, which is good.
Of course, in LPRun output I cannot click on the displayed brief information, so there is no way and no need to initiate the slow calculation which would display a more detailed.
What I want is very simple and intuitive: I would like to see the same output in LPRun output as I see in LINQPad UI initially without any interactions.
And speaking of
Lazy
: LPRun currently displaysLazy
in a very wrong way by executing the slow operation and displaying an internal view of theLazy
object where you can see that althoughIsValueCreated
isfalse
, yet theValue
is calculated (but this is another, although connected topic beside theOnDemand
topic).The following script:
currently results the following LPRun output:
while the following LPRun output would be desired:
or LPRun can omit
Lazy
objects altogether (in this case I can let go of the "same output" principal, because in LPRun output a displayedLazy<Int32>
text has no important information, and it has no purpose since it cannot be clicked):E.g. the following script:
should result the following LPRun output:
My scripted can be executed in different scenarios:
- locally on our developers' machine (in which case I want to provide a much richer, interactive UI experience, hence the usage of LINQPad UI)
- on our build server (in which case I need to use LPRun, and I need to create a text output so it can be easily read in the build server log)
main.linq
cmd.linq
Or pass your scripts on build server through replace utility or Roslyn and change all calls to the
CMD
specific ones.Thanks, but I believe that this code is a little bit intrusive as it defines its own
Lazy<T>
type. It is not a good idea on its own, let alone when I am dumping objects from other assemblies which use .NET'sLazy<T>
type instead of this specialLazy<T>
type, or when someone usesLazy<T>
type by its fully qualified nameSystem.Lazy<T>
.BTW the code outputs this in LPRun:
but this would be the desired output:
Of course, this can be fixed:
There is no need to define a special
Lazy<T>
type.BTW, there remained still a lot of problems with the LPRun output that I have not talked about. E.g.
HorizontalRun
andWordRun
is not displayed nicely as text in a row whenLINQPad.ObjectGraph.IMetaGraphNodeGen.InLine
istrue
, rather it is displayed in an ugly JSON serialized from. Or e.g.Util.Highlight("world")
is not displayed asworld
, rather in an ugly JSON serialized form of the internal data structure:But in the meantime I have managed to work around all of these problems simply by defining a
static
ToDump
method, without redefining any .NET or LINQPad types, so now I am getting the desired output in LPRun. Unfortunately, the LINQPad structures I needed to use areinternal
in theLINQPad.ObjectGraph
namespace, so I had to use reflection to access them.But my main problem is that LPRun should have the desired output without any workarounds, so my only goal here is to report a bug ticket and wait for @JoeAlbahari to fix these problems.
Some of those issues should be easy to fix. The interactive elements are intended to degrade gracefully with text-based output, but it looks like some bugs have crept in.
Try 8.8.5. Most of the issues should be fixed, except for the last example, which will now generate blank output instead of throwing an exception. To fix this would require rewriting the text visitor which is quite a lot of work.
I tried 8.8.5, but it seems that it still throws
JsonSerializationException
.The following script:
results the following output in LPRun:
However, the problems (both the exception and the style problems) can be work around by using LINQPad's powerful
ToDump
feature. So by using the following code:LPRun shows the proper output:
I think that you can easily incorporate this code into
TextOutputWriter
.