问题
I am trying to follow SOLID principles when writing new classes and code.
My question is about ASP.NET classes that extend the Page class i.e. ASPX files. If I had a page class that has a page_load event that creates instances of multiple objects e.g. Person class, Sport class etc then I believe this page class is tightly coupled with these classes. Is this the case or am I missing something obvious? Could it be that all classes should expose interfaces and the client (aspx pages) should use the interfaces rather than instantiating the classes directly.
I find interfaces useful if Polymorphism is involved e.g. using a Student interface to create an instance of graduate or undergraduate at runtime. Should all classes have interfaces?
回答1:
If you create your entities inside your Page
class (presentation layer), you are clearly violating the Single Responsibility Principle, since the page class will have multiple reasons to change.
Instead, move this logic to the business layer and create a service that handles this logic.
Your page will need to talk to the service interface, not the implementation (DIP) and this service interface needs to be narrow (ISP); probably just have one method.
If you package all arguments for the service into a single object, thus separating data and behavior, and use a generic interface for your services (i.e. ICommandHandler<TCommand>
), you can even comply to the OCP, since you can now add behavior (such as validation, transactions, deadlock detection, asynchronous processing, queueing) to services, whithout any changes to the application.
Last note, don't create interfaces for entities. This is rather useless, and obscures your code.
回答2:
I wouldn't instantiate the objects in the code behind. What I do is call a service that instantiates gets the objects - which instantiates them with data from the database. I also made the service public in a masterpage and reference it like this:
IUser _user = (Page.Master as MasterBase).RegistrationService.GetPersonFromEmailAddress(txtUser.text);
of course you should check to make sure (Page.Master as MasterBase) isn't null....
Edit by request
I create a base Master Page that all other site Master Pages must inherit from. In constructor of this base Master Page I instantiate all of the services that are necessary. Of course, if the services aren't used through-out the site, they can be instantiated on a different, more appropriate section-general Master page.
public readonly IRegistration RegistrationService;
public readonly IEventService EventService;
public readonly IEmailSendingService EmailService;
private readonly IMenuService _menuService;
public MasterBase()
{
RegistrationService = new BusinessLogic.Registration(DatabaseConnectionString);
EventService = new EventService(DatabaseConnectionString);
var fromAddress = ConfigurationManager.AppSettings["Webmaster"];
var defaultToEmailAddress = ConfigurationManager.AppSettings["toEmail"];
EmailService = new EmailSendingService(BaseUrl, fromAddress, defaultEmailAddress);
_menuService = new MenuService(DatabaseConnectionString);
}
As mentioned above, any Master Pages used on the site will inherit from the base:
public partial class MasterContent : MasterBase
Any UI page that references a Master Page (just about all of them in my case)
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="activities.aspx.cs" Inherits="activities" MasterPageFile="~/MasterContent.Master" %>
will now have access to the different services in the code behind:
IUser _user = (Page.Master as MasterBase).RegistrationService.GetPersonFromEmailAddress(txtUser.text);
As mentioned in the comments below, the different UI pages do have a tight coupling with the Base Master Page, but I don't know of any better way to do it in a general ASP.NET web forms application. It alleviates the need to instantiate the service on every UI page, and from a project-specific perspective, it is acceptable to make all Master Pages inherit from this base page.
回答3:
One way you could do this was to configure your dependencies in the Global.asax
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapPageRoute("",
"Create/{Service}",
"~/categoriespage.aspx",{"Service", new WhatEverSErvice()} );
}
Where you'd need to fit the URL pattern and file path to suite your site. This will give you a handle to a service object through the Service route value
You could then declare a base page class
public abstract class BasePage<TService> : Page where TService : IService {
protected TService Service{
get {
return Page.RouteData.Values["Service"] as TService;
}
}
}
Here IService
is an interface you will have to define that's the basis for your services (if such an interface makes sense otherwise simply omit the where clause)
Then have all your pages derive from this class and when ever you need your particular service you simply write something like
var myFoo = Service.CreateFoo();
This approach does act somewhat strange in error scenarios when the user supplies a string after the /. You could handle this by extending the property, since it's basically a file not found (The supplied URL is invalid) you could return a 404 FNF response
来源:https://stackoverflow.com/questions/11689959/solid-principles-and-web-page-class