C#9 + .NET5, nullable enabled: is it possible to use the object initialiser with non nullable properties without using null!? - .net-5

I'm testing c# 9.0 with NET 5.0 and there is something I don't understand. I enabled <nullable>enable</nullable>.
If I write a class
public class Potato {
public string Colour { get; set; }
public string Flavour { get; set; }
}
I get the warning CS8618: Non-nullable propery 'Colour' must contain a non-null value when existing constructor. Consider declaring the propery as nullable. I do NOT want to declare it nullable, that's why I enabled the tag...
Then, I declare the constructor:
public class Potato
{
public Potato(string colour, string flavour)
{
this.Colour = colour;
this.Flavour = flavour;
}
public string Colour { get; set; }
public string Flavour { get; set; }
}
So far so good. Now I want to create a Potato object using the object initialiser:
var sweetPotato = new Potato {
Colour = "orange",
Flavour = "tasty",
};
And now I get the complain CS7036: There is no argument given that corresponds to the required formal parameter 'colour' of 'Potato.Potato(string, object). So, it tries to use the constructor I explicitly declared but I didn't introduce the parameters. I should do:
var sweetPotato = new Potato(
colour: "orange",
flavour: "tasty");
but here I am not using the object initialiser.
Another option would be declaring the class as
public class Potato
{
public string Colour { get; set; } = null!;
public string Flavour { get; set; } = null!;
}
with the null! elements, but this allows some weird things like:
var sweetPotato = new Potato()
{
Colour = "orange",
};
which I think it is extremely weird. I declared 2 non nullable properties and thanks to the null! I can load the object with one property but not loading the second one. That breaks my nullable tag!
Then my question is: is it possible to use the object initialiser with non nullable properties if the nullable tag is enabled?
If it is not, I think that for classes with non nullable properties, without any null! involved, the default constructor should not be the empty one but the one with all arguments, and it should have to be possible to init an object with the object initialiser form, as long as all non-nullable properties are initialised.

I know this is an old Question but here's the comprehensive answer:
by adding the <nullable>true<nullable> tag you make it so that all nullables must be identified with ?
because of 1., and since you didn't add ? to their types, neither Colour nor Flavour can be NULL
because of 2., you must supply a constructor that initializes values for both Colour and Flavour - the default parameterless constructor won't work as it does nothing by itself and would return an object with NULL in both Colour and Flavour, as that's the default for their types, which you requested the compiler not to allow
The snippet below:
var sweetPotato = new Potato {
Colour = "orange",
Flavour = "tasty",
};
uses the default parameterless constructor and so cannot be used. It is the shorthand equivalent to:
var sweetPotato = new Potato(); // sweetPotato has Colour and Flavour NULL
sweetPotato.Colour = "orange"; // sweetPotato still has Flavour NULL
sweetPotato.Flavour = "tasty";
which as you can see would create the object first (with default values) and assign values later. But by your not allowing the default value of NULL, it fails
And this is why if you won't allow them to be nullable, you must add a constructor that initializes them, and you must use that constructor
This wouldn't apply if their types were of a primitive that has a different default - like an int or a double. In that situation, the default parameterless constructor would work. But by using types with a default value of NULL, (like string or any object derived type), you can either make them nullable by adding ? or you must use a constructor that initializes them to something other than NULL.
Late, Late Edit
You can use the object initializer still if you introduce a parameterless constructor that defaults those two properties:
public Potato()
{
Colour = "blue";
Flavor = "bitter";
}
Then you can do:
var bitterPotato = new Potato(); //blue and bitter potato
var sweetPotato = new Potato
{
Color = "orange",
Flavour = "tasty"
}; // orange and tasty potato

The default value for string is null. As your property declarations do not define a default value, they will default to default, which happens to be null in the case of string. The problem is that you have not declared the type as nullable.
The solution is to assign a default value, such as String.Empty:
public class Potato
{
public string Colour { get; set; } = String.Empty;
public string Flavour { get; set; } = String.Empty;
}

Related

Using FindAll in a List with instances from two derived classes

I wonder if the following is possible to accomplish with the FindAll-method:
I have a base class called Animal. I have two derived classes called Fish and Dog.
Animal have:
public string Type { get; set; }
public int Height { get; set; }
public int Length { get; set; }
Dog have:
public string Color { get; set; }
Fish have: nothing extra.
I want to do something like this:
Console.Write("\nEnter keyword to search the farm: ");
string keyword = Console.ReadLine().Trim();
var mySearch = myAnimals.FindAll(p => p.Type.Contains(keyword) || p.Color.Contains(keyword));
myAnimals is a List of type Animal, containing instances of Fish and Dog.
List<Animal> myAnimals = new List<Animal>();
The problem is that Color cant be accessed from there. Only what's in the base class Animal, i.e. Type, Height and Length, are possible targets.
You could use the following code:
animals.FindAll(animal => animal.Type.Contains(keyword) || (((animal as Dog)?.Color.Contains(keyword)) ?? false));
So what does the part (((animal as Dog)?.Color?.Contains(keyword)) ?? false) actually do?
Let's take it into parts:
(animal as Dog) tries to cast animal to the Type Dog. If this fails there is no exception thrown, instead it returns null
As we cannot access the property Color of null (this would lead to a NullReferenceException) we need to use the ?. Operator aka Null-conditional Operator. It will only try to access Color if (animal as Dog) is not null.
If (animal as Dog) is null the whole expression becomes null.
The same with Color?.Contains() as Color itself could be null.
As we need a boolean expression after || and null would not be boolean we also use the ?? Operator aka Null-coalescing Operator. If the expression on the left is null use the value on the right. Otherwise use the value on the left.
This means if animal is a Dog which has a Color property and this property is not null use the return value of Contains(keyword) which is true or false.
Otherwise if animal is not a Dog or Color is null default to false.
Type.Contains(keyword)|| false means whatever Type.Contains(keyword) returns.
But this becomes unflexible if you add more types. What could be another approach?
E.g. using a custom Attibute:
class Animal
{
[SearchableProperty]
public string Type { get; set;}
public string ImNotSearchable { get; set;}
}
class Fish : Animal { }
class Dog : Animal
{
[SearchableProperty]
public string Color { get; set;}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
class SearchablePropertyAttribute : Attribute
{
}
animals.FindAll(animal => animal.GetType().GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(SearchablePropertyAttribute))).Any(p => p.GetValue(animal)?.ToString()?.Contains(keyword) ?? false))
But be aware that these kind of things can be inefficient for larger datasets.

Expression.Property(param, field) is "trolling" me [System.ArgumentException] = {"Instance property 'B.Name' is not defined for type A"}

Once again, I am facing an issue, this time with LINQ Expression builder and this time I am even struggling to find the reason why it's not working. I have a Database-First EF project with quite a few tables. For this specific case, I have to use 2 of them - DocHead and Contragent. MyService.metadata.cs looks like this:
[MetadataTypeAttribute(typeof(DocHead.DocHeadMetadata))]
public partial class DocHead
{
// This class allows you to attach custom attributes to properties
// of the DocHead class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class DocHeadMetadata
{
// Metadata classes are not meant to be instantiated.
private DocHeadMetadata()
{
}
public string doc_Code { get; set; }
public string doc_Name { get; set; }
public string doc_ContrCode { get; set; }
//...
[Include]
public Contragent Contragent { get; set; }
}
}
[MetadataTypeAttribute(typeof(Contragent.ContragentMetadata))]
public partial class Contragent
{
// This class allows you to attach custom attributes to properties
// of the Contragent class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class ContragentMetadata
{
// Metadata classes are not meant to be instantiated.
private ContragentMetadata()
{
}
public string Code { get; set; }
public string Name { get; set; }
//...
I take some docHeads like this:
IQueryable<DocHead> docHeads = new MyEntities().DocHead;
Then I try to sort them like this:
docHeads = docHeads.OrderByDescending(x => x.Contragent.Name);
It is all working like I want it. I get those docHeads sorted by the name of the joined Contragent. My problem is that I will have to sort them by a field, given as a string parameter. I need to be able to write something like this:
string field = "Contragent.Name";
string linq = "docHeads = docHeads.OrderByDescending(x => x." + field + ")";
IQueryable<DocHead> result = TheBestLinqLibraryInTheWorld.PrepareLinqQueryable(linq);
Unfortunately, TheBestLinqLibraryInTheWorld does not exist (for now). So, I have set up a method as a workaround.
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(param, SortField); // normally returns x.sortField
var exp = Expression.Lambda(prop, param); // normally returns x => x.sortField
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); // normally returns sth similar to q.OrderBy(x => x.sortField)
return q.Provider.CreateQuery<T>(mce);
}
Normally... yes, when it comes to own properties of the class DocHead - those prefixed with doc_. The disaster strikes when I call this method like this:
docHeads = docHeads.OrderByField<DocHead>("Contragent.Name", true); // true - let it be Ascending order
To be more specific, the exception in the title is thrown on line 2 of the method OrderByField():
var prop = Expression.Property(param, SortField);
In My.edmx (the model), the tables DocHead and Contragent have got a relation already set up for me, which is the following: 0..1 to *.
Once again, I have no problem writing "static" queries at all. I have no problem creating "dynamic" ones via the method OrderByField(), but only when it comes to properties of the class DocHead. When I try to order by a prop of the joined Contragent class - the disaster strikes. Any help will be greatly appretiated, thank you!
The problem is that Expression.Property method does not support nested properties. It does exactly what it says - creates expression that represents a property denoted by propertyName parameter of the object denoted by the expression parameter.
Luckily it can easily be extended. You can use the following simple Split / Aggregate trick anytime you need to create a nested property access expression:
var prop = SortField.Split('.').Aggregate((Expression)param, Expression.Property);

Classes created in F# serialized incorrectly in a C# WebApi/MVC project

I created a class in FSharp like so:
type Account() = class
let mutable amount = 0m
let mutable number = 0
let mutable holder = ""
member this.Number
with get () = number
and set (value) = number <- value
member this.Holder
with get () = holder
and set (value) = holder <- value
member this.Amount
with get () = amount
and set (value) = amount <- value
end
When I reference the project from my C# WebAPI/MVC application like this
[HttpGet]
public Account Index()
{
var account = new Account();
account.Amount = 100;
account.Holder = "Homer";
account.Number = 1;
return account;
}
I am getting the following results. Note that the field name are camelCased.
<Account xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ChickenSoftware.MVCSerializationIssue">
<amount>100</amount>
<holder>Homer</holder>
<number>1</number>
</Account>
When I create a similar class in C# like this
public class NewAccount
{
public int Number { get; set; }
public String Holder { get; set; }
public int Amount { get; set; }
}
the output is Pascal cased
<NewAccount xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ChickenSoftware.MVCSerializationIssue.Api.Models">
<Amount>100</Amount>
<Holder>Homer</Holder>
<Number>1</Number>
</NewAccount>
I first thought it was the Json Serializer (Newtonsoft by default I think), but when I put a break in the controller, I see that the C# class only has its public properties exposed and that the F# class has both the Property and the backing field exposed. I tried to add a "private" keyword to the F# let statements but I got this:
Error 1 Multiple visibility attributes have been specified for this identifier. 'let' bindings in classes are always private, as are any 'let' bindings inside expressions.
So is there a way that the F# classes can treated the same as C# from Web Api?
See Mark's blog on this very issue: http://blog.ploeh.dk/2013/10/15/easy-aspnet-web-api-dtos-with-f-climutable-records/

Using abstract view model in MVC 3

I have an input form containing several input fields. Each input field has a ElementModel which has properties basically for the label and the value. The input fields to display are specified in a XML document, so I have kind of a dynamic view with only a list of elements.
The problem is, that each element should be either displayed as a decimal or as percentage value. And of course, if it's a percentage value, the user shoud be able to input something like "45%" and the value in the model should then be 0.45.
My first thought when I found this article was to use an abstract view model class with an abstract property for the value and to define a PercentageElementModel deriving from my base ElementModelclass that makes use of a custom model binder. Unfortunately, if I use that abstract base class in my view, the data annotations made in the PercentageElementModelare ignored.
Do you have any idea of how I can solve this? I don't want to use strings in my view model and do the parsing by myself as this will break the MVC pattern. Are there some other ways to achieve my goal?
Here are some code snippets:
public abstract class ElementModel
{
public string ElementName { get; set; }
public ElementType ElementType { get; set; }
public abstract double? ElementValue { get; set; }
}
public class PercentageElementModel : ElementModel
{
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:P2}")]
public override double? ElementValue { get; set; }
}
I came up with another solution as my problem is more a matter of display formatting than of validation: I wrote a custom ModelBinder which checks the input string of the text box. If it ends with a trailing '%' sign, I will devide the value by 100. Here's the code.
public class DoubleModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var modelState = new ModelState { Value = valueResult };
double? actualValue = null;
try
{
// cut trailing '%'
if (valueResult.AttemptedValue.EndsWith("%"))
{
var strValue = valueResult.AttemptedValue.Substring(0, valueResult.AttemptedValue.Length - 1);
actualValue = double.Parse(strValue, valueResult.Culture) / 100;
}
else
{
actualValue = Convert.ToDouble(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
}
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
The rest happens in the view itself. To display the correct format, I use a simple ToString() method with a percentage format string assigned if needed. And if the user enters a numeric value without '%', a jQuery's blur() event will append the '%' sign to the user's input. Perfectly works for me although this is not the best answer for my own question.

MVC Model Binding a Complex Type to a Simple Type and Vice Versa

Here's a scenario:
I have an autocomplete plugin (custom) that keeps a hidden field of JSON objects (using a specific struct).
I've created an Html helper that helps me easily bind to a specific custom model (basically, it has a JSON property that is for two-way binding and a property that lets me deserialize the JSON into the appropriate struct):
public class AutoCompleteModel {
public string JSON { get; set; }
public IEnumerable<Person> People {
get {
return new JavaScriptSerializer().Deserialize<Person>(this.JSON);
}
set {
this.JSON = new JavaScriptSerializer().Serialize(value);
}
}
}
This works great and I can model bind using the default binder #Html.Autocomplete(viewModel => viewModel.AutoCompleteModelTest). The HTML helper generates HTML like:
<input type="text" id="AutoCompleteModelTest_ac" name="AutoCompleteModelTest_ac" value="" />
<input type="hidden" id="AutoCompleteModelTest_JSON" name="AutoCompleteModelTest.JSON" value="{JSON}" />
The problem is this is not the best way for consumers. They have to manually set the People property to an array of Person structs. In my data layer, my domain objects probably will not be storing the full struct, only the person's ID (a corporate ID). The autocomplete will take care of looking up the person itself if only given an ID.
The best scenario will be to call it like this:
#Html.Autocomplete(domainObject => domainObject.PersonID) or
#Html.Autocomplete(domainObject => domainObject.ListOfPersonIDs
I would like it to work against the string property AND against the custom AutoCompleteModel. The autocompleter only updates a single hidden field, and that field name is passed back on postback (the value looks like: [{ "Id":"12345", "FullName":"A Name"},{ "Id":"12347", "FullName":"Another Name" }]).
The problem is, of course, that those domain object properties only have an ID or array of IDs, not a full Person struct (so cannot be directly serialized into JSON). In the HTML helper, I can transform those property values into a struct, but I don't know how to transform it back into a simple type on POST. The solution I need would transform an ID into a new Person struct on page load, serializing it into the hidden field. On POST, it would deserialize the generated JSON back into a simple array of IDs.
Is a custom model binder the solution I need? How can I tell it to work both with a custom model AND simple types (because I don't want it applied to EVERY string property, just need it to deal with the values given by the HTML helper).
I figured it out, it's possible!
To clarify, I needed to: transform a string or string array (of IDs) into a JSON structure for my hidden field value, then on post back, deserialize the JSON in the hidden field and transform the struct back into a simple string or string array (of IDs) for my domain object's property.
Step 1: Create a HTML helper
I had done this already, but only for accepting my custom AutoCompleteModel type. I needed one for a string and an Enumerable of string type.
All I did was generate my Person struct(s) from the value of the property and serialize them into JSON for the hidden field the Autocompleter uses (this is an example of the string helper, I also have a nearly identical one for IEnumerable<string>):
public static MvcHtmlString AutoComplete<TModel>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, string>> idProp)
where TModel : class
{
TModel model = htmlHelper.ViewData.Model;
string id = idProp.Compile().Invoke(model);
string propertyName = idProp.GetPropertyName();
Person[] people = new Person[] {
new Person() { ID = id }
};
// Don't name the textbox the same name as the property,
// otherwise the value will be whatever the textbox is,
// if you care.
MvcHtmlString textBox = htmlHelper.TextBox(propertyName + "_ac", string.Empty);
// For me, the JSON is the value I want to postback
MvcHtmlString hidden = htmlHelper.Hidden(propertyName, new JavaScriptSerializer().Serialize(people));
return MvcHtmlString.Create(
"<span class=\"AutoComplete\">" +
textBox.ToHtmlString() +
hidden.ToHtmlString() +
"</span>");
}
Usage: #Html.AutoComplete(model => model.ID)
Step 2: Create a custom model binder
The crux of my issue was that I needed this binder to only apply to certain properties, and they were strings or string arrays.
I was inspired by this article because it used Generics. I decided, hey, we can just ask people what property they want to apply the binder for.
public class AutoCompleteBinder<T> : DefaultModelBinder
where T : class
{
private IEnumerable<string> PropertyNames { get; set; }
public AutoCompleteBinder(params Expression<Func<T, object>>[] idProperties)
{
this.PropertyNames = idProperties.Select(x => x.GetPropertyName());
}
protected override object GetPropertyValue(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
var submittedValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (submittedValue != null && this.PropertyNames.Contains(propertyDescriptor.Name))
{
string json = submittedValue.AttemptedValue;
Person[] people = new JavaScriptSerializer().Deserialize<Person[]>(json);
if (people != null && people.Any())
{
string[] IDs = people.Where(x => !string.IsNullOrEmpty(x.ID)).Select(x => x.ID).ToArray();
bool isArray = bindingContext.ModelType != typeof(string) &&
(bindingContext.ModelType == typeof(string[]) ||
bindingContext.ModelType.HasInterface<IEnumerable>());
if (IDs.Count() == 1 && !isArray)
return IDs.First(); // return string
else if (IDs.Count() > 0 && isArray)
return IDs.ToArray(); // return string[]
else
return null;
}
else
{
return null;
}
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}
GetPropertyName() (translate LINQ expression into a string, i.e. m => m.ID = ID) and HasInterface() are just two utility methods I have.
Step 3: Register
Register the binder on your domain objects and their properties in Application_Start:
ModelBinders.Binders.Add(typeof(Employee), new AutoCompleteBinder<Employee>(e => e.ID, e => e.TeamIDs));
It's only a little bit annoying to have to register the binder for specific properties, but it's not the end of the world and provides a nice, smooth experience working with my autocompleter.
Any comments are welcome.

Resources