I currently have a 'Customer' class that looks like:
public class Customer
{
public string email { get; set; }
public string first_name { get; set; }
public string middle_name { get; set; }
public string last_name { get; set; }
}
However, I also have another class that wraps the 'Customer' as part of a REST 'GET' request:
public class CustomerRequest
{
Customer customer;
}
In my CustomerRequest, I'd like to make the email field required. In other words, I'd like to override the default behavior of the Customer class to throw an ArgumentNullException if the email isn't entered upon object creation. I'd only like to enforce this requirement as part of the CustomerRequest, NOT the customer.
I've tried to make the underlying Customer class fields 'virtual', but this still doesn't allow me to override them in the CustomerRequest class.
How can I achieve my desired functionality?
For whatever it's worth, I'd rethink throwing exceptions in a constructor. Doing so causes the exception to be caught and rethrown as an ObjectInitializationException by the runtime--an operation completely outside your control. These exceptions can be obscure and difficult to pin down, especially to developers who aren't familiar with the object model.
What you might do instead is put a Validate method on the CustomerRequest object that verifies that all required fields are populated (in this case, the email address). Sure, it requires a manual method invocation, but it's explicit, and it doesn't unexpectedly surprise you at runtime.
Short answer: you can't change the behaviour of Customer just because it's a field in another class.
Longer answer: there are a couple of ways of achieving what you're looking for. #MetroSmurf's answer is probably the simplest, but you could also change the customer field on CustomerRequest to be a property and perform the check there i.e.
public class CustomerRequest
{
private Customer _customer;
public Customer Customer
{
get { return _customer; }
set
{
if (string.IsNullOrEmpty(value.email))
{
throw new ArgumentNullException("email");
}
_cutomer = value;
}
}
}
But honestly, you'd just be better off validating the CustomerRequest when you recieve it.
public CustomerRequest
{
public Customer customer {get;set;}
public CustomerRequest(Customer c)
{
this.customer = CheckEmail(c);
}
private Customer CheckEmail(Customer c)
{
if (c.email == null) throw ArgumentNullException;
return c;
}
}
Related
I've been reading about DDD and am still confused about aggregate root.
Imagine that I have a situation similar to a blog, where people can create posts and add comments to other posts.
Rules:
-Everybody needs to have an account to add post or comment
-Users are able to delete their own comments only
With that in mind, I would need the following objects:
-Post
-PostComment
-User
So, I created only the Post object as aggregate root and added some business logic to it
public class User : EntityBase
{
public string Name { get; set; }
public string Avatar { get; set; }
}
public class Post : EntityBase, IAggregate
{
public string Title { get; set; }
public string Content { get; set; }
public User Creator { get; set; }
private IList<PostComment> Comments { get; set; }
public void AddComment(PostComment comment)
{
this.Comments.Add(comment);
}
public void DeleteComment(PostComment comment, int userId)
{
if (comment.Creator.Id != userId)
throw new Exception("You cannot delete a comment that is not yours. blablabla");
this.Comments.Add(comment);
}
public IList<PostComment> GetComments()
{
return this.Comments;
}
}
public class PostComment : EntityBase
{
public string Comment { get; set; }
public User Creator { get; set; }
}
Am I doing this correctly? I mean, is the business logic in the right place? Or I should've made PostComment as aggregate root too and added the logic of add/delete in it?
Warning: it's difficult to reason about DDD using toy problems. Especially in your core domain, the point of all of this work is that you can customize things to meet your local needs. If you didn't need a bespoke solution, you'd just buy some off-the-shelf solution, integrate and get on with it.
Or I should've made PostComment as aggregate root too and added the logic of add/delete in it?
Maybe. Aggregates are best thought of as atoms, you load the entire aggregate, make your changes, save the results.
So if you find yourself with many concurrent attempts to modify the same aggregate, then you have to deal with a bunch of contention issues. Alice can't change her comment while Bob is changing his; we have to do them one at a time (to avoid losing changes).
On the other hand, if each comment is an aggregate of its own, then Bob and Alice can make their changes in parallel, without needing to rerun the "business logic" because the other person's change happened first.
Which is great, when it is free. But it isn't free -- the cost you pay is that the information is now distributed, and you have to deal with the fact that the changes have different timing. You'll sometimes see "eventual consistency" used here -- because the authoritative information is distributed, there will be times where not all of the observers have the same sets of changes.
In most domains, this is fine: race conditions don't exist. But trying to perform an all or nothing change across distributed data is a nightmare.
On the other hand, if you are willing to accept that changes happen at different times, then separating the aggregates out is fine.
Example: Twitter. Bob tweets something dumb. Alice tweets that Bob is dumb, with a link to his tweet. Bob deletes his tweet. And that's all fine, because we're comfortable with the fact that Alice's tweet has a link to something that is no longer available.
It is often the case that information that comes from the outside world can be its own aggregate, because what we are really doing at that stage is caching data, which is already stale by the time we receive it.
You may also want to review Mauro Servienti's talk All Our Aggregates Are Wrong, which discusses the heuristics for breaking down an aggregate into smaller pieces.
Am I doing this correctly? I mean, is the business logic in the right place? Or I should've made PostComment as aggregate root too and added the logic of add/delete in it?
Partially! I consider the logic is in the right place and PostComment should not be an aggregate root. But if you wants to take off more about DDD I consider that there are some another points to review before continue. I hope I can help you some way in the explanations bellow.
I have reviewed the code and refactored it to explain some points you can reconsider. Try to read it, compare and understand before read my explanation below.
// you can simplify your DomainModel removing the IAggregate plus adding generics
public abstract class Entity<T>
{
public T Id { get; set; }
}
// this is an Aggregate Root
public class Person : Entity<int>
{
public string Name { get; set; }
public string Avatar { get; set; }
public override string ToString()
{
return Name;
}
}
//this is an Aggregate Root
public class Post : Entity<int>
{
private List<Comment> _comments = new List<Comment>();
public string Title { get; set; }
public string Content { get; set; }
public Person Author { get; set; }
public IReadOnlyList<Comment> Comments => _comments;
public void Reply(Comment comment)
{
_comments.Add(comment);
}
public void Delete(Comment comment, int personId)
{
if (!AreSame(comment.Author, personId))
throw new Exception("You cannot delete a comment that is not yours. blablabla");
_comments.Add(comment);
}
private bool AreSame(Person author, int personId)
{
return author.Id == personId;
}
public override string ToString()
{
return Title;
}
}
// this is a Value Object part of Post Aggregate
public struct Comment
{
public DateTime Date;
public string Text;
public Person Author;
public Comment(DateTime date, string text, Person author)
{
Date = date;
Text = text;
Author = author;
}
public override string ToString()
{
return $"{Date} - {Author}: {Text}";
}
}
If the PostComment is part of Post Aggregate, it can't be an EntityBase, because each Aggragate should have only one root (Id). You're modeling a domain where a Post may have N Comments. You can consider the PostComment as a Value Object instead an Entity removing his Id.
You should pay attention about the names you are using. Try to sound more natural. It is called, ubiquitous language, the words everybody speak.
User is a description that just have a sense in system's context, in other words, you should have a User if you dealing with Security or Authentication contexts, in a Blog Context you have a Person acting as Author.
Increase readability using terms your users says. Reply may be more natural than AddComment.
public void Reply(Comment comment)
{
_comments.Add(comment);
}
Increase readability adding names for your conditions:
public void Delete(Comment comment, int personId)
{
if (!AreSame(comment.Author, personId))
throw new Exception("You cannot delete a comment that is not yours. blablabla");
_comments.Add(comment);
}
private bool AreSame(Person author, int personId)
{
return author.Id == personId;
}
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 class Customers. I want to put some validations on it.
e.g. CustGuidId is not Guid.Empty, CustName is NOT NULL (Required).
public class Customer
{
public int CustId;
public string CustName;
public Guid CustGuid;
public Guid[] OrderGuids;
}
I have such collection of customers. So I have ended up adding code like this, which makes it look ugly.
public class BatchError
{
public int Index;
public string ErrorCode;
public string ErrorMessage;
}
public void GenerateValidationErrors(List<Customer> customers, out List<BatchError> batchErrors)
{
int rowNum = 0;
batchErrors = new List<BatchError>(customers.Count);
foreach (var customer in customers)
{
rowNum ++;
Guid customerGuidParsed;
if(!Guid.TryParse(customer.CustGuid.ToString(), out customerGuidParsed))
{
batchErrors.Add(new BatchError { Index = rowNum, ErrorCode = "CustomerGuidcannotBeNull", ErrorMessage = "Customer guid cannot be null." });
}
if (string.IsNullOrEmpty(customer.CustName))
{
batchErrors.Add(new BatchError { Index = rowNum, ErrorCode = "CustomerNamecannotBeEmpty", ErrorMessage = "Customer Name cannot be empty." });
}
}
}
Can we write separate validator classes, like GuidValidator, StringValidator.
and Create array of delegates & chain their invokes ?
(Customer c) => new GuidValidator(c.CustGuid.toString()),
(Customer c) => new StringValidator(c.CustName.toString())
But what design pattern would be best suitable for this scenario?
Is there any other way to add validations in WCF?
There are many ways to do the validation. I prefer to validate DataContract itself before any action.
It can also be done in many like :
DatamemberAttribute has many properties. One of them is
IsRequired,it controls the minOccurs attribute for the schema
element. The default value is false. You can use it like:
[DataContract(Name ="Place", Namespace ="")]
public class DataContractExample
{
[DataMember(IsRequired=true)]
public string DataMemberExample;
}
For more information refer: DataMemberAttribute Class on MSDN.
Easiest way is to validate property like:
[DataContract]
public class Customer
{
[DataMember]
public string CustName
{
get
{
return this._custName;
}
set
{
if(string.IsNullOrEmpty(value))
throw new MyValidationException();
else
this._custName=value;
}
}
}
Another way can be to use Microsoft Enterprise Library. In order to enable validation of the properties of a request message, you only need to add a [ValidationBehavior] attribute to your service interface, just next (or before) the [ServiceContract], and a [FaultContract(typeof(ValidationFault))] on the method declaration. The ValidationBehaviorAttribute and ValidationFault classes are defined in the Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF assembly and are part of the Validation Application Block of the Enterprise Library 4.1, more specifically, of the WCF integration module. See full implementation in detail at: http://weblogs.asp.net/ricardoperes/validation-of-wcf-requests-with-the-enterprise-library-validation-block
Finally one more solution cane be to use WCF Data Annotations from http://wcfdataannotations.codeplex.com/. Using this you can use validations like:
[DataMember]
[Required]
[StringLength(500, MinimumLength = 5)]
public string Description{ get; set; }
Choose which ever suite your requirements. Cheers.
I currently have a method to save a user.
public PersonDto Save(PersonDto personDto)
However, if, while saving, I find there is a duplicate username, or some other issue with the data - my only way to respond to this is to throw an exception.
throw new Exception("Username exists");
I have read that exceptions shouldn't be used for 'Business Requirement Transgressions'.
Is there a better way to return results to my calling methods? I need to return to PersonDto, but also, some information about any issues. Is there a common practice or model for doing this? Maybe return a 'SaveResult' object, which contains a Object SavedObject (in this case, my PersonDto), as well as some other properties like string SaveResult and bool Success?
Maybe implement a generic wrapper for use with all such requests.
enum Disposition
{
OK,
Warning,
Error
}
class Response<T>
{
public T Result { get; set; }
public Disposition Disposition { get; set; }
public string Message { get; set; }
}
i.e.:
public Response<PersonDto> Save(PersonDto personDto)
This way you can specify some metadata for each of your return values.
I recommend using a SaveResult approach to your problem e.g.
public class SaveResult
{
public PersonDto { get; set; }
public bool Success { get; set; }
public string ErrorMessage { get; set; }
}
var result = Save(person);
if (!result.Success)
{
Console.WriteLine(result.ErrorMessage);
}
Or something like that. This will allow you to pass back the PersonDto, but also signal to the API caller that an error occurred when trying to save.
There's nothing really wrong with throwing an exception, but as you also want to return the person DTO, then you might as well use a specific method return type for that purpose.
I have an Address object defined simply as follows:
public class Address
{
public string StreetNumber { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
}
Fairly simple. On the advice an answer to another question I asked, I am referring to this blog post when databinding my UI to an object of type Person (which contains an Address MailingAddress field).
The problem is that the IDataError interface method isn't validating any of the properties of the Address type.
public string this[string columnName]
{
get
{
string result = null;
// the following works fine
if(columnName == "FirstName")
{
if (string.IsNullOrEmpty(this.FirstName))
result = "First name cannot be blank.";
}
// the following does not run
// mostly because I don't know what the columnName should be
else if (columnName == "NotSureWhatToPutHere")
{
if (!Util.IsValidPostalCode(this.MailingAddress.PostalCode))
result = "Postal code is not in a know format.";
}
return result;
}
}
So, obviously I don't know what the columnName will be... I've stepped through it and it has never been anything other than any of the public properties (of intrinsic types). I've even tried running and breaking on a statement like:
if (columnName.Contains("Mailing") || columnName.Contains("Postal"))
System.Windows.Forms.MessageBox.Show(columnName);
All to no avail.
Is there something I'm missing?
You need to define IErrorInfo on all the classes that you want to supply error messages for.
Take a look at my answer here.
This explains how to use a modelbinder to add 'class-level' checking of your model without having to use IDataError - which as you have seen here can be quite clumsy. It still lets you use [Required] attributes or any other custom validation attributes you have, but lets you add or remove individual model errors. For more on how to use data annotations I highly recommend this post from Scott Gu.