Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
218 views
in Technique[技术] by (71.8m points)

c# - How Do You Communicate Service Layer Messages/Errors to Higher Layers Using MVP?

I'm currently writing an ASP.Net app from the UI down. I'm implementing an MVP architecture because I'm sick of Winforms and wanted something that had a better separation of concerns.

So with MVP, the Presenter handles events raised by the View. Here's some code that I have in place to deal with the creation of users:

public class CreateMemberPresenter
{
    private ICreateMemberView view;
    private IMemberTasks tasks;

    public CreateMemberPresenter(ICreateMemberView view) 
        : this(view, new StubMemberTasks())
    {
    }

    public CreateMemberPresenter(ICreateMemberView view, IMemberTasks tasks)
    {
        this.view = view;
        this.tasks = tasks;

        HookupEventHandlersTo(view);
    }

    private void HookupEventHandlersTo(ICreateMemberView view)
    {
        view.CreateMember += delegate { CreateMember(); };
    }

    private void CreateMember()
    {
        if (!view.IsValid)
            return;

        try
        {
            int newUserId;
            tasks.CreateMember(view.NewMember, out newUserId);
            view.NewUserCode = newUserId;
            view.Notify(new NotificationDTO() { Type = NotificationType.Success });
        }
        catch(Exception e)
        {
            this.LogA().Message(string.Format("Error Creating User: {0}", e.Message));
            view.Notify(new NotificationDTO() { Type = NotificationType.Failure, Message = "There was an error creating a new member" });
        }
    }
}

I have my main form validation done using the built in .Net Validation Controls, but now I need to verify that the data sufficiently satisfies the criteria for the Service Layer.

Let's say the following Service Layer messages can show up:

  • E-mail account already exists (failure)
  • Refering user entered does not exist (failure)
  • Password length exceeds datastore allowed length (failure)
  • Member created successfully (success)

Let's also say that more rules will be in the service layer that the UI cannot anticipate.

Currently I'm having the service layer throw an exception if things didn't go as planned. Is that a sufficent strategy? Does this code smell to you guys? If I wrote a service layer like this would you be annoyed at having to write Presenters that use it in this way? Return codes seem too old school and a bool is just not informative enough.


Edit not by OP: merging in follow-up comments that were posted as answers by the OP


Cheekysoft, I like the concept of a ServiceLayerException. I already have a global exception module for the exceptions that I don't anticipate. Do you find making all these custom exceptions tedious? I was thinking that catching base Exception class was a bit smelly but wasn't exactly sure how progress from there.

tgmdbm, I like the clever use of the lambda expression there!


Thanks Cheekysoft for the follow-up. So I'm guessing that would be the strategy if you don't mind the user being displayed a separate page (I'm primarily a web developer) if the Exception is not handled.

However, if I want to return the error message in the same view where the user submitted the data that caused the error, I would then have to catch the Exception in the Presenter?

Here's what the CreateUserView looks like when the Presenter has handled the ServiceLayerException:

Create a user

For this kind of error, it's nice to report it to the same view.

Anyways, I think we're going beyond the scope of my original question now. I'll play around with what you've posted and if I need further details I'll post a new question.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

That sounds just right to me. Exceptions are preferable as they can be thrown up to the top of the service layer from anywhere inside the service layer, no matter how deeply nested inside the service method implementation it is. This keeps the service code clean as you know the calling presenter will always get notification of the problem.

Don't catch Exception

However, don't catch Exception in the presenter, I know its tempting because it keeps the code shorter, but you need to catch specific exceptions to avoid catching the system-level exceptions.

Plan a Simple Exception Hierarchy

If you are going to use exceptions in this way, you should design an exception hierarchy for your own exception classes. At a minumum create a ServiceLayerException class and throw one of these in your service methods when a problem occurs. Then if you need to throw an exception that should/could be handled differently by the presenter, you can throw a specific subclass of ServiceLayerException: say, AccountAlreadyExistsException.

Your presenter then has the option of doing

try {
  // call service etc.
  // handle success to view
} 
catch (AccountAlreadyExistsException) {
  // set the message and some other unique data in the view
}
catch (ServiceLayerException) {
  // set the message in the view
}
// system exceptions, and unrecoverable exceptions are allowed to bubble 
// up the call stack so a general error can be shown to the user, rather 
// than showing the form again.

Using inheritance in your own exception classes means you are not required to catch multipile exceptions in your presenter -- you can if there's a need to -- and you don't end up accidentally catching exceptions you can't handle. If your presenter is already at the top of the call stack, add a catch( Exception ) block to handle the system errors with a different view.

I always try and think of my service layer as a seperate distributable library, and throw as specific an exception as makes sense. It is then up to the presenter/controller/remote-service implementation to decide if it needs to worry about the specific details or just to treat problems as a generic error.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...