zoukankan      html  css  js  c++  java
  • Castle项目简介第二部分

     

    Introduction Castle 2

    This articles is a continuation of: Introducing Castle Part I. I'll go on explaining how inversion of control can simplify the development of applications, but now paying special attention to web applications.

           这篇文章是:Introducing Castle Part I的后续篇。在这里将继续学习如何使用IOC来简化应用程序的开发,但是我们会将关注点切换到WEB应用开发上来。

           I'll also explain more about Castle's container named Windsor and how its facilities can simplify the developer's life. You can see a list of implemented facilities here.

           同时也会更加深入的探讨Windsor容器以及相关能简化开发人员工作的Facilities。在这里你可以获取已经实现好的Facility列表。

    The MindDump Application

    If we just talk about inversion of control without a real life example, things will soon get boring. Thus, let's create a simple yet functional web application. Why not another blog application? A few requirements that we need to accomplish:

           讨论IOC如果没有有现实意义的示例将变得毫无意义。所以,让我们来创建一个简单但是却有实际意义的Web应用。为什么不是其它的Blog应用,因为我们仅仅需要完成下列需求:

    • The user must create an account and a blog to access the publishing area of the application.
    • The user must be logged on to post texts on his blog.
    • The blogs must be accessible from a friendly URL.
    • The system must "remember" the user for a few days, and yet provide the minimum of security.
    • Each blog can have its own theme.
    • 用户必须创建一个账户才可以访问Blog发布区;
    • 用户发须登录后才可以发布新的Blog
    • 所有的Blog均可通过友好的Url来浏览;
    • 系统可以记住用户一段时间,同时也需要提供最小的安全支持。
    • 要求每个Blog可以有不同的主题;

    There are a few options to persist data nowadays, but let's stick with the old and well-known databases. I'm going to use MySQL for the development.

          如今保存数据有许多方式,我们仍选择传统众所周知的数据库。在这里我们将使用MySQL来做这件事。

    This is the screenshot of the first page:

           下图是首页的快照:

    This is the maintenance area where the user can publish new entries or edit old entries:

    The following are two different themes applied to the same blog:

           下面是相同的Blog应用两个不同主题的结果:

    NHibernate

    One of the famous anti-patterns is the NIH, which stands for Not Invented Here. Sometimes, reinventing the wheel is important, even as an exercise, so you can have a deeper knowledge about something you don't know much about. For example, try to create your own compression algorithm, try to implement your own docking code for your windows, and so on.

           NIH是一个著名的anti-patterns(有谁知道它们是什么,请告诉我),但这里并不准备这样做。有的时候,从0开始也是很重要,因为那时作为一种经历,你能更深入的了解你未知的领域。比如写个属于你的压缩算法,实现窗体停靠等等。

    However, reinventing the wheel for everything is a recipe for disaster, so we don't want to do that. One of Castle's goal is to provide integration with good projects out there for dealing with specific concerns. For example, for database access, we have facilities for NHibernate project and another one for iBatis.Net project. For this application, we're going to use the NHibernate facility.

           但是如果一切从0开始那将是一场灾难,所以我们不必这样。Castle目标之一就是整合好的项目使你只需关注特别的领域。举例来说,对于数据库访问我们有NHibernateiBatis.NetFacility的项目。本例中会使用NHibernate Facility

    Web Framework

    Every time some one approaches me with a new super framework that is supposed to be the new way of doing whatever, I turn on my ultra-skeptical mode. That's reasonable for everyone, and I had them turned on when I started to develop some application with Ruby and then was introduced to Ruby on Rails web framework. Few hours later, I was astonished. How can someone come up with such a great idea and didn't win the Nobel of Science?

           每次当有人告诉我一个新的超级框架可以使用一种新的方式来做任何事时我都极端怀疑。这是有原因的,但是当有人给介绍Ruby on Rails并且当我使用它开发一些应用时我开始转变。短短的时间,令我自已都很惊讶。为什么一个人有如此伟大的创意却没有获得诺贝尔奖呢?

    Ruby on Rails is a great framework, but in my humble opinion, not particularly because it solves a lot of common daily things, but because of its simplicity. It's so simple that it becomes predictable, which is extremely good in a framework. You don't have to read thousands of PDF pages to use it. Once you get the basic idea, you get used to it naturally.

           Ruby on Rails真的是一个伟大的框架,但是对于孤陋的我来说,并不是因为它能解决许多平常问题,而是它足够简单。如此简单才使得可估测,这对于一个框架来说非常好。使用它你不必阅读成千上万的文档。一旦你有了想法,你就可以方便的使用它。

    "And how it works?" you may ask. Basically, it's a MVC framework, but without configuration files to connect the controller with actions and with views and... You got it. With Rails, you'll have just three entities to pay attention: controllers, models and views. From the URL being accessed, Rails will infer the controller name and the action being called. Actions are nothing but public methods on the controller. So suppose you access the following URL:

           “它是怎样工作的呢?”基本上它就是一个MVC框架,但是它不需任何配置文件来连接actionsviews…,你就可以使用。在Rails中,你只需关注三个实体:controllers,modelsviews.根据访问的URLRails将可以推断controller名称与要调用的action.Actions是指controller的公共方法。假设你通过下的URL来访问:

    http://yoursite/home/index

    This URL will instantiate the controller named HomeController and will invoke the method named index. There's a folder for views which uses exactly the same hierarchical definition: controller name plus the action name. If your method doesn't specify anything different, the view chosen will match the controller/action. Your action can use a different view, though.

                   这个URL表示名为HomeControllercontroller,并将调用用名为index的方法。这里也有一个controller的名称/action名称的文件夹来保存views。如果方法没有其它不同,那么这个view将会是controller/action。当然你的action也可以使用其它的view.

    Well, Ruby on Rails supports much more than this, but you got the idea. You might be thinking about how valuable this concept is, and I'll say, from my standpoint, that this is extremely valuable for applications. It enforces simple design and distinct responsibilities: controllers will orchestrate the invocation of the business objects and obtain data from the model; views will only present the data.

                   Ruby on Rail还支持更多的特性,这仅仅是你了解的其中一点。你可能会问这有什么价值呢?从我的观点来说,这特别有用。它强制设计简单并使功能责任明确:controllers调用业务对象并从模型获取数据而views仅仅是显示数据。

    If you compare this approach with ASP.NET, you will see how those values were put off. Each ASP.NET WebForm is like a Visual Basic Form that handles everything: business object invocations - if you're lucky - data ostentation, data binding, input validation, generation of scripts, and at last, the presentation. Don't get me wrong, I like ASP.NET, the whole idea behind controls lifecycle is amazing, and the view state that simulates state in an inherent stateless environment is even greater.

                   将它与ASP.NET比较,你可能会明白这些好处。对于每一个ASP.NETWebFormVB的表单差不多,它处理一切:业务对象调用,数据绑定,输入校验,客户端校本生成到最后的显示。不要误解我的意思,我也非常喜欢ASP.NET,它整个代码后置的思想令人惊讶,使用view state使无状态成为有状态就更有意义。

    But ASP.NET WebForms do not promote good design. You have to be very picky to not put logic that doesn't belong to the presentation on the WebForm so you don't end up with an unmanageable application.

                   但是ASP.NET WebForms不能带来良好的设计。你不得不吹毛求疵将处理逻辑与表现层分开,但那将变得难以管理。

    Castle on Rails

    Castle on Rails is an attempt to provide something similar to Ruby on Rails for the .NET world, but it's not a blind port. As Ruby offers all the beauties of a dynamic language, .NET offers the beauties of static types and attributes. So the goal is trying to combine both worlds.

                   Castle on Rails是一个尝试在.Net里去提供一些与Ruby on Rails相近但不瞎同的项目。对于Ruby提供了优秀的动态语言支持,而.Net则提供了静态类型与特性支持。所以我们的目标是结合两者的优点。

    I've used Castle on Rails on three applications and it has simplified my life and simplified what I code. My controllers end up with little methods and less than a hundred lines of code.

                   我已经在三个项目中使用Castle on Rails,它使我的开发工作变得很简单。我的Controller仅仅少量的方法,节省了几百行代码。

    Please note that Castle on Rails does not intend to replace WebForms, in fact, it uses WebForms as one of the available views engine. It also supports NVelocity as a view engine. My personal choice is NVelocity as it stands exactly as it is supposed to be - just presentation processing.

                   注意Castle on Rails并不是要取代WebForms,实际上它也使用WebForms使为其中的一个views引擎。同时它也支持NVelocity,我个人选择NVelocity来处理表现层。

    How it works

    The working of Rails is pretty straightforward and intentionally similar to the original Ruby on Rails: you have to provide Controllers and Views. Each controller should deal with one specific concern of your web application. The public methods exposed by the controller are accessible actions. The following is a sample controller code:

                   它与Ruby on Rail类似:你可以提供ControllersViews。每个Controller应该只处理Web应用一个特定的问题。公共方法就是这个Controller可访问的Actsions。例如:

    public class HomeController : Controller
    {
      public void Index()
      {
      }
      
      public void ContactUs()
      {
      }
    }

    This specific controller can be accessed from the URL: http://yourserver/home/index.rails or http://yourserver/home/contactus.rails (it's case insensitive). The action body should perform any logic (gathering data, creating or updating table rows, and so on), and then, after the method process, the framework will execute the view. For the above controller, Castle on Rails will search for an ASPX on the view directory: views\home\index.aspx. If you're using the NVelocity view engine, then it will look for views\home\index.vm. The view must exist, otherwise the framework will throw an exception.

                   这个Controller可以通过http://yourserver/home/index.rails http://yourserver/home/contactus.rails来访问(这里大小写不敏感)。action应该进行执行一些任务,如收集数据,创建或更新数据表的记录等等。做完这些,框架将执行view。对于这个ControllerCastle on Rail将会在views目录中查找:views\home\index.aspx。如果你使用NVelocity作为view引擎,那它将会查找views\home\index.vm。这个view必须存在,否则框架将会抛出一个异常。

    The controller can use a different view, though. The code below will render the index view, ignoring the invoked action:

                Controller可以使用不同的View。下面的代码就会忽略调用的action,而呈现index view

    public class HomeController : Controller
    {
      public void Index()
      {
      }
      
      public void ContactUs()
      {
        RenderView("index");
      }
    }

    Instead of extending the Controller class, you can also use the SmartDispatcherController. This specific Controller will try to match the parameters on the request to your method arguments, for example:

                   除了继承Controller类,还可以继承SmartDispatcherController。这个Controller可根据你请求的参数来匹配带相应参数定义的方法,如:

    public class AccountController : SmartDispatcherController
    {
      public void New()
      {
      }
      
      public void CreateAccount(String name, String login, String password)
      {
        ...
      }
    }

    For the above controller, you can use the URL: http://yourserver/account/createaccount.rails?name=hammett&login=hammett&password=mypass.

                   使用http://yourserver/account/createaccount.rails?name=hammett&login=hammett&password=mypass这个URL使用上面这个Controller了。

    Castle on Rails also supports:

                   Castle on Rails同时支持:

    • Filters allowing you to associate a pre and post processing of the action.
    • Rescues allowing you to associate an error page per controller and per method.
    • Layouts allowing you to associate a master page.
    • Filters 使你可以在action处理过程前后联系;
    • Rescures 如果Controller执行失败,你可以关联特定的错误而到特定的Controller甚至方法;
    • Layouts使关联到一个master页。

    We are going to use most of these features to achieve the requirement for our application, so rest ensured that they will be covered somehow. Castle on Rails can be used as a standalone framework, or integrated with WindsorContainer. This allows you to use the juicy abilities of an inversion of control container.

                   为了实现我们的需求,我们将使这些功能。Castle on Rails是一个能独立使用的框架,同时你也将它与WindsorContainer集成,这样的话你同时可以使用许多IOC容器的有趣功能。

    The damn simple "architecture"

    No fancy things, please! We just need a few layers:

                   无需多想,我们需要:

    1.      Presentation: composed of Controllers and Filters.

    2.      Services: just a business abstraction layer between the Presentation and Data access. It's also responsible for the transaction boundaries.

    3.      Data Access Objects: or simple DAOs. We're going to use NHibernate to actually save, update and request data.

    1.表现层:包括ControllersFilters
    2
    服务层:位于表现层与数据访问层之间的业务抽象层。包括处理事务的职责;
    3.
    数据访问对对象:或者简单的DAO对象。在这里我们将使用NHibernate来保存更新与获取数据。

    However, we'll use a fancy publisher/subscriber just to know when a new blog was created. This is important so we can register a new controller for it.

                   然而我们将假设当一个Blog创建之后,publisher/subscriber是知道它的。这非常重要,因为只有这样我们才可以注册一个新的Controller给这个Blog

    The following picture depicts the components and their interaction:

                   下图描述了这些组件及它们之间的关系。

    By the way, the picture is a screenshot of a desktop application called Castle's Component Viewer. It instantiates your application's container and obtains its Component Graph. The Component Viewer is still on early stages, but will be a good addition to the whole Castle Project.

                   插一句,这个图片是Castle’s Component Viewer这个桌面应用程序的截屏。它可以将你的应用容器与其中的组件的图形化表示。虽然这个组件查看器还处在起步阶段,但它将成为整个Castle项目的一部分。

    The project structure

    I hope you agree with me, but I think we need the following separated projects:

                   为了实现它们,我们需要三面三个工程:

    • Castle.Applicatins.MindDump: to hold Controllers, DAOs, Services.
    • Castle.Applicatins.MindDump.Tests: to test the DAOs and Services - we could also test the Controllers.
    • Castle.Applicatins.MindDump.Web: ASP.NET application with the views.
    • Castle.Applications.MindDump:包括ControllersDAOsServices
    • Castle.Applications.MiniDump.tests:使用它去测试DAOs,Services以及Controllers
    • Castle.Applications.MindDump.Web:包含的ViewsASP.NET应用;

    Now that we've agreed, let's go on.

                   好了,让我们开始吧。

    Creating the MindDump application

    Of course, I won't explain each step taken to create the application, but I intend to illustrate a few of them so you can easily learn the rest from the source code provided.

                   我当然不会一步步进讲述怎样创建这个应用,但是我会特别指出其中一些地方以方便你学习附件中的源码。

    First of all, I've created two configuration files, one for production and one for the test cases. I've also used two Catalogs on MySQL, so the test cases could delete all the rows before starting the tests.

                   首先,我创建了两个配置文件,其中一个是给应用程序准备的,另一个为了测试案例准备的。

    The configuration files were used to configure the NHibernate facility, see below:

                   它们是给NHibernateFacility使用的,请看:

    <facilities>
        <facility id="nhibernate">
            <factory id="nhibernate.factory">
                <settings>
                    <item key="hibernate.connection.provider">
                      NHibernate.Connection.DriverConnectionProvider
                    </item>
                    <item key="hibernate.connection.driver_class">
                      NHibernate.Driver.MySqlDataDriver
                    </item>
                    <item key="hibernate.connection.connection_string">
                      Database=minddump;Data Source=localhost;
                                        User Id=theuser;Password=user
                    </item>
                    <item key="hibernate.dialect">
                      NHibernate.Dialect.MySQLDialect
                    </item>
                </settings>
                <resources>
                    <resource name="..\bin\Author.hbm.xml" />
                    <resource name="..\bin\Blog.hbm.xml" />
                    <resource name="..\bin\Post.hbm.xml" />
                </resources>
            </factory>
        </facility>
    </facilities>

    A good practice to simplify your development is to extend the WindsorContainer just to provide your initialization semantics and preferences and in a small reusable unit. For example, for this project, I've created the MindDumpContainer class. If you just instantiate it, it will use the production configuration file. Or you can use the other constructor to specify a different file - the test cases do exactly that.

                   继承WindsorContainer来提供初始化选项并将它们在小范围内重用来简化开发是一个好主意。如是果你这样,它就会使用这个配置文件。你也可以使用其它的方法来指向特定的文件,测试用例就是这样做的。

    public class MindDumpContainer : WindsorContainer
    {
        public MindDumpContainer() : 
               this( new XmlConfigurationStore("../app_config.xml") )
        {
        }
     
        public MindDumpContainer(IConfigurationStore store) : base(store)
        {
            Init();
        }
     
        public void Init()
        {
            RegisterFacilities();
            RegisterComponents();
        }
     
        ...

    DAOs and Services

    Now, I assume that you have some knowledge about NHibernate. To access the database, you need to create and open a NHibernate's Session, perform your work, and finally close it. Castle's NHibernate Facility can take care of this repetitive task, you just need to use the UsesAutomaticSessionCreation attribute. Follows the AuthorDao's code:

                   在这里,我假设你已经有一些NHibernate的知识。如访问数据库,需要新建并打开一个Session,完成后需要关闭它。Castle’s NHibernate Facility注意到了这些重复的工作,你只需使用UseAutomatiocSessionCreation特性就可以了。就像下面AuthorDao的代码一样:

    [UsesAutomaticSessionCreation]
    public class AuthorDao
    {
        public virtual Author Create(Author author)
        {
            ISession session = SessionManager.CurrentSession;
     
            if (author.Blogs == null)
            {
                author.Blogs = new ArrayList();
            }
     
            session.Save(author);
     
            return author;
        }
     
        public virtual IList Find()
        {
            return SessionManager.CurrentSession.Find("from Author");
        }
     
        ...
    }

    The others DAOs don't have anything much different from this one. We are now free to code the service layer. If the classes involve modification in more than one DAO, or more than one modification on the same DAO, it's necessary to use transactions. The NHibernate Facility detects a transaction on the thread and enlists the Session automatically. So here is the service responsible for creating an account and a blog:

                   其它的DAOs与它类似。现在让我看Service层的代码:当修改多个DAO对象或者对同一个DAO作多个修改时,需要事务的支持。NHibernate Facility可以在线程别自动进行事务处理。所以服务类只需关心创建用户帐号与Blog就可以了。

    [Transactional]
    public class AccountService
    {
        private AuthorDao _authorDao;
        private BlogDao _blogDao;
     
        public AccountService(AuthorDao authorDao, BlogDao blogDao)
        {
            _authorDao = authorDao;
            _blogDao = blogDao;
        }
     
        [Transaction(TransactionMode.Requires)]
        public virtual void CreateAccountAndBlog( Blog blog )
        {
            _authorDao.Create( blog.Author );
            _blogDao.Create( blog );
        }
     
        ...
    }

    Easy, huh?

                   简单吧,哈哈!

    The Controllers

    The start point of our application lies on the Intro controller. It does do much, just presents a list of recent blogs and recent posts. Please remember that the constructor's arguments will be resolved by the container.

                   我们这个应用的入口是Intro这个Controller。它需要列表最近的BlogPosts。请注意构造函数的参数将会由IOC容器来处理。

    [Layout("default")]
    public class IntroController : Controller
    {
        private BlogService _blogService;
     
        public IntroController(BlogService blogService)
        {
            _blogService = blogService;
        }
     
        public void Index()
        {
            PropertyBag.Add( "blogs", _blogService.ObtainLatestBlogs() );
            PropertyBag.Add( "posts", _blogService.ObtainLatestPosts() );
        }
    }

    On this application, we're using the NVelocity View Engine, so follows the contents of the view Intro\Index.vm:

                   我们在这个应用中使用NVelocity视图引擎,下面是Intro\Index.vm这个View的内容:

    <p>
    MindDump is a simple blog application. To create your blog and start to dump 
    your thoughts and opinions, please create 
    <a href="/account/new.rails">an account</a>.
    </p>
    <p>
    If you have an account, 
    please <a href="/account/authentication.rails">log on</a>
    </p>
    <br>
    <font size="+1">Last updates</font>
    <br>
    <p>
    <table width="100%" border="1">
    #foreach( $post in $posts )
      <tr>
        <td>
          <a href="/${post.blog.author.login}/view.rails?entryid=${post.id}"
          >$post.title
        </td>
        <td>$post.date</td>
      </tr>
    #end
    </table>
    </p>
    <br>
    <font size="+1">Blogs registered</font>
    <br>
    <p>
    <table width="100%" border="1">
    #foreach( $blog in $blogs )
      <tr>
        <td><a href="/$blog.author.login/view.rails">$blog.name</td>
        <td>$blog.description</td>
      </tr>
    #end
    </table>

    As you can see, this is just the child contents of a bigger page. That's because the Controller is using the Layout feature. As you can see, it's using the "default" layout. In practice, the NVelocity will look for a "default.vm" under the layouts directory:

                   正如你所见,这个页面有子内容。这是因为使用了Layout。它使用了Default这个Layout,运行时,NVelocity将会在Layouts目录中查找Defalut.vm

    <html>
    <head>
    <title>MindDump</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <link href="/css/main.css" rel="stylesheet" type="text/css">
    </head>
     
    <body>
    <div>
      <img src="/images/logo.jpg">
    </div>
    <br> <br>
     
    $childContent
        
    <p>
    <br> <br>
    <hr width="80%" size="1" noshade>
    <div align="center">
    Copyright (c) 2005 - Castle Project Team</div>
    </p>
     
    </body>
    </html>

    Secured areas

    We have the following requirement to keep in mind: "the user must be logged on to post texts on his blog". We can perform this check on the sensitive actions on each controller or we can think about a more clever solution. Well, I'd say that the best way to solve this problem is by creating a filter that checks whether the user trying to access the Controller is authenticated. If he's not, the filter redirects the user to a login page and returns false, thus preventing the action of being processed.

                   我们必须记得“用户只在登录到他的Blog后才可以发表文章”。这可以在每个相关的actions中来执行这个验证,要不我们就要想个更好的办法了。是的,我们可以通过创建一个Filter来访问Controller的用户是否授权了,如果没有,将重定向到用户登录页面并返回一个False值,以使停止相应的Action

    public class AuthenticationCheckFilter : IFilter
    {
        private EncryptionService _encryptionService;
        private AccountService _accountService;
     
        public AuthenticationCheckFilter(AccountService accountService, 
            EncryptionService encryptionService)
        {
            _accountService = accountService;
            _encryptionService = encryptionService;
        }
     
        public virtual bool Perform(ExecuteEnum exec, 
               IRailsEngineContext context, Controller controller)
        {
            if (!PerformAuthentication(context))
            {
                context.Response.Redirect("account", "authentication");
                return false;
            }
     
            return true;
        }
     
        protected bool PerformAuthentication(IRailsEngineContext context)
        {
            String contents = 
                   context.Request.ReadCookie("authenticationticket"); 
     
            if (contents == null)
            {
                return false;
            }
     
            String login = _encryptionService.Decrypt(contents);
     
            Author author = _accountService.ObtainAuthor(login);
     
            if (author == null)
            {
                return false;
            }
     
            context.CurrentUser = new PrincipalAuthorAdapter(author);
            
            return true;
        }
    }

    Then we create a base class for every controller that needs an authenticated user and that's it:

                   然后就可以为每一个需要使用用户验证的Controller创建一个基类:

    [Filter(ExecuteEnum.Before, typeof(AuthenticationCheckFilter) )]
    public abstract class AbstractSecureController : SmartDispatcherController
    {
     
    }

    The most complex Controller is exactly the one responsible for creating, changing an account and performing the authentication. It has 120 lines of code :-)

                   最复杂的是创建,修改帐号并执行用户验证的Controller,它有120行代码:-)

    Also note that some of the methods are decorated with SkipFilter so they can be accessed without the authentication checking.

                   注意一些方法标注了SkipFilter特性,这样它们就可以不用验证用户授证就可以使用的。

    [Layout("default")]
    public class AccountController : AbstractSecureController
    {
        private AccountService _accountService;
        private AuthenticationService _authenticationService;
        private EncryptionService _encryptionService;
     
        public AccountController(AccountService accountService, 
            AuthenticationService authenticationService, 
            EncryptionService encryptionService)
        {
            _accountService = accountService;
            _authenticationService = authenticationService;
            _encryptionService = encryptionService;
        }
     
        [SkipFilter]
        public void New()
        {
        }
     
        [SkipFilter]
        [Rescue("errorcreatingaccount")]
        public void CreateAccount(String login, String name, String email,
                              String pwd, String pwd2, String blogname,
                              String blogdesc, String theme)
        {
            // Perform some simple validation
            if (!IsValid(login, name, email, pwd, 
                    pwd2, blogname, blogdesc, theme))
            {
                RenderView("new");
                return;
            }
     
            Author author = new Author(name, login, pwd);
            Blog blog = new Blog(blogname, blogdesc, theme, author);
     
            _accountService.CreateAccountAndBlog( blog );
     
            // Done, now lets log on into the system
            PerformLogin(login, pwd);
        }
     
        [SkipFilter]
        public void Authentication()
        {
        }
     
        [SkipFilter]
        public void PerformLogin(String login, String pwd)
        {
            if (!_authenticationService.Authenticate(login, pwd))
            {
                Context.Flash["errormessage"] = 
                      "User not found or incorrect password.";
            
                RenderView("Authentication");
            }
            else
            {
                DateTime twoWeeks = DateTime.Now.Add( new TimeSpan(14,0,0,0) ); 
     
                Context.Response.CreateCookie("authenticationticket", 
                    _encryptionService.Encrypt(login), twoWeeks );
                
                Redirect("Maintenance", "newentry");
            }
        }
        ...
    }

    I'm afraid now you know as much about Castle on Rails as I do :-)

                   恐怕你现在了解Castle on Rails和我一样多了吧:-

    The test cases

    Testing a loosely coupled application is very simple. For example, follows a few test cases for one of the DAOs:

                   测试非常简单,比如一个DAO可以有几个测试用例:

    [TestFixture]
    public class AuthorTestCase : BaseMindDumpTestCase
    {
        [Test]
        public void Create()
        {
            ResetDatabase();
     
            AuthorDao dao = (AuthorDao) Container[ typeof(AuthorDao) ];
            Assert.AreEqual( 0, dao.Find().Count );
     
            Author author = new Author("hamilton verissimo", "hammett", "mypass");
     
            dao.Create( author );
     
            IList authors = dao.Find();
            Assert.AreEqual( 1, authors.Count );
     
            Author comparisson = (Author) authors[0];
            Assert.AreEqual( author.Name, comparisson.Name );
            Assert.AreEqual( author.Login, comparisson.Login );
            Assert.AreEqual( author.Password, comparisson.Password );
        }
     
        ...

    I've lived in hell five years ago when we had to cope with a complex and untesteable application in C++. It's incredible how much one can learn from their bad experiences... ;-)

                   五年前,对付复杂难以测试的C++应用就象生活在地狱中一样。那种学习经历还习想象J

    The Web Application

    In order to use Castle on Rails with the WindsorContainer, you need to:

                   为了和WindsorContainer一起使用Castle on Rails,你需要:

    • Add a few entries to web.config.
    • Make the container available through the HttpApplication.
    • 需要在Web.config中添加一配置项。
    • 确保WindsorContainer容器在HttpApplication中有效。

    Might sound complex, but it isn't.

                   听起也许很复杂,其实不然。

    First, the web.config. You need to say to Castle on Rails which View engine you wish to use and which Controller and Filter Factories it should use. Don't forget to associate the extension "rails" with the ASP.NET ISAPI if you're using IIS. Follows the web.config contents:

                   首先,web.config需要配置项来让Castle on Rails是使用那个View引擎以及使用那个ControllerFilter工厂类。如果使用IIS同时不要忘记将扩展名“rails”与ASP.NET ISAPI关联:

    <?xml version="1.0" encoding="utf-8" ?> 
    <configuration>
     
      <configSections>
        <section name="rails"
        type="Castle.CastleOnRails.Engine.Configuration.RailsSectionHandler, 
                  Castle.CastleOnRails.Engine" />
      </configSections>
     
      <rails>
        <viewEngine 
          viewPathRoot="views" 
          customEngine=
           "Castle.CastleOnRails.Framework.Views.NVelocity.NVelocityViewEngine, 
            Castle.CastleOnRails.Framework.Views.NVelocity" />
        <customFilterFactory 
          type="Castle.CastleOnRails.WindsorExtension.WindsorFilterFactory, 
                Castle.CastleOnRails.WindsorExtension" />
        <customControllerFactory 
          type="Castle.CastleOnRails.WindsorExtension.WindsorControllerFactory, 
                Castle.CastleOnRails.WindsorExtension" />
      </rails>
     
      <system.web>
        <httpHandlers>
          <add verb="*" path="*.rails" 
          type="Castle.CastleOnRails.Engine.RailsHttpHandlerFactory, 
                Castle.CastleOnRails.Engine" />
        </httpHandlers>
      </system.web>
     
    </configuration>

    Now, just create a global.asax if you don't have one, and associate the following code with it:

                   如果还没有global.asax文件就创建一个,并将它与下的代码关联:

    public class MindDumpHttpApplication : HttpApplication, IContainerAccessor
    {
        private static WindsorContainer container;
     
        public void Application_OnStart() 
        {
            container = new MindDumpContainer();
        }
     
        public void Application_OnEnd() 
        {
            container.Dispose();
        }
     
        public IWindsorContainer Container
        {
            get { return container; }
        }
    }

    Conclusion

    The sample code will probably lower your skeptical level. In the next article, I'll talk about the ActiveRecord Facility and anything else you might consider important.

                   示例代码可能会降低你的怀疑度。下一篇文章,我会谈及ActiveRecord Facility以及其它可能认为比较重的东东。

    Castle still is on the beta stage, but the public API is very unlikely to change. The team is growing as we develop a healthy community.

                   Castle仍然在beta阶段,但公开的API很少会改变。团队也因互相交流在不断壮大。

    Please contact the team through the mailing list on Castle Project site.

    History

    • 25-Jan-2005 - Initial version.

    About Hamilton Verissimo

     

    Hamilton Verissimo has been working with software development for 9 years. He's involved with a lot of open source projects to fill his spare time.

    Click here to view Hamilton Verissimo's online profile.

     

  • 相关阅读:
    k8s采坑记
    [dotnet] 封装一个同时支持密码/安全密钥认证的SFTP下载器,简单易用。
    亲测可用,iptables实现NAT转发。
    【转】干货,Kubernetes中的Source Ip机制。
    k8s实践
    干货!分享一款windows下的磁盘分析神器。
    干货,不小心执行了rm -f,除了跑路,如何恢复?
    Java8函数式编程
    搭建git服务器
    Python3安装
  • 原文地址:https://www.cnblogs.com/linwinfan/p/284933.html
Copyright © 2011-2022 走看看