Call method/execute piece of code using Property attribute - c#

Just wondering if it's possible to call a method using property attribute. Basically, I want to set state of the entity when any of the public property changes.
Like we have following code
public class Contact : EntityBase
{
[NotifyChange]
public string FirstName { get; set; }
private void ChangeState()
{
EntityState = EntityState.Modified;
}
}
And when we call
var c = new Contact();
c.FirstName = "John";
before (or after) setting value of FirstName, it call EntityState() function.
Any idea?

It would be much simpler if property is rewritten as:
public class Contact : EntityBase
{
private string _firstName;
[NotifyChange]
public string FirstName
{
get
{
return _firstName;
}
set
{
ChangeState();
_firstName = value;
}
}
private void ChangeState()
{
EntityState = EntityState.Modified;
}
}

Try this:
public class Contact : EntityBase
{
private string _firstName;
public string FirstName
{
get {return _firstName}
set
{
_firstName = value;
EntityState(); // maybe here must by ChangeState()
}
}
private void ChangeState()
{
EntityState = EntityState.Modified;
}
}

Related

Which one is the best practice for a constructor?

I looked at Microsoft's documentation, the best practice should be the second one. But I'm still puzzled by that. I used both constructors in my program without any problems. I would like to know what exactly the difference is?
public class Person
{
// fields
private string _firstName;
private string _lastName;
// data accessor
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
// constructor
public Person(string fn, string ln)
{
_firstName = fn;
_lastName = ln;
}
}
public class Person
{
// fields
private string _firstName;
private string _lastName;
// data accessor
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
// constructor
public Person(string fn, string ln)
{
FirstName = fn;
LastName = ln;
}
}
Since the class is mutable, and both first and last name are nullable, it is actually not mandatory for the fields to be set on instantiation. So you could save some typing and just do this:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
and instantiate it this way:
var instance = new Person { FirstName = "John", LastName = "Doe" };
Personally I tend to prefer immutable classes, so I would prefer something more like this:
public class Person
{
private readonly string _firstName;
private readonly string _lastName;
public string FirstName => _firstName;
public string LastName => _lastName;
public Person (string firstName, string lastName)
{
if (firstName == null) throw new ArgumentNullException("FirstName");
if (lastName == null) throw new ArgumentNullException("LastName");
_firstName = firstName;
_lastName = lastName;
}
}
With the immutable version you can be certain that, from very the moment of instantiation, the first and last name are valid. So the instance is always valid. With your class you can't, regardless of how you set things in the constructor.
I suspect Microsoft is promoting the technique demonstrated in the 2nd example to encourage the use of the setters in the defined properties in the event they are doing anything beyond assigning the value to a member field (as mentioned by Ken White above). At the moment they are not so your puzzlement is understandable.
However, as the code grows, the properties may evolve and take on the following shape (let's assume Person is now implementing the INotifyPropertyChanged interface and utilising a service transmit changes to a peer-to-peer network service like a game):
private readonly ISomePeerToPeerService _peerService;
public Person(string fn, string ln, ISomePeerToPeerServicepeerService peerService)
{
_firstName = fn;
_lastName = ln;
_peerService = peerService;
}
public string LastName
{
get { return _lastName; }
set
{
if (Equals(_lastName, value)
return;
_lastName = value;
OnPropertyChanged()
}
private void OnPropertyChanged([CallerMemberName] string memberName = null)
{
_peerService.Update(this); // Update peers
// notify stuff
}
}
...it becomes more important to ensure that all code (including the internals of Person to invoke these properties rather than modifying the fields directly. If not, the custom logic won't be invoked.
Another example is if the person gets married:
public BankAccount SharedBankAccount { get { ... } set { ... } }
public void MarryTheGroom(Person groom)
{
LastName = groom.LastName;
SharedBankAccount = groom.PersonalAccount; // he he
}
Conclusion
It's quite similar to accidentally exposing fields publically and then client code modifies them directly rather than calling some method like TransferFundsBeteenBankAccounts(x, y)

MVVM: Raise PropertyChanged event for a DataContract class members

I want to raise PropertyChanged event for a model with DataContract.
Initially I did this
[DataContract]
public partial class User : INotifyPropertyChanged
{
[DataMember(Name="username")]
public string Username
{
get
{
return this.Username;
}
set
{
this.Username = value;
RaisePropertyChanged("Username");
}
}
}
which gave StackOverflow Exception beacause of Infinite Recursion.
So the solution I come up with is
[DataContract]
public partial class User : INotifyPropertyChanged
{
private string _Username { get; set; }
[DataMember(Name="username")]
public string Username
{
get
{
return this._Username;
}
set
{
this._Username = value;
RaisePropertyChanged("Username");
}
}
}
Although this reflects the Username value to the control binding to "Username", this doesn't look the best way to me. Something is wrong. Also my model has approx 30-40 fields. Is this the right approach or can someone please suggest me a better way.
Thanks
I'd be so tempted to use caller-member-name here (if it is in your target framework):
private string _username;
[DataMember(Name="username")]
public string Username
{
get { return _username; }
set { SetField(ref _username, value); }
}
private void SetField<T>(ref T field, T value,
[CallerMemberName] string memberName = null)
{
if(!EqualityComparer<T>.Default.Equals(field,value))
{
field = value;
RaisePropertyChanged(memberName);
}
}
If caller-member-name isn't supported:
[DataMember(Name="username")]
public string Username
{
get { return this._Username; }
set { SetField(ref _Username, value, "Username"); }
}
[DataContract]
public partial class User : INotifyPropertyChanged
{
private string _Username;
[DataMember(Name="username")]
public string Username
{
get
{
return this._Username;
}
set
{
if(this._Username != value)
{
this._Username = value;
RaisePropertyChanged("Username");
}
}
}
}

How to use MetadataTypeAttribute with extended classes

I want to add a DisplayAttribute to the Client entity (from another project), but don't want to pollute my entity with attributes specific to MVC or a UI layer. So I planned to add the DisplayAttribute by applying a metadata class to a view model inheriting from the entity
If I inherit from the Client entity and then try to use the MetadataTypeAttribute to add a display attribute, it doesn't show up in the browser. Does anyone know how I can achieve the separation and the functionality of being able to add metadata to my entities?
The Client entity class:
public class Client
{
private string firstName;
private string lastName;
private string homeTelephone;
private string workTelephone;
private string mobileTelephone;
private string emailAddress;
private string notes;
public Title Title { get; set; }
public string FirstName
{
get { return this.firstName ?? string.Empty; }
set { this.firstName = value; }
}
public string LastName
{
get { return this.lastName ?? string.Empty; }
set { this.lastName = value; }
}
public string FullName
{
get
{
List<string> nameParts = new List<string>();
if (this.Title != Title.None)
{
nameParts.Add(this.Title.ToString());
}
if (this.FirstName.Length > 0)
{
nameParts.Add(this.FirstName.ToString());
}
if (this.LastName.Length > 0)
{
nameParts.Add(this.LastName.ToString());
}
return string.Join(" ", nameParts);
}
}
public Address Address { get; set; }
public string HomeTelephone
{
get { return this.homeTelephone ?? string.Empty; }
set { this.homeTelephone = value; }
}
public string WorkTelephone
{
get { return this.workTelephone ?? string.Empty; }
set { this.workTelephone = value; }
}
public string MobileTelephone
{
get { return this.mobileTelephone ?? string.Empty; }
set { this.mobileTelephone = value; }
}
public string EmailAddress
{
get { return this.emailAddress ?? string.Empty; }
set { this.emailAddress = value; }
}
public string Notes
{
get { return this.notes ?? string.Empty; }
set { this.notes = value; }
}
public Client()
{
this.Address = new Address();
}
}
The ClientViewModel view model class:
[MetadataType(typeof(ClientMetaData))]
public class ClientViewModel : Client
{
internal class ClientMetaData
{
[Display(ResourceType = typeof(ResourceStrings), Name = "Client_FirstName_Label")]
public string FirstName { get; set; }
}
}
I think you have change the typeof parameter to:
[MetadataType(typeof(ClientViewModel.ClientMetaData))]
public class ClientViewModel : Client
{
internal class ClientMetaData
{
[Display(ResourceType = typeof(ResourceStrings), Name = "Client_FirstName_Label")]
public string FirstName { get; set; }
}
}
For .Net Core 6.0 use
[ModelMetadataType(typeof(ClientViewModel.ClientMetaData))]
insead of
[MetadataType(typeof(ClientViewModel.ClientMetaData))]

asp.net c# class properties stackoverflow exception

I have a very simple C# class:
namespace mybox
{
public class userAccount : IMyInterface
{
public userAccount()
{
}
private int _userId;
private string _userName;
public int userId
{
get { return _userId; }
set { userId = value; }
}
public string userName
{
get { return _userName; }
set { userName = value; }
}
public string list(int myUserId)
{
...
myOutPut = string.Format("{0} {1} {2}", u.userId, u.userName);
return myOutPut.ToString();
}
public void add()
{
pillboxDataContext db = new pillboxDataContext();
userAccount newUser = new userAccount();
newUser.userName = "test123";
db.SubmitChanges();
}
}
}
In my default.aspx.cs in the Page_Load event I'm trying to call the list method:
protected void Page_Load(object sender, EventArgs e)
{
pillbox.userAccount myUA = new pillbox.userAccount();
myUA.add();
// Console.WriteLine(myUA.list(1));
}
When I call the add method I can see that it is trying to assign the value test123 to the property but I get the following message:
An unhandled exception of type 'System.StackOverflowException' occurred in App_Code.1zm0trtk.dll
Any ideas of what I'm doing incorrectly?
The problem is with the way you defined your properties.
You are trying to refer to the property to which you are assigning the value in the setter
which is resulting in an infinte recursion (to be specific this line is triggering it newUser.userName = "test123";).
Change them to:
public int userId
{
get { return _userId; }
set { _userId = value; }
}
public string userName
{
get { return _userName; }
set { _userName = value; }
}
It is because userName calls itself. You probably meant to assign the field:
This line is wrong:
set { userName = value; }
You meant to write:
set { _userName = value; }
You need to set the private backing field, not the property. Otherwise you are just going into infinite recursion as you call set on yourself the entire time.
private int _userId;
private string _userName;
public int userId
{
get { return _userId; }
set { _userId = value; }
}
public string userName
{
get { return _userName; }
set { _userName = value; }
}
In your case, you could just use auto implemented properties (I changed the casing to match the guidelines):
public int UserId { get; set; }
public string UserName { get; set; }
If you re-write your setters a bit, it's easy to see what's happening. The get and set on a property are actually compiled down to methods (PropType get_PropName() and void set_PropName(PropType value)), and any references to the property are compiled to calls to the appropriate method. So this code:
int i = myObj.MyIntProp;
myObj.MyIntProp = 6;
compiles to
int i = myObj.get_MyIntProp();
myObj.set_MyIntProp(6);
So your setter
set
{
username = value;
}
actually compiles to
public void set_username(string value)
{
set_username(value);
}
And now the cause of the stack overflow is obvious.

Fluent interfaces and inheritance in C#

I'll show a problem by example. There is a base class with fluent interface:
class FluentPerson
{
private string _FirstName = String.Empty;
private string _LastName = String.Empty;
public FluentPerson WithFirstName(string firstName)
{
_FirstName = firstName;
return this;
}
public FluentPerson WithLastName(string lastName)
{
_LastName = lastName;
return this;
}
public override string ToString()
{
return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
}
}
and a child class:
class FluentCustomer : FluentPerson
{
private long _Id;
private string _AccountNumber = String.Empty;
public FluentCustomer WithAccountNumber(string accountNumber)
{
_AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(long id)
{
_Id = id;
return this;
}
public override string ToString()
{
return base.ToString() + String.Format(" account number: {0} id: {1}", _AccountNumber, _Id);
}
}
The problem is that when you call customer.WithAccountNumber("000").WithFirstName("John").WithLastName("Smith") you can't add .WithId(123) in the end because return type of the WithLastName() method is FluentPerson (not FluentCustomer).
How this problem usually solved?
Try to use some Extension methods.
static class FluentManager
{
public static T WithFirstName<T>(this T person, string firstName) where T : FluentPerson
{
person.FirstName = firstName;
return person;
}
public static T WithId<T>(this T customer, long id) where T : FluentCustomer
{
customer.ID = id;
return customer;
}
}
class FluentPerson
{
public string FirstName { private get; set; }
public string LastName { private get; set; }
public override string ToString()
{
return string.Format("First name: {0} last name: {1}", FirstName, LastName);
}
}
class FluentCustomer : FluentPerson
{
public long ID { private get; set; }
public long AccountNumber { private get; set; }
public override string ToString()
{
return base.ToString() + string.Format(" account number: {0} id: {1}", AccountNumber, ID);
}
}
after you can use like
new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);
You can use generics to achieve that.
public class FluentPerson<T>
where T : FluentPerson<T>
{
public T WithFirstName(string firstName)
{
// ...
return (T)this;
}
public T WithLastName(string lastName)
{
// ...
return (T)this;
}
}
public class FluentCustomer : FluentPerson<FluentCustomer>
{
public FluentCustomer WithAccountNumber(string accountNumber)
{
// ...
return this;
}
}
And now:
var customer = new FluentCustomer()
.WithAccountNumber("123")
.WithFirstName("Abc")
.WithLastName("Def")
.ToString();
A solution where you need fluent interface, inheritance and also some generics...
Anyhow as I stated before: this is the only option if you want to use inheritance and access also protected members...
public class GridEx<TC, T> where TC : GridEx<TC, T>
{
public TC Build(T type)
{
return (TC) this;
}
}
public class GridExEx : GridEx<GridExEx, int>
{
}
class Program
{
static void Main(string[] args)
{
new GridExEx().Build(1);
}
}
Logically you need to configure stuff from most specific (customer) to least specific (person) or otherwise it is even hard to read it despite the fluent interface. Following this rule in most cases you won't need get into trouble. If however for any reason you still need to mix it you can use intermediate emphasizing statements like
static class Customers
{
public static Customer AsCustomer(this Person person)
{
return (Customer)person;
}
}
customer.WIthLastName("Bob").AsCustomer().WithId(10);
public class FluentPerson
{
private string _FirstName = String.Empty;
private string _LastName = String.Empty;
public FluentPerson WithFirstName(string firstName)
{
_FirstName = firstName;
return this;
}
public FluentPerson WithLastName(string lastName)
{
_LastName = lastName;
return this;
}
public override string ToString()
{
return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
}
}
public class FluentCustomer
{
private string _AccountNumber = String.Empty;
private string _id = String.Empty;
FluentPerson objPers=new FluentPerson();
public FluentCustomer WithAccountNumber(string accountNumber)
{
_AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(string id)
{
_id = id;
return this;
}
public FluentCustomer WithFirstName(string firstName)
{
objPers.WithFirstName(firstName);
return this;
}
public FluentCustomer WithLastName(string lastName)
{
objPers.WithLastName(lastName);
return this;
}
public override string ToString()
{
return objPers.ToString() + String.Format(" account number: {0}", _AccountNumber);
}
}
And invoke it using
var ss = new FluentCustomer().WithAccountNumber("111").WithFirstName("ram").WithLastName("v").WithId("444").ToString();
Is a fluent interface really the best call here, or would an initializer be better?
var p = new Person{
LastName = "Smith",
FirstName = "John"
};
var c = new Customer{
LastName = "Smith",
FirstName = "John",
AccountNumber = "000",
ID = "123"
};
Unlike a fluent interface, this works fine without inherited methods giving back the base class and messing up the chain. When you inherit a property, the caller really shouldn't care whether FirstName was first implemented in Person or Customer or Object.
I find this more readable as well, whether on one line or multiple, and you don't have to go through the trouble of providing fluent self-decorating functions that correspond with each property.
I know this is now an old question, but I wanted to share my thoughts about this with you.
What about separating fluency, which is a kind of mechanism, and your classes, when you can ? This would leave your classes pure.
What about something like this ?
The classes
public class Person
{
public string FirstName { get; set; }
public string LastName {get; set;}
public override string ToString()
{
return $"First name: {FirstName} last name: {LastName}";
}
}
public class Customer : Person
{
public string AccountNumber { get; set; }
public long Id { get; set; }
public override string ToString()
{
return base.ToString() + $" account number: {AccountNumber} id: {Id}");
}
}
A class that adds some fluent mechanism
public class FluentCustomer
{
private Customer Customer { get; }
public FluentCustomer() : this(new Customer())
{
}
private FluentCustomer(Customer customer)
{
Customer = customer;
}
public FluentCustomer WithAccountNumber(string accountNumber)
{
Customer.AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(long id)
{
Customer.Id = id;
return this;
}
public FluentCustomer WithFirstName(string firstName)
{
Customer.FirstName = firstName;
return this;
}
public FluentCustomer WithLastName(string lastName)
{
Customer.LastName = lastName;
return this;
}
public static implicit operator Customer(FluentCustomer fc)
{
return fc.Customer;
}
public static implicit operator FluentCustomer(Customer customer)
{
return new FluentCustomer(customer);
}
}
An extension method to switch to fluent mode
public static class CustomerExtensions
{
public static FluentCustomer Fluent(this Customer customer)
{
return customer;
}
}
The same example as in question
Customer customer = new Customer().Fluent()
.WithAccountNumber("000")
.WithFirstName("John")
.WithLastName("Smith")
.WithId(123);

Categories

Resources