How to fix 'Remove property setter' build error? - c#

I have a property in a model which has auto property getter and setter:
[DataMember]
public Collection<DTOObjects> CollectionName { get; set; }
I get the following error when building the solution:
Microsoft.Usage : Change 'propertyname' to be read-only by removing the property setter.
However, when I remove the setter and run the code, an error occurs because it's trying to set the property! It appears it's asking me to remove the setter despite the fact it is being set somewhere in the code.
Has anyone else come accross this problem? What do I need to modify?

I'm going to guess this is a list/collection (or something similar), in which case yes - it is unusual to have a setter. A typical example might be:
private readonly List<Foo> items = new List<Foo>();
public List<Foo> Items { get { return items; } }
Most callers should not be trying to assign to that; they shouldn't need to - they can add/remove/enumerate/clear/etc the list without ever needing to assign it.
an error occurs because it's trying to set the property
Then consider changing that code so that it doesn't try to set the property. It should not need to in virtually all cases.

One solution is to initialize the Collection in the constructor...
public class Email
{
public Email()
{
To = new List<MailAddress>();
}
....
public List<MailAddress> To { get; }
}
Then just use .add in code:
Email oEmail = new Email();
oEmail.To.Add(new MailAddress("Foo#fighter.com", "Mr. Foo"));

Just suppress it, right click the error and click suppress in code & an attribute will be added to the property.
Generally you shouldn't have a public set for collections as this allows the list to be replaced, however with objects that are created or deserialized at runtime sometimes the public setter is necessary.

From the docs:
"You can suppress the warning if the property is part of a Data Transfer Object (DTO) class. Otherwise, do not suppress warnings from this rule."
If it's not part of a DTO:
"To fix a violation of this rule, make the property read-only. If the design requires it, add methods to clear and repopulate the collection."
The preferred manner of replacing a read-only collection property is to use the Clear and AddRange methods (or their equivalents).
https://learn.microsoft.com/en-us/visualstudio/code-quality/ca2227?view=vs-2019

Related

Entity Framework adding functionality to Properties

In an old WPF project I have a class with Properties like this:
private string _name = "";
public string Name
{
get { return _name; }
set
{
string cleanName = clsStringManip.CleanText(value, true);
if (cleanName != _name)
{
_name = cleanName;
}
}
}
Where every time the name changes, I ensure that the value is "cleaned". Putting it in the property ensures I never forget to clean the string before setting the property on the object.
Now I am recreating this system using MVC5 and EntityFramework6.1 using DatabaseFirst.
So all the properties are autogenerated by EF. How then can I add the equivalent CleanText function to my properties without editing the autogen code? - as I'll lose these changes next time I change my database and resync.
All I can find via Google is a way add data annotations via MetadataType and partial classes but this doesn't answer my question.
I tried to add the above code into a partial class but get the error:
The type XXX already contains a definition for Name
The only way I can think is to create a bunch of SetProperty() functions but this is dirty and you can never ensure other developers (or myself) will remember to use them.
Disclaimer: I haven't used EF 6 yet.
Let me answer this in two parts. First, I will tell you how to do this. Then I will tell you why I don't think you should do this. :-)
HOW:
As you discovered, you cannot create another Name property. You need to modify the way the EF generates the code, so that it gives you a place to insert your new code. Depending on how you are using the EF, it often generates Validate() method calls or OnPropertyChanged() calls. You may be able to do what you want inside of those methods.
If you can't do this in Validate() or OnPropertyChanged(), you could change the T4 template to generate something like this:
private string _name = "";
public string Name
{
get { return _name; }
set
{
string cleanName = value;
Cleanup_Name(ref cleanName);
if (cleanName != _name)
{
_name = cleanName;
}
}
}
private partial void Cleanup_Name(ref string);
This gives you a partial method that you can then implement as you see fit. So for any property you want to customize, you can now add another file to your project that does this:
public partial class MyEntity {
void Cleanup_Name(ref string name)
{
// Put your logic in here to fixup the name
}
}
If you do not write the above code block, then the partial method is simply a no-op. (Partial methods must return void, hence the use of a ref parameter).
WHY NOT?
The advantage of this method is that it is totally transparent to the developer. The property is just magically changed. But there are several disadvantages:
Some controls expect that if they call name = "123" that if they get the name back, it is "123" and will fail if this happens. Values are changing but no PropertyChanged event fired. If you do fire the PropertyChanged, then they sometimes change the value back. This can cause infinite loops.
There is no feedback to the user. They typed in one thing, and it looked right, but now it says something different. Some controls might show the change and others won't.
There is no feedback to the developer. The watch window will seemingly change values. And it is not obvious where to see the validation rules.
The entity-framework itself uses these methods when it loads data from the database. So if the database already contains values that don't match the cleanup rules, it will clean them when loading from the database. This can make LINQ queries misbehave depending on what logic is run on the SQL server and what logic is run in the C# code. The SQL code will see one value, the C# will see another.
You might also want to look into what the Entity-Framework's change tracking does in this case. If a property set does a cleanup while loading values from the database, does it consider that a change to the entity? Will a .Save() call write it back to the database? Could this cause code that never intended to change the database to suddenly do so?
ALTERNATIVE
Instead of doing this, I suggest creating a Validate() method that looks at each property and returns errors indicating what is wrong. You could also even create a Cleanup() method that fixes the things that are wrong. This means the cleanups are no longer transparent, so the developer must call them explicitly. But that is a good thing: the code isn't changing values without them realizing it. The person writing the business logic or the UI knows at what point the values will change, and can get a list of why.
The only way you can achieve this is by creating a new property you actually use in your application. Perhaps you can hide the original property in the designer. The actual property you use could look like this:
public string ExternalName
{
get { return Name; }
set
{
string cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
{
Name = cleanName;
}
}
}
As an alternative, you can use POCO classes:
If you want to keep using database-first, check this answer
Use code-first for an existing database, see this detailed guide
Add partial to the generated class.
Change the scope of Name in the generated class from public to internal.
Add the following in the same assembly:
public partial class classname
{
[NotMapped]
public string CleanName
{
get { return Name; }
set
{
var cleanName = clsStringManip.CleanText(value, true);
if (cleanName != Name)
Name = cleanName;
}
}
}
Caveat: you'd have to remember to do steps 1-2 every time you regenerated your POCOs ... I'd seriously consider Code First to Existing Database.
EDIT
Optionally:
Rename Name as InternalName in the generated classname; decorate it with [Column("Name")].
Rename CleanName as Name in the partial class under your control.
Caveat in 4 becomes "remember to do steps 1, 2, and 5 every time you regenerate POCOs".
This approach has the added benefit of not having to modify any of your client code (i.e., use of Name remains Name). And I'd still strongly consider Code First to Existing Database.

Add code to C# get/set of property without needing backing field?

You know how you can have a property that automatically generates a backing field? Like if I go:
public String SomeProperty {get; set;}
I know that if I want to add code to that property I have to create the backing field as so:
public string someProperty = string.Empty;
public string SomeProperty
{
get { return someProperty; }
set
{
someProperty = value;
DoSomething();
}
}
Basically, what I want to know is... is there any way to do this but without having to create the backing field? For example I could use it to trigger some kind of event that occurs when a property is set. I'm looking for something like this:
public string SomeProperty
{
get;
set { this.OnSomeEvent; }
}
But I know that'll cause a compile error because get needs do declare a body if set does.
I've researched and I cant find anything, but I thought I'd check to see if anyone knew.
I guess what I'm really after is some way to trigger an event when a property is changed but without having to add all that extra clutter. Any suggestions?
Simple answer is no, you can't have it both ways. From .NET Docs:
In C# 3.0 and later, auto-implemented properties make property-declaration more concise when no additional logic is required in the property accessors.
There are not any solutions for this built into the framework, and you cannot modify existing types via reflection (in order to add the logic at runtime). The only way to accomplish this seems to be at compile time.
There is a product http://www.postsharp.net/ that can accomplish this (intercept property/method calls), and there does appear to be a free edition.
The field keyword might be added to C#, see https://github.com/dotnet/csharplang/issues/140, which removes "when no additional logic" requirement for auto properties.
It didn't make it into C# 10 nor 11, but latest comment from compiler team says C# version 12 might have it. They release yearly, so that would be Nov 2023.

Compare value member when setting property

I have a property, of a custom class, in C# that I have overridden the setter for. I want to compare a property of/in the custom class in the setter, like the following:
public DatabaseInfo CurrentDatabaseManagedSelection
{
get { return CurrentDatabaseManaged; }
set {
if (String.Equals(value.Name, CurrentDatabaseManaged.Name,StringComparison.OrdinalIgnoreCase))
return;
CurrentDatabaseManaged = DatabaseManagement.ReadDatabase(value.FileName);
}
}
Inside the DatabaseInfo class, there is a standard String property called Name.
However, when I run the program I get the following exception. Can anyone tell me why this happens and how to solve the issue please?
Exception has been thrown by the target of an invocation.
EDIT: I do set the value of the property which the setter above is for, in the constructor of the view model. I do this simply by setting CurrentDatabaseManagedSelection equal to an object of the DatabaseInfo class.
Think I might have found the problem... Well, I've solved it!
The issue was CurrentDatabaseManaged had not been initialized and so was equal to null when I tried setting the above property. I discovered this by adding a try.. catch in the setter method, and created a new String for CurrentDatabaseManaged.Name - the stack trace pointed to that line.
Hope that helps some one else in the future.

What is difference between this.PropertyName and _PropertyName?

as I often let LinqToSql generate partial entity classes, I am wondering if my practice of adding additional properties via code is correct and if there is a better way of doing the same thing? I am also wondering what is the difference between accessing the values of other properties using this.PROPERTY_NAME vs _PROPERTY_NAME?
In my web app I keep using this.PROPERTY_NAME, but I am wondering if that is, as I already said in opening sentence, the proper approach I should be using. Also, What is _PROPERTY_NAME and when do we use it?
Example:
public partial class User
{
public bool IsThisProper {
get{
return this.SomeIntProperty == 10; // I usually use this
}
}
public bool WhenToUseThisApproach {
get{
return _SomeIntProperty == 10; // What is this in comparison to above?
}
}
}
One is the property, and the other is the private backing field in which that property stores it's value. If you want to execute whatever code the property has in it's getter/setter, then use the property, if you don't, then don't. Chances are you want to use the property, not the field, especially with setting (setting it triggers the property changed event, so about the only time to use the property is if you don't want that event raised).

Fixing .NET code generation of properties for user controls

I have a property of type IEnumerable<SomeClassIWrote> in a user control. When I use this control in a GUI, the .Designer.cs file contains the line:
theObject.TheProperty = new SomeClassIWrote[0];
Which for some reason causes a compiler warning:
Object of type 'SomeClassIWrote[]' cannot be converted to type
'System.Collections.Generic.IEnumerable`1[SomeClassIWrote]'.
Which is a mystery to me because I pass arrays as IEnumerables all the time, and the compiler has never complained.
For what it's worth, I have a default value of null specified for the property, but I got the same error before I set a default value.
How can I fix this so Visual Studio doesn't complain and ask me to ignore and continue every time I pull up the designer?
Code for the property:
[DefaultValue(null)]
public IEnumerable<SomeClassIWrote> TheProperty {
get {
return _theProperty;
}
set {
if (value == null) {
_theProperty = new SomeClassIWrote[] { };
}
else {
_theProperty = value;
}
}
}
First up, do you WANT to be able to set it in the designer?
If not, add the following attributes:
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
If you DO want to set in the designer, I'd start out by trying your class as a SomeClassIWrote[], to see if that works.
That aside, is it that important to use an IEnumerable here? As you say, you can pass arrays as IEnumerables.
I suspect there's probably some restrictions inside the designer, which wiser people than me know about...
And if you really DO want an IEnumerable property, you can expose your array as an IEnumerable, but keep your array as a designer-friendly backing field.

Categories