The Microsoft code guidline forbids complex operations in a constructor ( see https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/constructor)
What's the proper way to validate the input data if not in the constructor. Here is an example:
class User{
User(string id){
Validate(id); //Looks up id in database or remote service
}
}
The validate should not be called in the constructor. But how can I make sure I don't have a User class with an invalid id?
Basically it depends on whether the operation is actually complex.
In case that the operation is complex, you can use properties and validate the values inside:
class User{
private string _id;
public int Id
{
get
{
return _id;
}
set
{
if (Validate(value))
{
_id = value;
}
}
}
User(){
}
}
Related
I have a web api action method which takes below Model as parameter (Post).
public class RequestModel
{
public string PartType { get; set; }
public int Quantity { get; set; }
public decimal UnitCost{ get; set; }
public bool? Owner { get; set; }
public bool? DoSplit { get; set; }
}
The options Owner/Do Split will be choosen by the user on UI and its based on Part Type. Also based on the Owner flag there is some other business logic which needs to be executed in combination with the DoSplit and Quantity. Hence I have many permuations and combinations. Going bruteforce the logic would go this way:
int existingQty = GetInitialQuantity(model.SerialId); //returns esisting qty
if(existingQty < model.Quantity && model.Owner)
{
// logic here
}
else if (existingQty < model.Quantity && model.Owner == false)
{
}
else if (existingQty = model.Quantity) // no need to check for DoSplit
{
}
etc..... more if else in combincation with qty comaprison, Dosplit and owner flag checks with null checks.
based on the different property values in the model (in combination) I need to do different actions. How to avoid if else and use a proper design patterns of C# here.
Since the model is passed from javascript through a web api call to my action method how can I use OOPs here for the requestmodel and avoid branching in the controller method ?
I think one of the main reasons that you have so much if/else is that you do not have the business logic in the object itsel but try to use the business logic from outside. As I do not get what your business logic is, my implementation might not work on your case, but i want to show you how to get rid of the if else in a simple case. The main goal is to not use the properties but only use the functions and let the object handle its state on its own. (lookup Tell dont ask and State Pattern)
lets look at this class
public class User
{
string name { get; set; }
bool isDisabled { get; set; }
}
using it might be like this
if (!user.isDisabled)
{
user.name = nameFromApi
}
but in this case you have to repeat this on every corner where you want to use the User. So consider this
public interface IUser
{
string name { get; }
IUser updateName(string newName);
IUser disableUser();
}
public class DisabledUser : IUser
{
public DisabledUser(IUser activeUser)
{
this.name = activeUser.name;
}
public string name { get; }
public IUser updateName(string newName)
{
return this;
}
public IUser disableUser()
{
return new DisabledUser(this);
}
}
public class ActiveUser : IUser
{
public ActiveUser(IUser user)
{
this.name = user.name;
}
public string name { get; private set; }
public IUser updateName(string newName)
{
this.name = newName;
return this;
}
public IUser disableUser()
{
return new DisabledUser(this);
}
}
In this way the if is gone and you actually gained something else: You can easily extend the implementation with other states like banned and you do not have to change the old implementation. Yes, it is more code, but way easier to maintain.
In you case i think you should be able to get rid of all the if/elses regarding the boolean flags, if you initialize the objects correctly. This is a powerfull pattern and you do not need to cast anything to be able to use the user.
I do not know your usecase for the quantity stuff, so I can not tell you how this might get resolved, but there is certainly a way to do that.
I have a typical web API with a couple of PUT/UPDATE endpoints. These endpoints simply call the underlying service, and do the update.
The service layer, has the typical signature such as Object Update(Object object). What I then do is I basically run the following pseudo code:
var dbobject = _db.Object.Find(object.Id);
dbobject.Field1 = object.Field1;
dbobject.Field2 = object.Field2;
// continue for all fields
_db.SaveChanges();
return GetObjectById(object.Id);
However, this provides a challenge for me.
Lets say we have a consumer of our API. This consumer calls my PUT endpoint (/api/Object/{id}), and the payload is the updated Object.
However, lets say that the object we put don't know about example Field4, then this value would be NULL after the update has been run.
My question is:
What do you do about all those fields the payload does NOT contain?
How do you handle not setting values to NULL you don't expect to be
NULL afterwards?
As one of the possible ways, here can be used mix of NotifyPropertyChanged with automapper
The Idea is to store in DTO object which fields exactly was set, and which stays filled with default value. And use collected data in mapping.
For example DTO object will be
public class Dto
{
private List<string> Changed = new List<string>();
public bool IsChanged(string field) => Changed.Contains(field);
private int _age;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// IMPORTANT: field name should fit main object field name
Changed.Add("Name");
}
}
public int Age
{
get { return _age; }
set
{
_age = value;
Changed.Add("Age");
}
}
}
I used Next class for test
public class Human
{
public string Name { get; set; } = "DEFAULT";
public int Age { get; set; } = -1;
}
and automapper configuration will looks like
cfg.CreateMap<Dto, Human>()
.ForAllMembers(s=> s.Condition(d=>d.IsChanged(s.DestinationMember.Name)));
This is a simple example. But it still doesn't prevent to use function IsChanged for some complex/specific logic, use not just a strings but Expressions / MethodInfo, or add custom attributes and use them in automapper configuration (DestinationMember is MethodInfo)
Append
Instead of complex DTO object the information about passed field you can get from Request.Properties in your controller (key ms_querynamevaluepairs value of type Dictionary<string, string>).
I have the following model:
public class ViewDataItem
{
public string viewName { get; set; }
public UpdateIndicator updateIndicator { get; set; }
}
With the following enum:
public enum UpdateIndicator
{
Original,
Update,
Delete
}
And the following Validator:
public class ViewValidator : AbstractValidator<ViewDataItem>
{
public ViewValidator()
{
RuleFor(x => x.viewName).NotEmpty().WithMessage("View name must be specified");
RuleFor(x => x.updateIndicator).SetValidator(new UpdateIndicatorEnumValidator<UpdateIndicator>());
}
}
public class UpdateIndicatorEnumValidator<T> : PropertyValidator
{
public UpdateIndicatorEnumValidator() : base("Invalid update indicator") {}
protected override bool IsValid(PropertyValidatorContext context)
{
UpdateIndicator enumVal = (UpdateIndicator)Enum.Parse(typeof(UpdateIndicator), context.PropertyValue.ToString());
if (!Enum.IsDefined(typeof(UpdateIndicator), enumVal))
return false;
return true;
}
}
The code is in a WebAPI that receives data via JSON, deserialize it to an object and then validates, but for some reason I can send whatever I please in the updateIndicator, so long as I don't put in an integer value larger than the max index in the enum (i.e 1,2 or 3 works fine, but 7 will generate an error).
How can I get this to validate the input of the data I receive to see if that value is actually in the Enum?
Try the built-in IsInEnum()
RuleFor(x => x.updateIndicator).IsInEnum();
This checks if the provided enum value is within the range of your enum, if not, the validation will fail:
"'updateIndicator' has a range of values which does not include '7'."
The problem arises from the fact that the API model builder will convert what is sent to an enum. If a value isn't found, it doesn't populate it, and the default value is used (as it would be with any other property data type that isn't populated).
In order to easily tell if the value sent is a valid enum value, you should make your property nullable. That way, if a value isn't able to be parsed, it will be set to null. If you want to ensure that the property is set, just have your validator not allow null values for it.
public class ViewDataItem
{
public string viewName { get; set; }
public UpdateIndicator? updateIndicator { get; set; }
}
public class ViewValidator : AbstractValidator<ViewDataItem>
{
public ViewValidator()
{
RuleFor(x => x.viewName).NotEmpty().WithMessage("View name must be specified");
RuleFor(x => x.updateIndicator).NotNull();
}
}
Without setting the property to null, your model will always have a valid value when you have it. Alternatively, you could have the first value of your enum be a dummy value, but that would be a code smell. A null model property makes far more sense.
If you want to find out what the actual value that was sent to the API endpoint was, you'll need to look at creating an HTTP Handler, which is beyond the scope of this question.
I am trying to use some methods within set/get accessors on properties of classes generated by entity framework. Purpose is to encrypt the field before saving to DB and Decrypt the fields before reading from DB.I am doing it at the POCO class level so that the encryption-decryption happens while interacting with DB itself and I dont have to scriible through a lot of code. Is the POCO class the best interface to do that?
Code builds successfully, but I get a .Net Framework error when I execute it and the visual studio process is killed eventually.
I am not sure, I might be making some very basic error, but haven't been able to get down to it. Any suggestions will be greatly appreciated !
[Table("Users")]
public class User
{
[Key]
[Required]
public Int32 UserID { set; get; }
[Required]
[MaxLength(50)]
public String UserName // This is where I am using encryption decryption methods
{
set
{
this.UserName = NewEncryptionMethod(value);
}
get
{
return NewDecryptionMethod(this.UserName);
}
}
[Required]
public Int32 CustID { set; get; }
The reason that it's failing right now is because you're running into an infinite amount of calling the get part of UserName since you're trying to grab the value by trying to grab the value and decrypting it. This will cause an overflow error eventually.
Your solution to this is to leave the records in the database as they are, bring them over, and then use a ViewModel class that will contain the decrypted data. When you want to go back to the database, simply encrypt the value in the ViewModel and push that value to the database.
I don't say that the way you are doing the work is good , first thing i can say is that you have infinite recursive call.
Solution:
private string _username;
[Required]
[MaxLength(50)]
public String UserName // This is where I am using encryption decryption methods
{
set
{
_username = NewEncryptionMethod(value);
}
get
{
//you have to deal with a null username here is a bad but quick solution
_username = _username ?? string.Empty;
return NewDecryptionMethod(_username);
}
}
You can keep in UserName raw, insecure data. But make this property internal.
// map me
internal string UserName { get; set; }
And secure it using another property but public:
// don't map me
public string SecureUserName
{
get { return Encrypt(UserName);
set { UserName = Encrypt(value); }
}
Another way is to use a separate class for this purpose, say SecureUser or UserViewModel:
public class SecureUser
{
private readonly User _user;
public SecureUser(User user)
{
_user = user;
}
public string UserName
{
get { return Encrypt(_user.UserName);
set { _user.UserNAme = Decrypt(value); }
}
}
How to avoid redundant second query to database with using MVVM pattern on view model:
public class DataFormViewModel : INotifyPropertyChanged
{
private int companyId
public int CompanyId
{
get { return companyId; }
set
{
companyId = value;
RaisePropentyChanged("FindingStatuses");
RaisePropentyChanged("StatusCount");
}
}
public List<FindingStatus> FindingStatuses
{
get
{
return FindingStatusService.GetAvalableStatuses(CompanyId);
}
}
public int StatusCount
{
get { return FindingStatuses.Count; }
}
}
i.e. when CompanyId was changed by DataBinder FindingStatuses will be executed and then StatusCount will be executed, that will execute FindingStatuses again.
I'm not sure I'd bind the property directly to a database operation in the first place. Why not have a local List<FindingStatus> representing the "last fetched" statuses, and then explicitly refresh it?
Apart from anything else, property access is usually expected to be reasonably cheap - making a database call every time you access either of those properties sounds like a bad idea to me.
Like Jon already mentioned, accessing properties are expected to be cheap, something you can do a thousand times without any sideeffect.
I would cache the result of your database access and return the cached object on any following request. Ie
private IList<FindingStatus> _findingStatuses;
public IList<FindingStatus> FindingStatuses
{
get
{
if (_findingStatuses == null)
{
_findingStatuses = FindingStatusService.GetAvalableStatuses(CompanyId);
}
return _findingStatuses;
}
}
And then you would of course have to clear your cache before raising the notification
public int CompanyId
{
get { return companyId; }
set
{
companyId = value;
_findingStatuses = null;
RaisePropentyChanged("FindingStatuses");
RaisePropentyChanged("StatusCount");
}
}
The best way to avoid multiple (and useless) queries to the database, is implement a simple cache layer in the Data Access Layer.
1- Ask the cache if he already has an updated result
2- Else query the database
Here is a cache class you can try: http://www.codeproject.com/KB/recipes/andregenericcache.aspx