I have a class called Menu. Menu has a private list variable _Sections and a public list variable sections. I am using the following pattern for lazy loading. This also allows me to retrieve child items when serializing
public class Menu
{
public int Id;
private List<MenuSection> _Sections = null;
public List<MenuSection> Sections
{
get
{
return _Sections ?? (_Sections = MenuSection.GetListByMenu(Id)); //database call
}
set
{
_Sections = value;
}
}
}
I then modify the collection client side and send it back to the API as JSON.
My problem is that the getter for Sections is called before the setter. This means that the collection is refilled from the database and then has my now updated sections appended.
I've created the following work around, but it's ugly and I don't want to have to remember to do it everywhere I want to lazy load.
public class Menu
{
public int Id;
private bool deserialized = false;
[OnDeserializing()]
internal void OnDeserializingMethod(StreamingContext context)
{
DeserializerCalled = true;
}
private List<MenuSection> _Sections = null;
public List<MenuSection> Sections
{
get
{
return _Sections ?? (_Sections = DeserializerCalled ? new List<>() : MenuSection.GetListByMenu(Id));
}
set
{
_Sections = value;
}
}
}
Am I missing some property attribute or global JSON setting that will resolve this for me?
The problem is that your getter is actually setting the variable, you are just fighting the framework. The get block should just return the value of the _Sections collection. Why not considering in using the Lazy construct introduced in C# 4 ?
private Lazy<List<MenuSection>> _someVariable = new Lazy<List<MenuSection>>(() => MenuSection.GetListByMenu(id));
public string SomeVariable => _someVariable
As a side note, I suggest to follow the right naming convention here and have the private member in camel case: _sections.
Related
I am trying to serialize a class, expected behaviour is that it succeeds. It does not succeed with error in the title. The title is a subset of the error as the full one will not fit.
Here is the full error:
System.Text.Json.JsonException HResult=0x80131500 Message=A
possible object cycle was detected which is not supported. This can
either be due to a cycle or if the object depth is larger than the
maximum allowed depth of 5.
I have a pretty simple model that I am unable to serialize and the option to skip properties using [JsonIgnore] is not viable.
The class model looks like;
Package has a property Steps which is an IList of Step
Step has a property of Constraints which is an IList of Constraint.
When I try and serialize using this code;
public static class PackageIO
{
public static void SaveAsJsonFile(Package pkg, string FullyQualifiedFileName)
{
string jsonString;
//TODO: Needs Exception handler
var options = new JsonSerializerOptions
{
WriteIndented = true,
MaxDepth = 5
};
jsonString = JsonSerializer.Serialize(pkg, options);
File.WriteAllText(FullyQualifiedFileName, jsonString);
}
}
I get the exception. This is .Net Core 3.1 and the library is not in a web app so I can't (easily) switch to the MVC Newtonsoft serializer that I see suggested sometimes.
If I remove the Constraints property above then it serializes just fine. Here is what the JSON looks like;
{
"Steps": [
{
"Name": "stepTestName"
}
],
"Name": "packageTestName"
}
Here is what the package class looks like;
public class Package
{
private string _name;
private Steps<Step> _steps;
public Package()
{
_steps = new Steps<Step>();
}
public Package(string name) : this()
{
_name = name;
}
public Steps<Step> Steps
{
get { return _steps; }
set { _steps = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
}
Here is what the Step class looks like;
public enum StepExecStatus
{
Waiting = 1,
InProgress = 2,
Inactive = 3,
Completed = 4
}
public class Step
{
private string _name;
private PrecedenceConstraints<PrecedenceConstraint> _precedenceConstraints;
private StepExecStatus _execStatus;
#region INTERNAL PROPERTIES
internal StepExecStatus ExecStatus
{
get { return _execStatus; }
set { _execStatus = value; }
}
#endregion
#region INTERNAL METHODS
internal StepExecStatus Execute()
{
return StepExecStatus.Completed;
}
#endregion
#region PUBLIC PROPERTIES
public string Name
{
get { return _name; }
set { _name = value; }
}
public PrecedenceConstraints<PrecedenceConstraint> PrecedenceConstraints
{
get { return _precedenceConstraints; }
set { _precedenceConstraints = value; }
}
#endregion
#region PUBLIC METHODS
public Step()
{
_precedenceConstraints = new PrecedenceConstraints<PrecedenceConstraint>();
_execStatus = StepExecStatus.Waiting;
}
#endregion
}
Here is what the top of the Steps collection looks like its just a basic IList implementation for now:
public class Steps<T> : IList<T> where T:Step
{
private readonly List<T> _steps = new List<T>();
Here is the constraint class;
public enum StepPrecedenceValue
{
Completion = 1,
Success = 2,
Failure = 3
}
public class PrecedenceConstraint
{
private string _sourceStepName;
private StepPrecedenceValue _constraintValue;
private bool _constraintMet;
public PrecedenceConstraint(string itemName, StepPrecedenceValue value)
{
_sourceStepName = itemName;
_constraintValue = value;
}
public string SourceStepName
{
get { return _sourceStepName; }
set { _sourceStepName = value; }
}
public StepPrecedenceValue ConstraintValue
{
get { return _constraintValue; }
set { _constraintValue = value; }
}
public bool ConstraintMet
{
get { return GetConstraintMet(); }
set { _constraintMet = value; }
}
private bool GetConstraintMet()
{
bool result = false;
//TODO: Needs implemented
return result;
}
}
And here is the Constraints class again a basic IList implementation for now;
public class PrecedenceConstraints<T> : IList<T> where T:PrecedenceConstraint
{
private readonly IList<T> _precedenceConstraints = new List<T>();
Thx
As others have commented, you will need to post your constraint/step class to really give you an exact answer, but we can be pretty certain what will be causing the issue.
Your step class will reference a constraint, which in turn will either reference the step class or reference a package. So you will have a circular reference when coming to serialize your object because as it steps through.
So your options are :
Remove the circular reference. e.g. there shouldn't be two way "navigation" properties or similar. Package should reference Step, Step should reference Constraint, and you can't go the other way.
If you absolutely need the logic in your code to be able to traverse through the objects both ways, then you can use the [JsonIgnore] attribute on the navigation properties that go in reverse so that they aren't serialized.
Finally, you can switch to using NewtonSoft serializer (As you already mentioned), as that has support for detecting looping and can break out of the loop and still serialize your model.
At this time, System.Text.Json does not have support for a mechanism for handling circular references (https://github.com/dotnet/runtime/issues/30820) because in reality, it's a bandaid over the fact your object is not readily serializable.
More info :
https://dotnetcoretutorials.com/2020/03/15/fixing-json-self-referencing-loop-exceptions/
You have encountered a couple problems here.
Firstly, you need to increase MaxDepth from 5 to 6:
var options = new JsonSerializerOptions
{
WriteIndented = true,
MaxDepth = 6 // Fixed
};
jsonString = JsonSerializer.Serialize(pkg, options);
Demo fiddle #1 here.
The JSON you are trying to serialize looks like this:
{ // Level 1
"Steps": [ // Level 2
{ // Level 3
"Name": "stepTestName",
"PrecedenceConstraints": [ // Level 4
{ // Level 5
"SourceStepName": "stepTestName", // THESE PROPERTY VALUES
"ConstraintValue": 1, // ARE APPARENTLY LEVEL 6.
"ConstraintMet": false
}
]
}
],
"Name": "packageTestName"
}
It seems as though the primitive property values in the PrecedenceConstraints objects count as an extra level. If I comment out its properties I can serialize your data model at MaxDepth = 5:
{
"Steps": [
{
"Name": "stepTestName",
"PrecedenceConstraints": [
{} // No properties so level maxes out at 5, apparently.
]
}
],
"Name": "packageTestName"
}
Demo fiddle #2 here demonstrating this. (The documentation doesn't explain the precise meaning of MaxDepth.)
Secondly, your PrecedenceConstraint lacks a public, parameterless constructor. As explained in the documentation How to migrate from Newtonsoft.Json to System.Text.Json : Deserialize to immutable classes and structs, deserialization of such types is not supported out of the box:
System.Text.Json supports only public parameterless constructors. As a workaround, you can call a constructor with parameters in a custom converter.
This prevents your data model from being deserialize successfully. One fix is to add a parameterless constructor as required by the documentation:
public class PrecedenceConstraint
{
private string _sourceStepName;
private StepPrecedenceValue _constraintValue;
private bool _constraintMet;
public PrecedenceConstraint() { } // FIXED added parameterless constructor as required by System.Text.Json
// Remainder unchanged.
Now your data model can be round-tripped at MaxDepth = 6. Demo fiddle #3 here.
Check to see whether you have awaited all your asynchronous calls.
The only time I have encountered this error was when I forgot to add await to a function call and returned the not yet executed Task from my API endpoint.
For what i have been reading, with another Class I would be able to add the indexing to the property. But i am not managing to achieve the get/set of the "Option[x]" property of my custom "Poll" class.
public class Poll
{
//Constructor
public Poll() { }
//Properties
public string Title
{
get { return title; }
set { title = value; }
}
public Options Option { get; set; }
private string title;
}
public class Options
{
string[] option = { };
public string this[int i]
{
get { return option[i]; }
set { option[i] = value; }
}
}
When i try to add the first option to the poll, it says the object ("Options") has not being instantiated. And it does make sense. But I couldn't figure out where in Poll i would instantiate it.
So, can anyone explain me what am i doing wrong? Am i following the right direction? Or point to me further reading. For the solutions I have seen, this one seemed the most logical to me, but was only a small raw example, low on details.
I didn't want to follow the dictionary (Implementing indexing "operator" in on a class in C#) way, or "Option" property returning a List of strings.
Change:
public Poll() { }
To:
public Poll() { Option = new Options(); }
Also pay attention to "Wai Ha Lee" pointed out: "the indexer will always throw an IndexOutOfRangeException because the option array is always an empty array."
What he means is that you have to replace:
string[] option = { };
With:
string[] option = new string[X]; //X is Array size
I have come across a situation where I probably needed to add properties(of a class) in a list to invoke them manually(or you can say, I need to assign there values(setter)). That is why because, I don't even know which properties is to set the values, but they are decided at runtime. So far I am trying to find out the solution here and there but still I don't get any article that even hints me a work around for this purpose.
Here's what I want to do exactly (mentioned as comments)-
public class DemoClass
{
IList<Properties> _listOfProps;
private int _iFirstProperty;
private string _iSecondProperty;
public DemoClass()
{
_listOfProps = new List<Properties>();
}
public int FirstProperty
{
get
{
return _iFirstProperty;
}
set
{
_iFirstProperty = value;
// Here I want to add this property into the list.
_listOfProps.Add(FirstProperty);
RaisePropertyChanged("FirstProperty");
}
}
public string SecondProperty
{
get
{
return _iSecondProperty;
}
set
{
_iSecondProperty = value;
RaisePropertyChanged("SecondProperty");
}
}
public void HandleChangedProperties()
{
foreach (var list in _listOfProps)
{
// Here I want to invoke the property. ie. sets the 'value' of this property.
list.Invoke(value)
}
}
}
I know, I can use Func to add in the list like- but I can't go with this.
List<Func<int>> listOfFunc = new List<Func<int>>();
listOfFunc.Add(() => { return 0; }); // Adds using lambda expression
listOfFunc.Add(temp); // Adds as a delegate invoker
private int temp()
{
return 0;
}
from MSDN
Properties can be used as if they are public data members, but they
are actually special methods called accessors.
if properties are internally methods, Why they can't be added as List of Func<>
Also, if there's no way I can do that without using Reflection (by getting PropertyInfo list), why Microsoft hasn't designed this in C#?
You can either keep a list of PropertyInfo values and later set the value of the properties using reflection, or you can keep a list of setter delegates (which effectively just forward the value to the real, hidden setter).
For example:
IList<Action<object>> listOfSetters;
listOfSetters.Add(o => this.FirstProperty = (int)o);
// and then:
listOfSetters[0](42); // FirstProperty = 42
I've been using ReSharper to do some work on cleaning up a C# codebase. I had been using both private fields in model classes along with public properties. However, I'm finding that I can simply take the properties that have no backing fields and convert them into auto-properties. These are model classes; no methods exist within them to impact the data in the objects. Is it better to just use the auto-properties?
EDIT: Including example of "Backing fields"
public class Gizmo
{
//this is what I call the "backing" field, only because it's "behind" the
//publicly-accessible property and you access it through the property
private Int32 _count;
//and this is the property, of course
public Int32 Count
{
get { return _count; }
set { _count = value; }
}
}
Is it better to just use the auto-properties
If your property involve is simple get;set, you can just use a "auto-property". If I am not wrong, compiler will create a private backing field behind the scenes.
If in your property, you are doing some kind of validation before; say before set then it makes sense to use a property with a backing field (non-auto)
An example would be
private string name;
public string MyName {
get {
return name;
}
set {
name = (value == null)
? "Anonymous" : value;
}
}
Methods are irrelevant here. If you have a property of:
private int foo;
public int Foo
{
get { return foo; }
set { foo = value; }
}
Then it absolutely makes sense to turn that into:
public int Foo { get; set; }
If any other code uses the private field, you can just change it to use the property instead.
This is pretty much a code-style question. So you should have a standard for model properties throughout your project or solution. If you find that the auto properties makes your intent more obvious, by all means use them--just use them consistently.
I need a field that can be assigned to from where ever I want, but it should be possible to assign it only once (so subsequent assignments should be ignored). How can I do this?
That would not be a readonly field then. Your only options for initializing real readonly fields are field initializer and constructor.
You could however implement a kind of readonly functionality using properties. Make your field as properties. Implement a "freeze instance" method that flipped a flag stating that no more updates to the readonly parts are allowed. Have your setters check this flag.
Keep in mind that you're giving up a compile time check for a runtime check. The compiler will tell you if you try to assign a value to a readonly field from anywhere but the declaration/constructor. With the code below you'll get an exception (or you could ignore the update - neither of which are optimal IMO).
EDIT: to avoid repeating the check you can encapsulate the readonly feature in a class.
Revised implementation could look something like this:
class ReadOnlyField<T> {
public T Value {
get { return _Value; }
set {
if (Frozen) throw new InvalidOperationException();
_Value = value;
}
}
private T _Value;
private bool Frozen;
public void Freeze() {
Frozen = true;
}
}
class Foo {
public readonly ReadOnlyField<int> FakeReadOnly = new ReadOnlyField<int>();
// forward to allow freeze of multiple fields
public void Freeze() {
FakeReadOnly.Freeze();
}
}
Then your code can do something like
var f = new Foo();
f.FakeReadOnly.Value = 42;
f.Freeze();
f.FakeReadOnly.Value = 1337;
The last statement will throw an exception.
Try the following:
class MyClass{
private int num1;
public int Num1
{
get { return num1; }
}
public MyClass()
{
num1=10;
}
}
Or maybe you mean a field that everyone can read but only the class itself can write to? In that case, use a private field with a public getter and a private setter.
private TYPE field;
public TYPE Field
{
get { return field; }
private set { field = value; }
}
or use an automatic property:
public TYPE Field { get; private set; }