I have serializable class
[Serializable]
public class Entity
{
public Guid? SomeId {get;set;}
...
}
In DB it has SomeId == null
When restored from DB it has SomeId == Guid.Empty, but if I add emty default constructor it is restored with SomeId == null.
Can anyone explain such behaviour.
Thanks
The empty constructor is required by the serializer to create an instance of the Entity. After creating this instance, it will perform its deserialization: setting the individual properties with their serialized values. That is why the property setter must also be public.
To find out your actual problem you can start to verify whether the data has been properly serialized.
I assume you are using OR mapper software.
In that case it could also be that the OR mapper is initializing the SomeId property with a default value.
Because you use Nullable types Guid?, you add emty default construtor so that SomeId is null.
eg: if you use public bool? IsTrue { get; set; },the IsTrue property can be assigned the values true or false, or null. details Nullable types in msdn: http://msdn.microsoft.com/en-us/library/1t3y8s4s(v=vs.80).aspx
Related
Situation overview
AutoMapper provides functionality to set Collections to null if no value was assigned for it, I am looking for a similar thing but objects.
new MapperConfiguration(cfg =>
{
cfg.AllowNullCollections = true;
cfg.AddProfile(new CustomMapper());
});
Let's say I have a simple DTO
public class RequestModel
{
public string? Header { get; set; }
}
Which should map to InnerObject's Header property.
public class Document
{
public InnerObject? InnerObject { get; set; }
}
public class InnerObject{
{
public string? Header { get; set; }
}
As you can see fields are nullables which means that DTO's Header may be null.
When Header is not null - AutoMapper automatically creates dependant objects (InnerObject) and assigns correct value
When Header is null - AutoMapper still creates instance even though no value will be assigned to InnerObject.Header
CreateMap<RequestModel, Document>()
.ForPath(
dest => dest.InnerObject.Header,
opts => opts.MapFrom(src => src.Header));
Question
The question is how to configure AutoMapper not to initialize parental objects (InnerObject in our case) if children objects (Header in our case) is null.
Json example
Expected:
"innerObject": null
Actual
"innerObject": {
"header": null
}
Similar cases
Solution provided in accepted answer is not effective as it requires manual destination object creation. So if there are 100 different properties, it would be needed to write custom converts for them. Answer was posted in 2015, maybe AutoMapper introduced any updates. AutoMapper set destination to null on condition of source property
Extension method answer suggested in following thread might be a remedy for simple objects, but it would not work with complex objects when there is a need to create 3 parental objects (Ex. ParentalComplexObject.ChildrenComplexObject1.ChildrenComplexObject2.SimpleString) How do I conditionally set the destination object to null using automapper
Potential workaround
Speaking of potential workaround, some kind of utility service might be introduced to perform cleanup setting objects to null if its inner children (complex objects or properties) are null, however IMO it is not elegant solution. Set Object null if all its properties are null in C#
var x = something.Relation.Where(x => ...); // no warning here
I am wondering how to tell the compiler that a certain property will never be null, despite the compiler does not see any initialization code. Currently the compiler issues a warning:
Non-nullable property is uninitialized. Consider declaring the property as nullable
My most frequent cases are entities initialized by an ORM framework. Either, they are auto-instantiated by the ORM when the entity instance is created, or a post-processor analyzes the assembly and injects initialization code. Hence, when a new instance is created by a simple new MyEntity() certain properties of reference types are already non-null, despite the compiler doesn't see that.
Case 1: Relationships to other entities
The [Association] attribute is handled in a specific way by the ORM, ensuring properties of EntitySet<T> types are auto-initialized to actual instances of these collections.
public class MyEntity : Entity
{
// this reference will never be null, warning here
[Field]
[Association]
public EntitySet<OtherEntity> Relation { get; set; } // * the setter might be omitted
// this reference will never be null, warning here
[Field]
[Association]
public ParentEntity Parent { get; set; }
// here it is clearly annotated that this reference may be null, no warning, obviously
[Field]
[Association]
public OtherEntity? Relationship { get; set; }
}
Case 2: Nested instance (composition)
The parent type of the property's type triggers a specific handling by the ORM, ensuring the instance is auto-created:
public class MyEntity : Entity
{
// this is, in fact, an aggregation and is always initialized by the ORM, warning here
[Field]
public NestedPart SubPart { get; set; } // * the setter might be omitted
}
Currently available annotations:
One possibility is to annotate the whole property with a [NotNull] attribute. However, in that case the warning won't go away immediatelly, and it's necessary to delcare the property with a nullable reference type:
[Field]
[Association]
[NotNull]
public EntitySet<OtherEntity>? Relation { get; set; }
This is a partially acceptable solution, as both declaration-site warning is removed and when dereferencing the Relation property it won't cause a warning at use-site:
var x = something.Relation.Where(x => ...); // no warning here
However, using the ? at the same time a [NotNull] seems counter-intuitive.
Another option is to apply [NotNull] to the return of the getter. However, again the warning won't go away unless a nullable reference type is used:
[Field]
[Association]
public EntitySet<OtherEntity>? Relation { [return: NotNull] get; set; }
This mitigates the declaration-site warning, but the use-site warning remains:
var x = something.Relation.Where(x => ...); // warning here
The question is, whether there is a way to null-annotate such properties without using nullable reference types, or whether these scenarios are unsupported by the existing set of attributes and a ticket should be raised in the dotnet github repo?
I don't think there's any other way to annotate it to disable this warning. You can, however, simply disable this diagnostic for some files via editorconfig file:
[{**/Responses/*.cs,**/Requests/*.cs,**/Models**/*.cs}]
# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
dotnet_diagnostic.CS8618.severity = none
I am trying to initialize an own view model in which every property is set as null or "" but when I use
ViewModel model = new ViewModel
any property that is of type int gets set with 0 as it's value. I know I could go in and manually set each property to null or "" but that can get lengthy, what is the proper way to initialize a completely empty view model?
A property of type int cannot be null or ""
If you want to do that you should create a Nullable<int> (for null):
class ViewModel
{
public int? SomeInt{get; set;}
}
Setting an int to "" makes completely no sense
No matter which class you are using (Model or ViewModel), only set nullable fields by ? suffix after type like int. Note that System.String is a reference type and already nullable. Nullable<T> and the ? suffix are for value types such as Int32, double, DateTime, etc.
public int? name {get; set;}//as nullable field
And
public int name {get; set;}//as not nullable field
Is there ANY way to initialize a property in an Entity Framework entity that has a collection?
This is the generated code for an Entity that has a collection:
public partial class MyEntity
{
public MyEntity()
{
this.MySubEntities = new HashSet<MySubEntity>();
}
public bool IsActive {get; set;}
public virtual ICollection<MySubEntity> MySubEntities {get; set;}
}
If I need to make a new MyEntity that I want to default to IsActive = true, it cannot be done! (Unless I edit the T4 template.)
Please tell me there is a way to default IsActive = True without editing the generated file (or the T4).
Note: I have AutoMapper making the entity for me, so new MyEntity {IsActive = true} will not work.
Without editing the T4, I don't see what you can do.
So you could change your T4 to generate something like that (initialize null lists in getter instead of doing that in ctor)
private ICollection<MySubEntity> mySubEntities_;
public virtual ICollection<MySubEntity> MySubEntities {
get {
return mySubEntites_ ?? (mySubEntites_ = new HashSet<MySubEntity>());
}
set {
mySubEntities_ = value;
}
}
then you wouldn't need the empty ctor in the generated class, and could have a partial class with an empty ctor, doing what you need
public MyEntity() {
IsActive = true;
}
I'm not sure... but I will give you some directions so you can take a look.
If you set your default value on your database to IsActive = true I believe EF handles this by itself. If not, I'm pretty sure you can set the default value on the Designer.
I think you can also do another constructor because this are partial classes.
you might read this :How to set default value for POCO's in EF CF? may give you more insight.
another solution, (practical but not good and didn't answer the question), is to change IsActive to IsInactive so it defaults to false :D - I really do this when design databases.
You can edit the EF model to add default values. In the properties of a column you can find "Default value" and set it to what you want. It is default set to "(None)".
my class property has default value which will be serialize.
public class DeclaredValue
{
[XmlElement(ElementName = "Amount", DataType = "double", IsNullable = false), DefaultValue(999)]
public double Amount { get; set; }
[XmlElement(ElementName = "Reference2", DataType = "string", IsNullable = false), DefaultValue("")]
public string Reference2 { get; set; }
}
so we create instance of DeclaredValue class and provide value for Reference2 property and do not assign anything for Amount. so when we serialize the class DeclaredValue then no tag found for amount in my xml. i mention default value for amount "999" then why it does not work in serialization. i want that if do not assign anything for amount then amoun tag should be there in my xml with default value.
to do this what way i need to decorate the amount property that it always comes with default value in xml after serialization if user do not assign anything to this property.
please guide me what i need to change in the code to get my desired output.
Per the note on MSDN:
A DefaultValueAttribute will not cause
a member to be automatically
initialized with the attribute's
value. You must set the initial value
in your code.
Somewhat surprisingly the DefaultValue only regulates the writing of an object, members that are equal to their DefaultValue will not be written out.
You must still initialize members before or after loading yourself, for example in the constructor.
Let me thoroughly describe what is happening.
When XmlSerializer Deserialize() method is called, it creates a new object using a default constructor. It doesn't apply any DefaultValueAttributes to this object, I beleave, because of assumption that default ctor should "know better" how to initialize values by default. From this point of view - it is logical.
XmlSerializer doesn't serialize members which values are the same as marked by DefaultValue attribute. From some point of view such behavior is driven by logic too.
But when you do not initialize members in ctor and call deserialize method, XmlSerializer see no corresponding xml field, but it see that the field/property has DefaultValueAttribute, serializer just leave such value (according to the assumption that the default constructor knows better how to initialize a class "by defaults"). And you've got your zeros.
Solution
To initialize a class members by these DefaultValueAttributes (sometimes it is very handy to have this initialization values just in place) you can use such simple method:
public YourConstructor()
{
LoadDefaults();
}
public void LoadDefaults()
{
//Iterate through properties
foreach (var property in GetType().GetProperties())
{
//Iterate through attributes of this property
foreach (Attribute attr in property.GetCustomAttributes(true))
{
//does this property have [DefaultValueAttribute]?
if (attr is DefaultValueAttribute)
{
//So lets try to load default value to the property
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
try
{
//Is it an array?
if (property.PropertyType.IsArray)
{
//Use set value for arrays
property.SetValue(this, null, (object[])dv.Value);
}
else
{
//Use set value for.. not arrays
property.SetValue(this, dv.Value, null);
}
}
catch (Exception ex)
{
//eat it... Or maybe Debug.Writeline(ex);
}
}
}
}
}
This "public void LoadDefaults()", can be decorated as an Extension to object or use as some static method of a helper class.
As Henk Holterman mentionned, this attribut doesn't set the default value automatically. Its purpose is mostly to be used by visual designers to reset a property to its default value.
As others mentioned, the DefaultValue attribute doesn't initialize the property. You could use a simple loop to set all properties:
foreach (var property in GetType().GetProperties())
property.SetValue(this, ((DefaultValueAttribute)Attribute.GetCustomAttribute(
property, typeof(DefaultValueAttribute)))?.Value, null);
Even though ?.Value could return null, it works with non-nullable types, I tested this.
If only few of your properties have a default value, you should maybe only set the value if it is there.
If all properties should have a default value, remove the ? to get an error if you forgot one.
Most likely, arrays won't work, see MajesticRa's solution how to handle that.