2014年8月28日 星期四

個人常用Log格式筆記

個人常用的Log格式,在此記錄一下

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true">

    <!-- 
  See https://github.com/nlog/nlog/wiki/Configuration-file 
  for information on customizing logging rules and outputs.
   -->
    <targets async="true">
        <default-wrapper xsi:type="BufferingWrapper" bufferSize="500" flushTimeout="3000" />

        <target xsi:type="Chainsaw" name="console" address="udp4://127.0.0.1:7071"
                layout="${longdate} ${uppercase:${level}} ${message}" />

        <target xsi:type="File" name="file" fileName="D:\LogFiles\Proj_Log\${shortdate}\${date:format=yyyy-MM-dd-HH}.log"
                  layout="${longdate} ${uppercase:${level}} ${message}${newline}${onexception:inner=${newline}Exception:${newline}${exception:format=ToString}${newline}}STACKTRACE:${newline}${stacktrace:format=DetailedFlat}" />
    </targets>

    <rules>
        <logger name="*" minlevel="Trace" writeTo="console,file" />
    </rules>
</nlog>

2014年8月26日 星期二

Select2套件搭配MVC專案

首先開一個MVC專案

透過NuGet引用Select2.js套件

因為MVC使用Bootstrap框架,所以需要搭配select2-bootstrap.css來修正畫面的輸出

新增一個HomeModel用來傳遞資料
public class HomeModel
{
    public IEnumerable<int> DataList { get; set; }
}

在Controler中透過ViewBag,傳入一個MultiSelectList
public ActionResult Index()
{
    this.ViewBag.DataList = new MultiSelectList(
        new List<SelectListItem>()
        {
            new SelectListItem{ Text="aaa", Value="1"},
            new SelectListItem{ Text="bbb", Value="2"},
            new SelectListItem{ Text="ccc", Value="3"},
        }, "Value", "Text");

    return View();
}

在View中透過@Html.ListBoxFor輸出下拉選單
@using WebApplication1.ViewModels
@model HomeModel
@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<div class="form-horizontal">
    <div class="form-group">
        @Html.LabelFor(x => x.DataList, new { @class = "col-md-2 label-control" })
        <div class="col-md-10">
            @Html.ListBoxFor(x => x.DataList, null, new { @class = "form-control" })
        </div>
    </div>
</div>

@section scripts
{
    <script type="text/javascript">
        $("#DataList").select2();
    </script>
}

Select2套件在MVC專案中的樣子

如果是編輯頁面,初始化要有值的話,只要在model上面指定值
public ActionResult Edit(int? id)
{
    this.ViewBag.DataList = new MultiSelectList(
        new List<SelectListItem>()
    {
        new SelectListItem{ Text="aaa", Value="1"},
        new SelectListItem{ Text="bbb", Value="2"},
        new SelectListItem{ Text="ccc", Value="3"},
    }, "Value", "Text");

    HomeModel model = new HomeModel { DataList = new int[] { 1, 3 } };
    return View(model);
}

初始化的時後就有值了


2014年8月22日 星期五

Select2 套件基本用法

Select2 套件是一個加強下拉選單的套件,官網提供了許多詳細的示範
這裡用一個簡單的例子來介紹這個套件
首先開一個Web專案

用一個html頁面來練習

透過NuGet新增select2.js套件

select2.js套件包含了CSS和JavaScript和語系

在html裡面引用CSS和JavaScript,該套件相依於JQuery,最低版本1.7以上

手動輸入一個下拉選單

一般的下拉選單看起來像這個樣子

透過JQuery啟用select2

看起來漂亮多了,而已還有查詢過濾的功能

加入多選屬性

Select2套件的多選功能

參考資料

Select2 官網

Select 2 CDN

Select2 Bootstrap 3 CSS

2014年8月19日 星期二

密碼複雜度

密碼複雜度利用Regular Expression可以簡單地完成
先用一個簡單的例子,6~12個英數字混合的規則
<div>
    <asp:TextBox ID="Tbx_Password" runat="server" />
    <asp:Button ID="Btn_Save" Text="Save" OnClick="Btn_Save_Click" runat="server" />
    <asp:RequiredFieldValidator ID="Rfv_Password" ControlToValidate="Tbx_Password" 
        ErrorMessage="密碼不可空白" SetFocusOnError="true" Text="密碼不可空白" ForeColor="Red" 
        Display="Dynamic" runat="server" />
    <asp:RegularExpressionValidator ID="Reg_Password" ControlToValidate="Tbx_Password" 
        ValidationExpression="^(?!.*[oO0])(?=.*[a-zA-Z])(?=.*[\d])(?!.*[\d][\d][\d]).{6,12}$"
        ErrorMessage="密碼長度6~12英數混合" Text="密碼長度6~12英數混合" SetFocusOnError="true" 
        ForeColor="Red" Display="Dynamic" runat="server" />
</div>

^ 開頭符號
(?=.*[a-zA-Z]) 最少一個英文字
(?=.*[\d]) 最少一個數字
.{6,12} 6~12長度
$ 結尾符號

利用群組的功能還可以組合出更多的規則
(?=.*[!@#$%^&*]) 最少一個符號
(?=.*[\d][\d]) 連續兩個數字
(?=.*[a-z][a-z]) 連續兩個小寫英文
(?!.*[\d][\d][\d]) 禁止連續三個數字
(?!.*[oO0]) 禁止小寫的o和大寫的O和數字的0

如果不是WebForm專案,也可以透過System.Text.RegularExpressions空間下的Regex物件來使用

protected void Btn_Save_Click(object sender, EventArgs e)
{
    Regex reg = new Regex(@"^(?!.*[oO0])(?=.*[a-zA-Z])(?=.*[\d])(?!.*[\d][\d][\d]).{6,12}$");
    if (reg.IsMatch(Tbx_Password.Text))
    {
        Response.Write("Match");
    }
    else
    {
        Response.Write("Not Match");
    }
}

2014年8月6日 星期三

AutoMapper 全域設定

AutoMapper可以把物件的對應設定集中在一個設定檔裡面,然後用Profile來做群組分類
用一個簡單的MVC網站來做例子

首先是HomeModel物件
public class HomeModel
{
    public int ModelId { get; set; }
    public string ModelName { get; set; }
}

再來是HomeViewModel物件
public class HomeViewModel
{
    public int ViewModelId { get; set; }
    public string ViewModelName { get; set; }
}

接下來在App_Start裡面新增一個AutoMapperConfig檔案
public class AutoMapperConfig
{
    public static void Configure()
    {
        Mapper.Initialize(x =>
            {
                x.AddProfile<HomeProfile>();
            });
    }

    private class HomeProfile : Profile
    {
        protected override void Configure()
        {
            Mapper.CreateMap<HomeModel, HomeViewModel>()
                .ForMember(dest => dest.ViewModelId, opt => opt.MapFrom(src => src.ModelId))
                .ForMember(dest => dest.ViewModelName, opt => opt.MapFrom(src => src.ModelName));

            Mapper.CreateMap<HomeViewModel, HomeModel>()
                .ForMember(dest => dest.ModelId, opt => opt.MapFrom(src => src.ViewModelId))
                .ForMember(dest => dest.ModelName, opt => opt.MapFrom(src => src.ViewModelName));
        }
    }
}

然後在Global.asax中呼叫就行了
public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        AutoMapperConfig.Configure();
    }
}

2014年8月5日 星期二

AutoMapper 自訂對應關系

在來源和目的物件中,如果屬性名稱和型態都相同的話,AutoMapper會自動轉換
但通常事情不會這麼單純,很多時後我們會需要指定轉換的方式,用一個簡單的例子來說明

首先是來源的Model物件
public class DemoModel
{
    public int CustId { get; set; }
    public string CustName { get; set; }
    public bool CustGender { get; set; }
}

再來是要轉換的ViewModel物件
public class DemoViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string FullName { get; set; }
    public string Gender { get; set; }
    public DateTime Date { get; set; }
}

轉換的設定方式
Ignore是用來略過對應,不給值
MapFrom是用來指定對應的屬性名稱,或是組合多個屬性值
ResolveUsing是指定自訂的型別轉換方式
UseValue是在轉換的過程中直接給值
NullSubstitute是在原始值為空的時後才給值
Condition傳入一個條件為true才給值
public class Class1
{
    public void GetViewModel()
    {
        Mapper.CreateMap<DemoModel, DemoViewModel>()
            .ForMember(dest => dest.Id, opt => opt.Ignore())
            .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.CustName))
            .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => string.Format("{0}:{1}", src.CustId, src.CustName)))
            .ForMember(dest => dest.Gender, opt => opt.ResolveUsing<GenderResolver>().FromMember(src => src.CustGender))
            .ForMember(dest => dest.Date, opt => opt.UseValue<DateTime>(DateTime.Now));

        DemoModel model = new DemoModel
        {
            CustId = 123,
            CustName = "abc",
            CustGender = true
        };

        DemoViewModel viewModel = Mapper.Map<DemoModel, DemoViewModel>(model);
        Console.WriteLine("id:{0}, name:{1}, fullname:{2}, gender:{3}, date:{4}", viewModel.Id, viewModel.Name, viewModel.FullName, viewModel.Date, viewModel.Gender);
    }

    private class GenderResolver : ValueResolver<bool, string>
    {
        protected override string ResolveCore(bool source)
        {
            return source ? "男" : "女";
        }
    }
}

AutoMapper 簡單用法

AutoMapper是一個用來處理類別之間轉換的套件,以下用個Model和ViewModel的轉換當例子

首先是一個簡單的Model
public class DemoModel
{
    public int Id { get; set; }
    public string Name { get; set; }
}

再來是對應的ViewModel
public class DemoViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
}

兩個類別要轉換的一般寫法
private static void V1()
{
    DemoModel c1 = new DemoModel()
    {
        Id = 123,
        Name = "abc"
    };

    DemoViewModel c2 = new DemoViewModel
    {
        Id = c1.Id,
        Name = c1.Name
    };

    Console.WriteLine("id:{0}, name:{1}", c2.Id, c2.Name);
}

使用AutoMapper之後的寫法
private static void V2()
{
    Mapper.CreateMap<DemoModel, DemoViewModel>();
    DemoModel c1 = new DemoModel
    {
        Id = 123,
        Name = "abc"
    };

    DemoViewModel c2 = Mapper.Map<DemoModel, DemoViewModel>(c1);
    Console.WriteLine("id:{0}, name:{1}", c2.Id, c2.Name);
}

AutoMapper用來把DataReader轉成IEnumerable也很簡單,以北風資料庫的Customers為例子
首先是轉換後的CustViewModel
public class CustViewModel
{
    public int CustomerID { get; set; }
    public string CompanyName { get; set; }
    public string City { get; set; }
    public string Region { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }
    public string Phone { get; set; }
    public string Fax { get; set; }
}

取得資料列表
public static IEnumerable<CustViewModel> GetList()
{
    Mapper.CreateMap<IDataReader, IEnumerable<CustViewModel>>();
    string connString = @"data source=(localdb)\v11.0; initial catalog=Northwind; trusted_connection=true;";
    using (SqlConnection conn = new SqlConnection(connString))
    {
        SqlCommand cmd = new SqlCommand("select * from customers", conn);
        conn.Open();
        SqlDataReader dtr = cmd.ExecuteReader();
        return Mapper.Map<IDataReader, IEnumerable<CustViewModel>>(dtr);
    }
}


AutoMapper 筆記

1. AutoMapper 簡單用法
http://blog.developer.idv.tw/2014/08/automapper_5.html

2. AutoMapper 自訂對應關系
http://blog.developer.idv.tw/2014/08/automapper_60.html

3. AutoMapper 全域設定
http://blog.developer.idv.tw/2014/08/automapper_6.html

參考資源

AutoMapper 官方文件