星期五, 11月 25, 2011

簡單的Text Templating

最近在處理範本的問題,Bibby 習慣慣把範本寫成一個一個的text檔,然後再寫一個Parser去解析,最後用自己寫好的 class 處理,今天看到一個蠻不錯的 extention,可以很快速的解處理這個問題,在這裡分享給大家

我們先開啟 console application 再開啟NuGet

clip_image001

下載GazorGenerator

clip_image002

安裝完直接在目錄下會多個 SampleTemplate.cshtml 檔案,這是範本測試檔,我們直接在 Program.cs 加入下列這段code 就可以跑了

            var template = new SampleTemplate()
{
Message = "Bibby",
};
Console.WriteLine(template.TransformText());
Console.ReadLine();

我們可以看到畫面是

clip_image001[4]

這樣程式就寫完了,夠快吧! 不過我們一定會想,為啥會這樣就寫完了,我們看剛剛那個 SampleTemplate.cshtml 還有 SampleTemplate.generated.cs 就大概就明瞭了

SampleTemplate.cshtml

@* Generator: Template *@

@functions {
public string Message { get; set; }
}

Hello @Message!
SampleTemplate.generated.cs

#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.239
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace RazorTemplateTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "1.2.0.0")]
public partial class SampleTemplate : RazorGenerator.Templating.RazorTemplateBase
{
#line hidden

#line 3 "..\..\SampleTemplate.cshtml"

public string Message { get; set; }

#line default
#line hidden

public override void Execute()
{


WriteLiteral("\r\n\r\n");


WriteLiteral("\r\n\r\nHello ");



#line 7 "..\..\SampleTemplate.cshtml"
Write(Message);


#line default
#line hidden
WriteLiteral("!\r\n");


}
}
}
#pragma warning restore 1591

SampleTemplate.generated.cs是自動產生出來的,裡面程式都實作完了,所以畫面就會是你想要的結果了,那要怎麼設定才有自動產生的功能呢?我們看一下 SampleTemplate.cshtml 的Properties

clip_image001[6]

就是在 Custom Tool 上面加入RazorGenerator,加入後在 cshtml 有任何改變 generated.cs 就會自動再產生一次,設定很簡單,接下來我們就按圖施工新增一個 MailTemplate.cshtml 來試著完成一個需求

MailTemplate.cshtml

@* Generator : Template TypeVisibility : Internal *@
@functions {
public dynamic Model { get; set; }
}
Hi! @Model.Name

非常感謝您申請 @Model.Domain
以下是 @Model.Name 的登入情報。請妥善保管。
(此郵件由自動送信程序發送。)

郵件地址: @Model.Email
密碼 : @Model.Password
登入URL : http://@Model.Domain

-----------------------------
@Model.Domain, inc. http://@Model.Domain

然後在 MailTemplate.cshtml 的 Properties 加入 RazorGenerator 就行了,最後在 Program.cs 上加入

            var mailTemplate = new MailTemplate()
{
Model = new {
Name="Bibby",
Password="1234",
Domain = "Bibby.be",
Email="admin@bibby.be",
}
};
Console.WriteLine(mailTemplate.TransformText());

結果:

clip_image001[8]

這樣就完成我們當初的需求了,收工!不過這方式是有缺點啦,因為要改 cshtml 的時候 generated.cs 會重新產生,這樣對於沒有 visual studio 或是不是 developer 是有問題的,如果有這種需求那要參考我好朋友 Wade的文章 用Razor語法寫範本-RazorEngine組件介紹 這種方式,這樣就可以用程式動態去處理,試試看吧!有問題再一起討論吧!

參考:
http://razorgenerator.codeplex.com/

http://haacked.com/archive/2011/08/01/text-templating-using-razor-the-easy-way.aspx

星期日, 11月 20, 2011

Config the Elmah with Sqlite

之前Bibby都把elmah放在mssql裡面,可是遇到一個問題,如果專案沒有mssql的時候,就得改成xml的方式來做,改成xml來做其實也沒有啥不好,只是龜毛的我不喜歡xml那樣,一天一個xml的,看起來有點礙眼,之前閱讀到elmah的文件,有看過是支援sqlite的,所以乾脆以後都把elmah功能寫到sqlite裡面好了,這樣就不同的專案就可以用相同的解決方法處理了
不過在套sqlite的時候,其實不是這麼順利,官方文件(wiki)沒有寫的很清楚,花了一會才用成功,在這裡紀錄一下,以後大家參考就不用浪費時間再跟我做一樣的事情了,接下來就看圖說故事..

clip_image001

先在Reference上面按右鍵選擇NuGet

clip_image002

用搜尋的方式,先把System.Data.SQLite裝上

clip_image003

再裝上ELMAH,這樣元件都裝好了,NuGet真的超級方便..讚..

接下來我們開始設定web.config檔,我們先看一下官方的設定檔好了(這設定檔我的官方網站在鬼打牆,一直找不到)

<!--
Use to log errors to SQLite using ASP.NET 2.0.
Set value of connectionStringName attribute to
the name of the connection string settings to
use from the <connectionStrings> section.
<errorLog type="Elmah.SQLiteErrorLog, Elmah" connectionStringName="..." />
The format of the connection string should be as follows:
<connectionStrings>
<add name="..." connectionString="data source=[path to the database file]" />
</connectionStrings>
Replace the content of the brackets with a file name, for example:
data source=C:\Elmah.db
If the database file doesn't exist it is created automatically.
To specify a path relative to and below the application root,
start with the root operator (~) followed by a forward slash (/),
as it is common in ASP.NET applications. For example:
data source=~/App_Data/Error.db
-->

上面的設定檔很清楚,所以我們修改web.config就行了

  <connectionStrings>
  <add name="elmah" connectionString="data source=~/App_Data/elmah.db" />
</connectionStrings>
<elmah>
  <errorLog type="Elmah.SQLiteErrorLog, Elmah" connectionStringName="elmah" />
</elmah>

把上面這段加在web.config裡面,這樣就可以使用了,第一次的時候elmah會很貼心的幫你產生出sqlite.db的,這時你就可以用資料庫工具看到資料已經寫入了sqlite
clip_image001[4]

測試一下頁面也沒問題..

clip_image002[4]

這樣就完成我們的需求了,有問題再討論吧!

參考:
http://code.google.com/p/elmah/w/list

星期四, 11月 17, 2011

mvc3 自動加的 client 驗證

日前在處理mvc3 client的驗證問題,發現到如果在型別是int或是decimal等都會自動幫你加入驗證,這一點對Bibby我是蠻困擾的,Bibby驗證都是自己加的,自己來才是王道!下列就是mvc3"假伯"幫我們加的東西..

自動幫妳加入錯誤訊息
The field Money must be a number.

產生的html code

<label for="Money">Money</label>:
<input class="input-validation-error text-box single-line" data-val="true"
data-val-number="The field Money must be a number."
id="Money" name="Money" type="text" value="" />
<span class="field-validation-error" data-valmsg-for="Money" data-valmsg-replace="true">
A value is required.</span>

接下來就是尋找如何解決這ox的問題,找了一下發現原來是ClientDataTypeModelValidatorProvider幫我們加上去的,我們看一下source code

/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This software is subject to the Microsoft Public License (Ms-PL).
* A copy of the license can be found in the license.htm file included
* in this distribution.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/

namespace System.Web.Mvc{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.Mvc.Resources;

public class ClientDataTypeModelValidatorProvider : ModelValidatorProvider {

private static readonly HashSet<Type> _numericTypes = new HashSet<Type>(new Type[] {
typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort),
typeof(int), typeof(uint),
typeof(long), typeof(ulong),
typeof(float), typeof(double), typeof(decimal)
});

public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) {
if (metadata == null) {
throw new ArgumentNullException("metadata");
}
if (context == null) {
throw new ArgumentNullException("context");
}

return GetValidatorsImpl(metadata, context);
}

private static IEnumerable<ModelValidator> GetValidatorsImpl(ModelMetadata metadata, ControllerContext context) {
Type type = metadata.ModelType;
if (IsNumericType(type)) {
yield return new NumericModelValidator(metadata, context);
}
}

private static bool IsNumericType(Type type) {
Type underlyingType = Nullable.GetUnderlyingType(type); // strip off the Nullable<>
return _numericTypes.Contains(underlyingType ?? type);
}

internal sealed class NumericModelValidator : ModelValidator {
public NumericModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
: base(metadata, controllerContext) {
}

public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
ModelClientValidationRule rule = new ModelClientValidationRule() {
ValidationType = "number",
ErrorMessage = MakeErrorString(Metadata.GetDisplayName())
};

return new ModelClientValidationRule[] { rule };
}

private static string MakeErrorString(string displayName) {
// use CurrentCulture since this message is intended for the site visitor
return String.Format(CultureInfo.CurrentCulture, MvcResources.ClientDataTypeModelValidatorProvider_FieldMustBeNumeric, displayName);
}

public override IEnumerable<ModelValidationResult> Validate(object container) {
// this is not a server-side validator
return Enumerable.Empty<ModelValidationResult>();
}
}

}
}

上面的code很清楚,在"那些"型別,都會幫你加上驗證,所以知道"人是誰殺的"那就閹掉他吧XD..解決方法請在Global.asax裡面加入下段的code

            var cdProvider = ModelValidatorProviders.Providers
.SingleOrDefault(p => p.GetType().Equals(typeof(ClientDataTypeModelValidatorProvider)));
if (cdProvider != null)
{
ModelValidatorProviders.Providers.Remove(cdProvider);
ModelValidatorProviders.Providers.Add(new CustomClientDataTypeModelValidatorProvider());
}

用借屍還魂的方式,再新增一個去代替內建的就可以了

    public class CustomClientDataTypeModelValidatorProvider : ModelValidatorProvider
{
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
{
return Enumerable.Empty<ModelValidator>();
}
}

就可以解決了,有問題在一起討論吧!

參考:
http://forums.asp.net/t/1512140.aspx/2/10