Reputation: 18734
I have an APS.Net application, which is layered - UI-Service-Business-Data Access-Database.
When I save or update a user, or example, I create a UserDto object, populate the properties in the UI, and then pass the object down the layers, to a Save method in the data accessor.
When it saves, it return the id of the updated or inserted user:
protected void btnSave_Click(object sender, EventArgs e)
{
var o = new UserDto
{
DisplayName = txtName.Text,
Email = txtEmail.Text,
Username = txtUsername.Text,
Password = txtPassword.Text,
TimeZoneId = ddZones.SelectedValue,
Id = Session["SelectedUserId"] == null ? 0 : int.Parse(Session["SelectedUserId"].ToString())
};
int id = new UserService(Common.CurrentUserId()).SaveUser(o);
Response.Redirect("users.aspx");
}
This is all working, but I am adding a validation layer to my business layer. And this will validate the object against some business rules, and if it's OK, save it - else, I want it to return ... something ... to use on the UI to indicate an issue.
So, my validator at the moment, basic.. which has a method that accepts the user obejct before saving, does validation, and then returns true or false... It looks like this:
public static bool SaveUser(UserDto user)
{
if (user == null)
{
return false;
}
if(user.Username.Length < 4 || user.Username.Length > 15)
return false;
if (user.Password.Length < 4 || user.Password.Length > 15)
{
return false;
}
try
{
var m = new MailAddress(user.Email);
return true;
}
catch (FormatException)
{
return false;
}
}
Note: This method doesn't do the saving... so I might rename it (It's in my Validation class though). It just asks the question, can I save this object.
So, some basic validation. However, my return type is just bool. I want it to return something like a List<String> ValidationErrors
or something.
But, my save method from the UI expects an int, and if all saves well, I just wan the int. How would I handle the time when errors occur, and I need a List<> back?
An idea that just dawned on me, as I posted this: Maybe I can create a SaveUserDto object, which is the return type, and has the 'Id' of the item saved, as well as a List?
So, I send a UserDto down, and reply with e SaveUserDto? Or, to be more generic, I send down a UserDto, and reply with a ResponseDto, which simply has an id (if the item saved) and a List of the errors, and a bool property which is true if ErrorList.Count > 1?
Upvotes: 1
Views: 1012
Reputation: 1206
From my point of view, all your layers but presentation should avoid catching exceptions, this way the error bubbles up through the layers. It's only in the final layer, at presentation, where you deal with the problems using common asp.net validators and summaries to display them properly. With this approach you leave to the presentation layer the work of error handling, your other layers remain clean and agnostic all the exceptions get to the top level without changes.
If you feel forced to make error handling in inner layers I would just add custom exceptions so instead of getting a NullReferenceException you can catch it and throw your own WebUserNotFoundException. In this case, add a new project with all your exceptions, a reference it across all layers. For creating exceptions I would create a base custom exception that inherits from the exception class and then create each custom exception inheriting the base class.
I would only change this approach if I had to create a SOA, in that case, I would create a base return object where I can have both the information requested and what happened when making the service call.
Upvotes: 1
Reputation: 63105
change method as
public static bool SaveUser(UserDto user, out List<String> ValidationErrors)
{
// do the stuff
// in case of error set ValidationErrors
}
then you can call above method as
List<String> ValidationErrors;
if(!SaveUser(user, out ValidationErrors))
{
//you have errors, show the errors using ValidationErrors
}
Upvotes: 1
Reputation: 39278
With this pattern it might make sense to create a custom validation exception that you can throw in case of validation errors. The try catch on the caller side can look for a List<string>
property on the exception itself and display the errors in the UI.
Alternatively you can return an object with success/error collections instead.
Upvotes: 1