asp.net MVC與RAILS3的比較
進入后Web年代之后,MVC框架進入了快速演化的時代,Struts等垂垂老矣的老一代MVC框架因為開發效率低下而逐漸被拋棄,新一代的MVC則高舉敏捷的大旗,逐漸占領市場,其中的代表有Rails (ruby), .NET MVC (.NET), Django (Python),Symfony (php)等等,這些框架的思想都大同小異,這里列舉出Rails3和.NET MVC的一些的區別,以方便Web開發者從Rails遷移到.NET MVC,或者反之,從.NET MVC遷移到Rails.生成項目Rails和.NET MVC都能夠產生項目的基本骨架,只是生成的方式略有不同,Rails采用的是命令行的方式:
java代碼
- railstapir
而Microsoft則秉承其強大的IDE,提供了項目向導。最終得到的目錄結構,僅在測試和配置項上略有不同。
| Rails | ASP.NET MVC |
| /app/models | /Models |
| /app/controllers | /Controllers |
| /app/views | /Views |
| /public/javascript | /Scripts |
| /public | /Content |
| /db | /App_Data |
| /test | 單獨的VS項目 |
| /config | /Global.asax, /PRoperties, Web.config |
值得一提的是rails的一個亮點:rails可以預先配置三個不同的環境:開發、測試、最終產品,可以通過RAILS_ENV這個環境變量來做簡單切換,.NET MVC并未提供這樣的配置環境,你可以通過手工配置來完成。模型ModelRails默認采用ActiveRecord作為模型,當然切換到其他的框架也很簡單,可選項有 Neo4J, MongoDB,和DataMapper。在Rails中,還是采用命令行來創建模型,Rails會生成一些骨架代碼,包括:模型、遷移任務和測試。你可以用-o來選擇其他模型、-t來選擇其他測試框架:
Java代碼
- $railsgmodelcustomername:stringemail:string
- invokeactive_record
- createdb/migrate/20100419094010_create_customers.rb
- createapp/models/customer.rb
- invoketest_unit
- createtest/unit/customer_test.rb
- createtest/fixtures/customers.yml
Rails默認采用Sqlite3作為后臺數據庫,而且Rails會很貼心的為開發、測試、生產三個環境分別產生一個數據庫拷貝。在Rails中,所有的數據庫的操作都通過腳本和遷移來完成,Rails中的遷移應該是最有價值的一個東西,當不同的開發者同時在修改一個數據庫,或者您在升級現有的生產環境下的數據庫,遷移就顯示出它的強大威力:
Java代碼
- classCreateCustomers<ActiveRecord::Migration
- #Calledwhenmigratinguptothisversion
- defself.up
- create_table:customersdo|t|
- t.string:name
- t.string:email
- t.timestamps
- end
- end
- #Calledwhenmigratingdownfromthisversion
- defself.down
- drop_table:customers
- end
- end
我們可以通過rake db:migrate命令遷移到不同的數據庫版本上去。和Rails不同的是,.NET MVC并為綁定一個模型框架,你要從既有的框架中選擇一個適合你的,這個名單里可以用Nhibernate,Linq to SQL, Entity Framework,Castle ActiveRecord或者Ruby的ActiveRecord,不過.NET MVC沒有遷移的概念,這有點遺憾。大部分情況下Linq To SQL就很適合項目開發。查詢語言Rails3使用AREL(Active Record Relations),LINQ-to-SQL則使用LINQ。 二者都是相當優美的語言
Java代碼
- #AsimplequerywithAREL
- User.where(users[:name].eq('Anders')).order('users.idDESC').limit(20)
Java代碼
- //ThesamewithC#
- //LambdaSyntax
- db.Users.where(u=>u.Name=="Anders").orderBy(u=>u.Id).Take(20)
- //LINQSyntax
- (fromuindb.Users
- whereu.Name=="Anders"
- orderbyu.Iddescending
- selectu).Take(20);
現在除了在.NET中采用Ruby的ActiveRecord(借助ironruby),目前還沒有其他框架提供類似Ruby的findbyXXX的功能,不過C# 4.0的method_missing使得這類框架應該會很快出現(比如Nhibernate 3.0)控制器在.NET MVC中,你在Controller目錄上點添加,就有很貼心的向導引導你為項目添加控制器,甚至還可以增加基本的CRUD的功能。
Java代碼
- publicclassCustomersController:Controller{
- //GET:/Customers/
- publicActionResultIndex(){
- returnView();
- }
- //GET:/Customers/Details/5
- publicActionResultDetails(intid){
- returnView();
- }
- //GET:/Customers/Create
- publicActionResultCreate(){
- returnView();
- }
- //POST:/Customers/Create
- [HttpPost]
- publicActionResultCreate(FormCollectioncollection){
- try{
- //TODO:Addinsertlogichere
- returnRedirectToAction("Index");
- }catch{
- returnView();
- }
- }
- }
和Rails的腳手架代碼一樣,這些最基本的代碼99%會被廢棄,但是提供了“讓程序跑起來看看”的基礎。Rails還是通過命令行來為項目增加控制器,你還可以在命令行里制定為控制器生成哪些Action。過濾器Rails很容易為某個Action添加個過濾器
Java代碼
- classItemsController<applicationController
- before_filter:require_user_admin,:only=>[:destroy,:update]
- before_filter:require_user,:only=>[:new,:create]
- end
.NET也不含糊,只要重載OnActionExecuting就可以實現同樣的功能:
Java代碼
- overridevoidOnActionExecuting(ActionExecutingContextfilterContext)
- {
- varaction=filterContext.ActionDescriptor.ActionName;
- if(newList<string>{"Delete","Edit"}.Contains(action)){
- RequireUserAdmin();
- }
- if("Create".Equals(action)){
- RequireUserAdmin();
- }
- }
或者通過.NET的attribute更漂亮的完成
Java代碼
- [RequireUserAdmin("Delete","Edit")]
- [RequireUser("Create")]
- publicclassCustomersController:Controller
路由在Rails中,可以修改routes.rb來修改路由,默認的Rails的路由被配置成RESTful:
Java代碼
- Tapir::Application.routes.drawdo|map|
- resources:animals
- get"customer/index"
- get"customer/create"
- match"/:year(/:month(/:day))"=>"info#about",
- :constraints=>{:year=>//d{4}/,
- :month=>//d{2}/,
- :day=>//d{2}/}
- match"/secret"=>"info#about",
- :constraints=>{:user_agent=>/Firefox/}
- end
通過rake routes你可以快速查看路由的結果。ASP.NET MVC的路由稍微復雜一些,不過同樣強大:
Java代碼
- //Global.asax.cs
- publicclassMvcApplication:System.Web.HttpApplication{
- publicstaticvoidRegisterRoutes(RouteCollectionroutes){
- routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
- //Constrainedroute
- routes.MapRoute("Product","Product/{productId}",
- new{controller="Product",action="Details"},
- new{productId=@"/d+"});//Constraint
- //Routewithcustomconstraint,definedbelow
- routes.MapRoute("Admin","Admin/{action}",
- new{controller="Admin"},
- new{isLocal=newLocalhostConstraint()});
- }
- ...
- }
- publicclassLocalhostConstraint:IRouteConstraint{
- publicboolMatch(HttpContextBasehttpContext,Routeroute,
- stringparameterName,RouteValueDictionaryvalues,
- RouteDirectionrouteDirection)
- {
- returnhttpContext.Request.IsLocal;
- }
- }
View二者在View上的表現十分接近,添加控制器的時候,會自動創建相應的視圖,規則也類似:視圖所在的文件夾以控制器的名字命名,視圖的文件名則以控制器的action命令,二者也都提供了從某個模型創建腳手架視圖的能力。PartialsRails和Asp.NET MVC都提供了在文件中包含部分HTML文件能力,ASP.NET MVC的文件采用ASP,而Rails默認是ERB或HAML.
Java代碼
- <!--Rails-->
- <%=render'form'%>
Java代碼
- <!--ASP.NETMVC-->
- <%Html.RenderPartial("Form",Model);%>
.NET MVC 2中更做出了一些改進,提倡用2個替代的方法來產生代碼:
Java代碼
- <%=DisplayFor("Address",m=>m.Address)%>
- <%=Edit