2010年12月29日 星期三

Windows Server 2008 常常 BSOD (藍色死機)

最近公司的一台伺服器,連續兩天建立 Hyper-V 的 Virtul Machine時,就會發生 Blue Screen Of Dead 的經典畫面。畫面中,出現了

IRQL_NOT_LESS_OR_EQUAL, 及

STOP: 0x0000000A (parameter1parameter2parameter3parameter4)


最後是找到了這個 KB979444,套上了 hotfix 才解決了問題。

2010年12月20日 星期一

WCF RIA Service 部署的問題

原故

今天將寫了幾天的 Silverlight 部署到測試機時,一直發生錯誤。404 resource not found.

使用 Fiddler 來查詢,是在呼叫 RIA Service 時發生錯誤。  http://servername/virtul directory/ClientBin/My-Ria-Service.svc.svc/binary/MyMethod 404。

明明在我的機器可以執行啊!

原故

爬了一下文,知道了原因。RIA Service 實在做的太好了,以致於在開發階段,幾乎不必手動增加必要的參考。而在 http://www.silverlight.net/getstarted/riaservices/ 下載的安裝檔,又直接幫我們註冊到 GAC 中,以致於一些部署需要的 dll 就被遺忘了, 這些設定通常在 Web.config 中會出現。

部署時,主要有兩種做法。一是 bin 另一種是 GAC。

bin: 即 XCopy 做法

在部署ASP.NET 專案中,加以下的參考,並且在Property 中標Copy Local 為 True。

  • System.ServiceModel.dll
  • System.ServiceModel.DomainServices.Hosting.dll
  • System.ServiceModel.DomainServices.Server.dll
  • System.ComponentModel.DataAnnotations.dll

image

GAC

第二種做法,是直接將 Ria Services 註冊到伺服器上。指令如下
msiexec /i RiaServices.msi SERVER=TRUE

可惜,這樣不會將 System.ServiceModel.dll , System.ComponentModel.DataAnnotations.dll 標示為 Copy Local = true。還是要手動將這兩個dll設定一下。

換SSL 憑證後,客戶得到未信任的憑證

原由

這次,由於網站的 SSL 憑證到期了,更新了SSL 憑證。更換後,有一家公司抱怨瀏覽網站時,會出現「未信任的憑證」問題。

原因

只有這一位客戶哦!請客戶幫我們看一下憑證的問題,會出現「信任憑證授權單位無法確認這個憑證」的錯誤訊息。如下圖

image

檢視憑證路徑時,也少了一層根憑證。

客戶的電腦
image

正常的狀況
SNAGHTMLc8c1dc

因此,我判斷 Baltimore CyberTrust Root 這個憑證並未在「 信任的根憑證」中。

Debug 步驟

為什麼別的客戶ok,單獨這位客戶有這樣的議題呢?我找到一某一台相同問題的電腦,特別先看了在「 信任的根憑證」中,也相同地沒有這張憑證。

接著,我瀏覽了該網頁,並以 Fiddler 錄下的 Request ,如下圖。

image

答案揭曉。原來當我們瀏覽一個https網頁時,windows 會幫我們檢查SSL 憑證。而SSL 憑證的 CA 尚未匯入時,且是公開常見的CA時,windows update 會自動匯入 CA 任證。

該客戶由於控管嚴格,連 windows update 的 IP 都使用防火牆封鎖了,造成憑證路徑不正確的問題。

結論

雖然只能檢視 https 網站這樣常見的動作,其後隱含了多少工作原理啊。這一次又學到新東西。

2010年12月14日 星期二

全球千大企業的應用伺服器使用的網頁技術

舊聞了,但今天才知道的訊息。根據Port80 Surveys the Top 1000 Corporations' Application Servers and Scripting Platforms (July 2007),ASP.NET 在2007 年時被採用的比例為 51.5%,遠超過第二名的Java platform 12.7%

image

而在這篇中Port80's 2010 - Top 1000 Corporation Web Servers,54.5%的伺服器是使用 IIS,但其中絕大部份仍使用 IIS 6.0。

image

2010年12月10日 星期五

Corss domain access

Silverlight 預設只能存取同一個網站的服務。例如  http://server/ 下的 silverlight 就不能存取 http://server.domain.com/service.svc ,即使其實這兩者是同一台伺服器。此時,必須在Web伺服器的根目錄下放 clientaccesspolicy.xml,內容如下

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>      
      <allow-from http-request-headers="*">
        <domain uri="*"/>"
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

詳細使用方式見 讓服務可跨網域界限使用

2010年12月9日 星期四

Silverlight TreeView 的 DataBinding (2)

簡介

WPF TreeView 的 DataBinding (2) 相同的問題,仍然發生在 Silverlight 上。那 Silverlight 可以使用與 WPF 相同的解法嗎?不行,原因出在Silverlight 並設計出 DataTemplateSelector 這樣的概念。

但是,相同的問題該怎麼解決呢?

自己做一個 DataTemplateSelector

沒有?就自己打造一個。

using System.Windows;
using System.Windows.Controls;

namespace SilverlightTreeView1
{
    public abstract class DataTemplateSelector : ContentControl
    {
        public virtual DataTemplate SelectTemplate(
                object item, DependencyObject container)
        {
            return null;
        }

        protected override void OnContentChanged(
                object oldContent, object newContent)
        {
            base.OnContentChanged(oldContent, newContent);

            ContentTemplate = SelectTemplate(newContent, this);
        }
    }
    
    public class MyDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate FileTemplate { get; set; }
        public HierarchicalDataTemplate FolderTemplate { get; set; }
        public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
        {
            if (item is MyFolder)
                return FolderTemplate;
            else
                return FileTemplate;
        }
    }
}

xaml

<UserControl x:Class="SilverlightTreeView1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:SilverlightTreeView1="clr-namespace:SilverlightTreeView1">
    <UserControl.Resources>
        <SilverlightTreeView1:FolderHelper x:Key="helper" />
        <sdk:HierarchicalDataTemplate x:Key="folder" ItemsSource="{Binding Items}">
            <StackPanel Orientation="Horizontal">
                <Image Source="/SilverlightTreeView1;component/Images/folder.png" />
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </sdk:HierarchicalDataTemplate>
        <sdk:HierarchicalDataTemplate x:Key="file">
            <StackPanel Orientation="Horizontal">
                <Image Source="/SilverlightTreeView1;component/Images/doc.png" />
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </sdk:HierarchicalDataTemplate>
    </UserControl.Resources>
        <Grid x:Name="LayoutRoot" Background="White">
        <sdk:TreeView Name="treeView1" ItemsSource="{Binding Source={StaticResource helper}, Path=Items}">
            <sdk:TreeView.ItemTemplate>
                <sdk:HierarchicalDataTemplate ItemsSource="{Binding Items}">
                    <SilverlightTreeView1:MyDataTemplateSelector Content="{Binding}"
                        FolderTemplate="{StaticResource folder}" FileTemplate="{StaticResource file}" />
                </sdk:HierarchicalDataTemplate>
            </sdk:TreeView.ItemTemplate>
        </sdk:TreeView>
    </Grid>
</UserControl>

程式運行結果

image

程式碼下載

Generics Variance

一個有趣的問題。以下的程式可以編譯嗎?

using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            List<A> listA = new List<A>();
            List<B> listB = new List<B>();
            listA.AddRange(listB.AsEnumerable());
        }
    }

    internal class A{} 
    class B : A{}
}
答案很好玩,.NET Framwork 4可以,.NET 2.0 不可以。
.NET Framwork 2 的錯誤訊息是

cannot convert from 'System.Collections.Generic.IEnumerable<B>' to 'System.Collections.Generic.IEnumerable<A>'   

這是為什麼呢?B 是 A 的子代,把 B 當成A 加到 listA 這個集合中,看起來沒有什麼不對。

原因是 .NET Framwork 2 的泛型(Generics)尚不支援這樣的場景。這樣的狀況稱 Generics Variance。以前的處理方式請參考Variance in Generic Types

當然,這樣的問題已經在 .NET Framework 4 中得到了解決。

 

WPF TreeView 的 DataBinding (2)

上一回的 WPF TreeView 的 DataBinding (1),TreeView繫結的是單一型別物件的樹狀結構。這一次要挑戰的是同時繫結到兩種型別。

資料

和上次不同的,這一次目錄 (MyFolder)這個容器(container)中,可同時持有MyFolder 與 MyFile。為此,我們修改程式如下

public static List<MyFolder> GetAllFolder()
{
    var folders = new List<MyFolder>()
                      {
                          new MyFolder("A")
                              {
                                                Files = new List<MyFile>() { new MyFile("d_1")},
                                                SubFolders = new List<MyFolder>(){
                                  new MyFolder("A1")
                                      {
                                                        Files = new List<MyFile>() { new MyFile("d1_1"), new MyFile("d1_2")},
                                                        SubFolders = new List<MyFolder>()
                                                                         {
                                                                             new MyFolder("A11"),
                                                                                        new MyFolder("A12"),
                                                                                        new MyFolder("A13"),
                                                                                        new MyFolder("A14"),
                                                                         }
                                      },
                                                new MyFolder("A2"),
                                                new MyFolder("A3")
                                                }
                              },
                                            new MyFolder("B")
                                                {
                                                    Files = new List<MyFile>() { new MyFile("db_1"), new MyFile("db_2")},
                                                    SubFolders = new List<MyFolder>(){
                                  new MyFolder("B1"),
                                                new MyFolder("B2"),
                                                new MyFolder("B3"),
                                                new MyFolder("B4"),
                                                }
                                            }
                      };
    return folders;
}

繫結

.NET Framework 的元件(control)在進行繫結時,只能繫結到單一物件或同一類別的集合。因此,TreeView 要同時顯示 MyFolder 或 MyFile 時,ItemsSource 給什麼值呢?是 SubFolders 嗎?還是 Files 嗎?都只能顯示一部份。為了讓 ItemsSource 在繫結時可以繫結到所有的children,我們必須製作一個新的屬性 Items,可讀取到所有的children,包含MyFolder 及 MyFiles。故程式碼修改如下

public abstract class MyItem
{
    public string Name { get; set; }
}
public class MyFolder : MyItem
{
    public MyFolder(string name)
    {
        Name = name;
    }

    public List<MyFolder> SubFolders { get; set; }
    public List<MyFile> Files { get; set; }

    public List<MyItem> Items
    {
        get
        {
            var items = new List<MyItem>();
            items.AddRange(SubFolders.AsEnumerable());
            items.AddRange(Files.AsEnumerable());
            return items;
        }
    }
}

xaml 修改如下

<Window x:Class="WpfTreeView1.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfTreeView1="clr-namespace:WpfTreeView1" Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <WpfTreeView1:FolderHelper x:Key="helper" />
    </Window.Resources>
    <Grid>
        <TreeView Name="treeView1" ItemsSource="{Binding Source={StaticResource ResourceKey=helper}, Path=Items}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Items}">
                    <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

注意到我們ItemsSource改繫結到 Items。程式運作結如果附圖image

圖示

上面運作的結果是正確的,但風格還是無法讓使用者接受。原因在於無法分辨何者為目錄,何者為檔案。

但怎麼改呢?HierarchicalDataTemplate 只有一個,如何才能分開顯示目錄及檔案呢?

image

答案是實作一個 DataTemplateSelector。

DataTemplateSelector 的想法是這樣:資料繫結的過程中,當遇到某類型的物件時,套用指定的樣版。而這樣的邏輯是完全客製化的,因此必須自己實作。在 WPF 中,就是繼承DataTemplateSelector, 實作自己的 SelectTemplate。程式如下

using System.Windows;
using System.Windows.Controls;

namespace WpfTreeView1
{
    public class MyDataTemplateSelector : DataTemplateSelector
    {
        public HierarchicalDataTemplate FileTemplate { get; set; }
        public HierarchicalDataTemplate FolderTemplate { get; set; }
        public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
        {
            if (item is MyFolder)
                return FolderTemplate;
            else
                return FileTemplate;
        }
    }
}

xaml

<Window x:Class="WpfTreeView1.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfTreeView1="clr-namespace:WpfTreeView1" Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <WpfTreeView1:FolderHelper x:Key="helper" />
        <HierarchicalDataTemplate x:Key="folder" ItemsSource="{Binding Items}">
            <StackPanel Orientation="Horizontal">
                <Image Source="/WpfTreeView1;component/Images/folder.png" />
                <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/>
            </StackPanel>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="file">
            <StackPanel Orientation="Horizontal">
                <Image Source="/WpfTreeView1;component/Images/doc.png" />
                <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/>
            </StackPanel>
        </HierarchicalDataTemplate>
        <WpfTreeView1:MyDataTemplateSelector x:Key="selector" FolderTemplate="{StaticResource ResourceKey=folder}" FileTemplate="{StaticResource ResourceKey=file}" />
    </Window.Resources>
    <Grid>
        <TreeView Name="treeView1" ItemsSource="{Binding Source={StaticResource ResourceKey=helper}, Path=Items}"
                            ItemTemplateSelector="{StaticResource ResourceKey=selector}">
        </TreeView>
    </Grid>
</Window>
程式運行結果如下

image

結論

WPF 已經幫我們想到非常多的問題與解決方式。在解決問題的時候,千萬不要一味的自己發明輪子,最後又難以維護。

程式碼下載

WPF TreeView 的 DataBinding (1)

一樣的問題,在 Silverlight 的版本如何在 WPF 上實作呢?其實作法是一致的。

實作1

xaml

<Window x:Class="WpfTreeView1.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TreeView Name="treeView1">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding SubFolders}">
                    <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>
cs
using System.Windows;

namespace WpfTreeView1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            treeView1.ItemsSource = FolderHelper.GetAllFolder();
        }
    }
}
執行結果如下圖: image

實作2: 改用 Resource 讀取 Folder

在 cs 中,只有一行的程式 treeView1.ItemsSource = FolderHelper.GetAllFolder(); 實在不美觀。這裡使用在介紹的static binding小技巧,將這一行消除掉。

xaml

<Window x:Class="WpfTreeView1.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfTreeView1="clr-namespace:WpfTreeView1" Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <WpfTreeView1:FolderHelper x:Key="helper" />
    </Window.Resources>
    <Grid>
        <TreeView Name="treeView1" ItemsSource="{Binding Source={StaticResource ResourceKey=helper}, Path=AllFolders}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding SubFolders}">
                    <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

cs

using System.Windows;

namespace WpfTreeView1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

當然,程式運作的結果是一致的。

程式碼下載

2010年12月8日 星期三

Silverlight TreeView 的 DataBinding (1)

這一天研究了 Silverlight TreeView 的 DataBinding. 這裡作一下簡單的記錄,以便自己回憶。

首先,使用 Silverlight Application 建立一個  SilverlightApplication1 吧。

資料

由於 TreeView 是樹狀結構的顯示元件,要顯示 TreeView 的 DataBinding,當然需要一個樹狀結構的資料。以下是目錄的結構程式。

using System.Collections.Generic;

namespace SilverlightTreeView1
{
    public class MyFolder
    {
        public MyFolder(string name)
        {
            Name = name;
        }
        public string Name { get; set; }

        public List<MyFolder> SubFolders { get; set; }
    }
    public class FolderHelper
    {
        public static List<MyFolder> GetAllFolder()
        {
            var folders = new List<MyFolder>()
                              {
                                  new MyFolder("A")
                                      {
                                                        SubFolders = new List<MyFolder>(){
                                          new MyFolder("A1")
                                              {
                                                                SubFolders = new List<MyFolder>()
                                                                                 {
                                                                                     new MyFolder("A11"),
                                                                                                new MyFolder("A12"),
                                                                                                new MyFolder("A13"),
                                                                                                new MyFolder("A14"),
                                                                                 }
                                              },
                                                        new MyFolder("A2"),
                                                        new MyFolder("A3")
                                                        }
                                      },
                                                    new MyFolder("B")
                                                        {
                                                            SubFolders = new List<MyFolder>(){
                                          new MyFolder("B1"),
                                                        new MyFolder("B2"),
                                                        new MyFolder("B3"),
                                                        new MyFolder("B4"),
                                                        }
                                                    }
                              };
            return folders;
        }
    }


}

使用 TreeView 元件

在MainPage.xaml 上拖入一個 TreeView 元件。

Xaml

<UserControl x:Class="SilverlightTreeView1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:SilverlightTreeView1="clr-namespace:SilverlightTreeView1">
        <Grid x:Name="LayoutRoot" Background="White">
        <sdk:TreeView Name="treeView1">
        </sdk:TreeView>
    </Grid>
</UserControl>

在 MainPage.xaml 中,建構子程式碼中設定 treeView1.ItemsSource

using System.Windows.Controls;

namespace SilverlightTreeView1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            treeView1.ItemsSource = FolderHelper.GetAllFolder();
        }
    }
}

運行程式後,結果如下圖

image

運作的結果只出現了最上層的兩個目錄,而且並沒有顯示我想要的目錄名稱。

修改1: 顯示目錄名稱

要顯示目錄名稱,其實很簡單,就是要在 xaml 中作一些變化。

<sdk:TreeView Name="treeView1">
    <sdk:TreeView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}" />
        </DataTemplate>
    </sdk:TreeView.ItemTemplate>
</sdk:TreeView>
TreeView.ItemTemplate的內容是內容的樣版,並且使用DataTemplate 作為資料繫結樣版。這裡使用了一個 TextBlock,並且繫結到 MyFolder 物件的 Name 屬性(Property)。
結果如下圖。
image

修改2:改成樹狀顯示

DataTemplate 只能顯示繫結物件的資料,並無法顯示樹狀結構。為了顯示樹狀結構,我們必須將資料顯示的樣版,改用HierarchicalDataTemplate

<sdk:TreeView Name="treeView1">
    <sdk:TreeView.ItemTemplate>
        <sdk:HierarchicalDataTemplate ItemsSource="{Binding SubFolders}">
            <TextBlock Text="{Binding Name}" />
            </sdk:HierarchicalDataTemplate>
    </sdk:TreeView.ItemTemplate>
</sdk:TreeView>

結果如下圖

image

程式碼下載

2010年12月2日 星期四

使用 TFS Power Tool 的 Best Practices Analyzer

微軟出了很多的 Best Practices Analyzer(BPA),幫助我們檢視電腦、資料庫、伺服器的設定。目前介紹的就是 TFS 的 BPA

SNAGHTML1590726

掃描完畢後,看一下報告。Oh! 好像有不少的問題。

SNAGHTML184e2de

再按一下 「Tell me more about this issue and how to resolve it」連結,就會顯示相關的訊息。

Team Foundation Server Power Tools September 2010

直到最近才知道 TFS Power Tool 又在10月皆出新的版本

移除舊版本

如果之前已安裝過舊的版本,記得先移除舊版本。下圖是舊的版本。

image

安裝

安裝仍和之前版本相同。記得安裝前先將 Visual Studio 2010 關閉。安裝時使用 Typical 的選項即可。

也不知道為什麼,這個工具安裝時總是會花去5分鐘以上。明明該安裝檔 tfpt.msi 才 12.8 MB 啊!

使用

與之前的功能相同的就不說了。新功能可見這裡的說明。之後有機會再介紹吧!

2010年11月23日 星期二

Visual Studio 2010 做壓力測試 (load test),出現錯誤訊息

使用 Visual Studio 2010 做壓力測試 (load test)好一陣子。有一天做完後,,突然發生了錯誤訊息

Error occured running test. The loadTest completed but an error occurred while collecting the final results. Could not access the load test results repository: Incorrect syntax near 's'.  The label 'http' has already been declared. Label names must be unique within a uqery batch or stored procdure

檢視報表切到 Detail時顯示了下面的訊息

No detail exists at the selected point

使用 「Open and manage results」也找不到剛剛執行的測試結果。

原本想說將 load test 所使用的資料庫清空,重新執行loadtestresultsrepository.sql 就可以解決了。這招也不成功。

看來這是 Visual Studio 的 bug 了。

接著,我想了一下,之前為什麼可以?我修改了什麼後變成這樣了。於是我找到了原因。

我的 load test 中其中一個Web Performance Test 有問題。將該 Web Performance Test 移除,或者在 Test Matrix 中設該test 的比例為0,就可以正常儲存測試結果到資料庫。

為什麼這個 Web Performance Test 會造成 load test 的問題呢?這看起來是 Visual Studio 的 bug

2010年11月7日 星期日

TPL 學習日記(9): 使用TPL 應避免的事情1: Avoid Memory Allocation

有些事情,同樣在使用 TPL 時也需要避免。第一件事情就是使用記憶體的 allocation。

原因

記憶體的 allocation 會導致 GC,記憶體的回收變成的一項負擔。

解決方法1: 使用 stack,避免GC。

範例程式如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace TPL_2 {
  class Program {
    static void Main(string[] args) {
      Console.WriteLine("Sequence");
      Console.WriteLine(Time(GoSeq));
      Console.WriteLine("Parallel, using heap");
      Console.WriteLine(Time(GoPalHeap));
      Console.WriteLine("Parallel, using stack");
      Console.WriteLine(Time(GoPalStack));
    }

    private static void GoSeq() {
      var q = (from i in Enumerable.Range(1, 20000000)
               select new { Value = i }).Max(i => i.Value);
      Console.WriteLine(q);
    }

    //anonymous type, 會使用 heap。導致GC 回收記憶體,效能較差
    private static void GoPalHeap() {
      var q = (from i in ParallelEnumerable.Range(1, 20000000)
               select new { Value = i }).Max(i => i.Value);
      Console.WriteLine(q);
    }

    //struct, 會使用 stack,效能較快
    private static void GoPalStack() {
      var q = (from i in ParallelEnumerable.Range(1, 20000000)
               select new MyClass { Value = i }).Max(i => i.Value);
      Console.WriteLine(q);
    }

    struct MyClass {
      public int Value;
    }

    static TimeSpan Time(Action a) {
      Stopwatch w = Stopwatch.StartNew();
      a();
      return w.Elapsed;
    }
  }
}
執行結果

image

想不到吧!使用 Heap 反而使得 TPL 執行效果比不使用更差。改使用 struct 後就變快了。

解決方法2: 使用 Server GC

第二個方法是啟用 Server GC (伺服器記憶體回收),見<gcServer> 項目。簡單的說,使用這個方法,會讓CPU 的每個 Core 有獨立的 GC,因此回收的效能變快了。

.net framework 預設使用 workstation (工作站)的方式回收記憶體,也就是只有一個 GC。使用預設的 workstation 方式時, GC 必須考慮 multi-core 的 concurrency 問題,因此回收的效能較差。

我們加上一個 app.config ,如下

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <gcServer enabled="true" />
  </runtime>
</configuration>

執行結果

image

由結果得知,加上設 gcServer 的設定,雖然仍然使用了 heap,速度還是變快了,但無法與使用 stack相比。

TPL 學習日記(8): BlockingCollection 的 GetConsumingEnumerable

上一回提到了 BlockingCollection,順便提一個值得玩玩的事。

BlockingCollection 實作了IProducerConsumerCollection<T>. 此 interface 定義了製造者/消費者使用方式的安全執行緒集合。而BlockingCollection 是其中最典型的實作。BlockingCollection 可以使用GetConsumingEnumerable 取得加入/移除到集合中的項目。如下範例。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Threading;

namespace TPL_1 {
  class Program {
    static void Main(string[] args) {
      var primes = new BlockingCollection<int>();
      var t = Task.Factory.StartNew(
        () =>
        {
          //取得對BlockingCollection中新增或移除的每個項目
          foreach (var item in primes.GetConsumingEnumerable())
            Console.WriteLine(item);
        });
      for (int i = 0; i < 5000000; i++)
        if (IsPrime(i)) primes.Add(i);
      primes.CompleteAdding(); //結束新增
      t.Wait(); //直到 t 執行完
    }

   
    /// <summary>
    /// 是否為質數
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    static bool IsPrime(int input) {
      int bound = (int)Math.Sqrt(input);
      for (int i = 2; i <= bound; i++)
      {
        if (input % i == 0) return false;
      }
      return true;
    }

    static TimeSpan Time(Action a) {
      Stopwatch w = Stopwatch.StartNew();
      a();
      return w.Elapsed;
    }
  }
}

執行起來,功能平凡無奇。好玩的是,這樣的技巧已經在之前的「讀取大型文字檔」中顯示過了,而GetConsumingEnumerable 以相同的方式實作出來。

CompleteAdding() 方法標示該集合不會再新增項目,因此 GetConsumingenumerable 的巡覽才會結束。

2010年11月5日 星期五

TPL 學習日記(7): Thread-Safe Collections

Parallel library 是一個多執行緒的一套 API,因此在使用時,也需要遵守多執行緒程式的規則。

以下,是一個「找出3 到 5,00,000 之間有多少個質數」的解法。

單一執行緒

首先,我們當然要寫出單一執行緒的作法,再寫成多執行緒的作法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace TPL_1 {
  class Program {
    static void Main(string[] args) {
      //單一執行緒
      Console.WriteLine(Time(
    () =>
    {
      List<int> primes = new List<int>();
      for (int i = 0; i < 2000000; i++)
      {
        if (IsPrime(i)) primes.Add(i);
      }
      Console.WriteLine("Count = " + primes.Count);
    }
  ));
    }
    /// <summary>
    /// 是否為質數
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    static bool IsPrime(int input) {
      int bound = (int)Math.Sqrt(input);
      for (int i = 2; i <= bound; i++)
      {
        if (input % i == 0) return false;
      }
      return true;
    }

    static TimeSpan Time(Action a) {
      Stopwatch w = Stopwatch.StartNew();
      a();
      return w.Elapsed;
    }
  }
}

程式碼很簡單,其實就是將所有的質數找出並加進到一個使用 List<int> 的 instant 中(即 primes),並在最後算出總共有348515個質數。

使用 Parallel library (Wrong)

這次要使用 Parallel library。由之前的經驗,我們會寫成下面的程式碼。

static void Main(string[] args) {
      //單一執行緒
      Console.WriteLine(Time(
    () =>
    {
      List<int> primes = new List<int>();
      for (int i = 0; i < 5000000; i++) {
        if (IsPrime(i)) primes.Add(i);
      }
      Console.WriteLine("Count = " + primes.Count);
    }
  ));
      Console.WriteLine(Time(
          () =>
          {
            var primes = new List<int>();
            Parallel.For(0, 5000000, i =>
            {
              lock(primes) //Watch this line
                if (IsPrime(i)) primes.Add(i);
            });
            Console.WriteLine("Count = " + primes.Count);
          }
        ));
    }

注意到 //Watch this line 的那一行。由於在寫 multi thread 程式, concurrent 非常重要,我加了 lock(primes) 讓存取 primes 可避免掉一致性的問題。結果呢,Multithread 竟然比 Single thread 還慢!真不可思議。

image

使用 Parallel library (Correct)

不要自己發明輪子。上面例子的問題,是自行使用 lock 來解決 concurrency 的問題。如果是在 TPL 的世界中,該問題就應使用 System.Collection.Concurrent namespace 內的集合。以下範例加上新的解法 (BlockingCollection)

static void Main(string[] args) {
      //單一執行緒
      Console.WriteLine(Time(
    () =>
    {
      List<int> primes = new List<int>();
      for (int i = 0; i < 5000000; i++) {
        if (IsPrime(i)) primes.Add(i);
      }
      Console.WriteLine("Count = " + primes.Count);
    }
  ));
      Console.WriteLine(Time(
          () =>
          {
            var primes = new List<int>();
            Parallel.For(0, 5000000, i =>
            {
              lock(primes)
                if (IsPrime(i)) primes.Add(i);
            });
            Console.WriteLine("Count = " + primes.Count);
          }
        ));
      Console.WriteLine(Time(
          () =>
          {
            var primes = new BlockingCollection<int>();
            Parallel.For(0, 5000000, i =>
            {
              if (IsPrime(i)) primes.Add(i);
            });
            Console.WriteLine("Count = " + primes.Count);
          }
        ));
    }

程式執行結果如下

image

結論

TPL 的程式已經很棒了,但有些細節的部份仍然要相當小心,否則甚至比不用還差。

2010年10月19日 星期二

單元測試(7): 使用 Moles 增加可測試性

在多次的文章中討論到可測試性。為了增加 method 的可測試性,往往需要更改 method 的參數,讓不具可測試性的 method,抽離出可測試的部份。

問題

靜態函式往往是最不具測試性的原兇。例如 DateTime.Now, File.Exists 等。為了這些障礙,我們必須一次又一次的拆解我們原本良好的 method 嗎?

舉例

public class MonthHelper
{
    /// <summary>
    /// 今天是否為單數月
    /// </summary>
    /// <returns></returns>
    public bool IsOddNumberMonth()
    {
        return DateTime.Today.Month%2 == 1;
    }
}

DateTime.Today 會隨著電腦時間而變,IsOddNumberMonth 也會因執行時間而回傳不同的結果,因此該 method 不具可測試性。為了可測試性,之前教大家要將此程式拆成兩部份。如下

public class MonthHelper
{
    /// <summary>
    /// 今天是否為單數月
    /// </summary>
    /// <returns></returns>
    public bool IsOddNumberMonth()
    {
        return IsOddNumberMonth(DateTime.Today);
    }

    /// <summary>
    /// 是否為單數月
    /// </summary>
    /// <returns></returns>
    public bool IsOddNumberMonth(DateTime dateTime)
    {
        return dateTime.Month % 2 == 1;
    }
}

這樣,我們就可以對 IsOddNumberMonth(DateTime) 進行測試。而原本的IsOddNumberMonth() 就只好放棄了。

問題在於,如此簡單的 method,卻必須為了可測試性拆成兩個 method,不太合理。萬一有個 method 裡有一堆不可測試的 method,如 DateTime.Now, File.Exists, File.ReadAllLines 等,我們需要拆解多少次才具可測試性?這樣的程式具可讀性嗎?

Moles

Moq 是非常棒的 Isolation framework,但只能對 interface 或 virtual method 進行 mock,對於靜態函式仍莫可奈何。

對此問題,微軟的 Microsoft Research 提出Moles 這一個 Isolation framework。其原理在於對整個 .NET Framework 的 mock,而非單一類別/方法的 mock。

以上例來說,我們可以進行下來步驟進行單元測試撰寫。(記得先下載及安裝 Moles)

1. 建立測試專案

在該 method 上按右鍵,執行「Create Unit Tests」

image

2. 加入 Moles 項目

在剛剛建立的測試專案上增加一個項目,選到 「Moles and Stubs for Testing」。此時項目的名稱必須與要模擬的 assembly 名稱一致。由於 DateTime 這個 type 的assembly 是 mscorlib 中,(見 DateTime 結構),所以名稱必須取作 mscorlib.moles。按 Add鍵到下一步驟。

SNAGHTML30c6ff

3. 建立測試

在測試程式的最上頭,增加 [assembly: MoledType(typeof(System.DateTime))]。這一行說明 Moles 的 runtime 要對 System.DateTime 進行模擬。
image

修改測試如下。[HostType(“Moles”)] 告訴測試環境是跑在 Moles 的環境下。而 MDateTime 則是對 DateTime 的模擬。Moles 對原來的 dotnet framework 的模擬,都使用固定的命名方式。MDateTime 就是對 DateTime 的 mock type. 屬性的 mock也區分 Get 與 Set。MDateTime.TodayGet 是對 DateTime.Today getter 的 mock。

image

4. 執行測試

以下是完整的測試內容。當然,測試結果都是成功的

using System.Moles;
using ConsoleApplication1;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Microsoft.Moles.Framework;
[assembly: MoledType(typeof(System.DateTime))]

namespace TestProject1
{
    [TestClass()]
    public class MonthHelperTest
    {
        [TestMethod()]
        [HostType("Moles")]
        public void IsOddNumberMonth_Even_ReturnFalse()
        {
            MDateTime.TodayGet = () => new DateTime(2010, 12, 31);
            var target = new MonthHelper();
            bool actual = target.IsOddNumberMonth();
            Assert.AreEqual(false, actual);
        }

        [TestMethod()]
        [HostType("Moles")]
        public void IsOddNumberMonth_Odd_ReturnTrue()
        {
            MDateTime.TodayGet = () => new DateTime(2010, 11, 30);
            var target = new MonthHelper();
            bool actual = target.IsOddNumberMonth();
            Assert.AreEqual(true, actual);
        }
    }
}

結論

怎樣,相當棒吧。Moq 是我最常用的 isolation framework,執行起來會快很多。而當需要模擬 dotnet framework 的靜態方法時,我就會用 Moles。

2010年10月14日 星期四

寄測試信到本機資料夾

緣由

開發程式時,我們都會建立Builde Server,每日建置並測試我們的程式是否正確。而寄信的功能是常見的需求,而每日測試時發信的問題就來了。

  1. Mail Server 難道一定要存在嗎?
  2. Mail Server 未能連線時,測試寄信的功能就會失敗。測試失敗看來就像是程式的問題。
  3. 這當然不是程式的問題,因為程式一直都沒動啊!
  4. 結論:測試無效==>測試無用。

這是老話題了。因為這已經不是單元測試了,而是整合測試。

那,我該如何進行寄測試信的「單元測試」呢?

解法

最簡單的測試方法,是修改 config,讓寄信的功能,改寄到本機資料夾,就不會亂發信。測試信真的寄給了客戶,客戶還會以為是真的。測試信寄太多,也會被當成垃圾信的跳版而被封鎖呢。

修改 config 如下。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.net>
        <mailSettings>
            <smtp from="mymail&lt;testmail@domain.com.tw&gt;" deliveryMethod="SpecifiedPickupDirectory">
                <specifiedPickupDirectory pickupDirectoryLocation="c:\temp\mails" />
            </smtp>
        </mailSettings>
    </system.net>
</configuration>

記得要先建立 pickupDirectoryLocation 所指定的目錄,否則還是會發生例外。

PS: 這樣還是會測到「檔案系統」,嚴格來說並非完全是道地的單元測試。不過至少,不寄測試信的目的達到了。

參考

2010年10月12日 星期二

TFS 2010 自動部署 Web

今天一直在測試自動部署 Web Application 到 Windows Server 2008 的功能。

先部署最簡單的 case: 直接部署到 Build server 並進行測試。

然而,之前的方法不知為何一直行不通?難道是 Windows2008 的原故嗎?

找了好久,終於找到了原因。原來不知道我新的 Build Server (for Windows Server 2008)哪個步驟省略掉了,導致 %Program Files%\MSBuild\Microsoft\VisualStudio\v10.0\ 下少了 Web 這個目錄。從別台伺服器 copy 過來,就又可以自動部署了。

image

PS: 之前也 copy 過 WebApplications 這個目錄,才能 Daily Build。真的忘記哪個步驟被忽略了。

2010年10月7日 星期四

No Code Coverage Results

問題

在 TFS 伺服器上,老是出現「No Code Coverage Results」的訊息。我就是要知道程式碼涵蓋率啊!

1 test run(s) completed - 100% average pass rate (100% total pass rate)
No Code Coverage Results

設定

目前方案的測試設定,是使用哪一個設定呢?我都是使用 Local (local.testsettings)這一個。

image

在Solution Items中,可以找到這一個項目。雙擊該項目,打開如下圖的設定。記得將 Code Coverage 勾選。

SNAGHTML44ee3d

按「Configure」,或雙擊「Code Coverage」,必須再設定對哪些  assembly 進行code coverage 的分析。

SNAGHTML47521e

這樣的設定下就成功了。先在本機跑一下,確定可以跑出 code coverage後,再 check in 到 TFS 上吧。

SNAGHTML4aa744

原因

在vs2010中,又多了一個TraceAndTestImpact.testsettings的設定。開發時,如果要了目前程式的修改衝擊到哪些測試,就必須使用這個一測試設定。而一旦設定了這個測試設定,很容易就忘記了,就這樣 check in 到 TFS中。這就是原因所在。?

正在設定windows更新 0% 完成 請勿關閉電腦

過程

同事今早抱著 NoteBook 來問我,已經一個星期了,開機和關機都離譜的慢。開機時,會出現「正在設定windows更新 0% 完成 請勿關閉電腦」,關機時,會顯示「正在安裝更新1/2」的訊息。但是,每次開關機都會出現。

天啊,對於只寫程式的我,這是一大難題。查了 Google,有一堆說要系統還原,有一些說要手動安裝更新。但,這都要花不少時間。

我直接採用的最簡單的一種方法,出奇地非常有效,一次就解決了。

方法

開機後,等到Windows 更新的訊息結束,且在敲入帳號密碼前,此時畫面右下方會有一個紅色的按鍵。選擇「關機」,然後系統就會關機了。

再開啟電源,惱人的更新訊息就消失了。

討論

這個真的很怪,正常的解決方法不應該是這樣的。如果您被這個問題困擾很久,不如使用這個方法試試看。

2010年10月5日 星期二

ASP.NET 錯誤:找到模稜兩可的符合項目

同事問我這個問題怎麼解?

剖析器錯誤
描述: 當剖析服務此要求所需的資源時發生錯誤。請檢閱下列的特定剖析錯誤詳細資訊,並且適當地修改您的原始程式檔。

剖析器錯誤訊息: 找到模稜兩可的符合項目。

SNAGHTML1c362fe

過程

這個問題花去我不少的時間。第一時間還以為是多餘的 dll 殘存在 bin 目錄下。找了很久沒有找到。

並且,這是 WebSite 的模型,經由 publish 得到的 dll,並不會有上述殘存的 dll。

終於,使用 .net reflector 找到了問題所在。

原因

這原本是個 VS2003 開發的程式,而 VS2003 只有一種 Web application 的模型。經由不知名的人改成 Web site 模型,又不小心將 aspx 中,原來 ID=”_hid002oid” 的 control,改名為 ID=”_hid002OID”,雖然只是大小寫的差別,但在 C# 就足以形成兩個 member field了。

image

解法

將aspx 與 codebehind  的 control 名稱統一即可。

2010年9月29日 星期三

讓 Visual Studio 跑 3GB

緣由

在許多狀況,Visual Studio 都會跑的很辛苦,其中一個原因是 Visual Studio 只有 32位元的版本,而沒有 64 位元的版本。至於為什麼微軟不出 64 位元版本,原因很多,詳情見Visual Studio: Why is there no 64 bit version? (yet)

然而,Windows 只能讓32位元的應用程式存取到 2GB的記憶體。對於剛推出32位元電腦的時代(15年前了吧),有4MB記憶體的電腦就已經很高檔了。 對於現在的我來說,我的電腦已有8GB,作業系統也是Windows 7 64位元版,Visual Studio 只能跑2GB 的限制簡直是孫悟空的緊箍咒,真是天大的限制。尤其是跑  CAT.NET 時,需要大量的記憶體,2GB 一下子會出現 out of memory,真是不知道該怎麼辦。

[解法一]

找到了這一篇 Hacking Visual Studio to Use More Than 2Gigabytes of Memory。讚!

由於我是 64位元的系統,對於系統的 3GB修改就免了。直接使用 Visual Studio Command Prompt,執行下面的指令

cd "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE"editbin /LARGEADDRESSAWARE devenv.exe

執行

以下執行 CAT.NET 時,需要2.05 GB 的記憶體。在未操作前,跑到 1.64 GB 就不行了。

image

[解法二]

OutOfMemoryException Fix 這裡則給了另一個解法。由於這是 ReSharper 的公司 jetbrains 給的 solution,但在 windows server 2008R2 + Visual studio 2005 上跑似乎並非預期可以解決該問題。

[20101001] 在 Windows Server 2003 + Visual studio 2005,該方法卻似乎有用,但在最後,還是讓 Visual Studio 2005 掛掉。

image

Windows Server AppFabric: ASP.NET 4 應用程式共用 Session

常常被問到,ASP.NET 的應用程式是否可以共用 Session?我之前的答案一律是「不行」,但真的不行嗎?其實是可以的,只是很難實作。現在,應用 Windows Server AppFabric 的 Cache 功能,就可以達到這樣的境界。

環境

使用上次介紹的建立 AppFabric Cache Cluster,就可以建立起 Windows Server AppFabric 的環境。

測試網頁

首先,建立一個設定/讀取 Session 的網頁。網頁內容如下。

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="WebApplication3._Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <h2>
        Welcome to ASP.NET! SessionId = <%= Session.SessionID%>
    </h2>
    Set value:
    <asp:TextBox runat="server" ID="txtValue" />
    <asp:Button Text="Set Value" runat="server" ID="btnSet" 
    onclick="btnSet_Click" />
    <br />
    Read value:
    <asp:Label Text="text" runat="server" ID="lblValue" />
    <asp:Button Text="Read Value" runat="server" ID="btnRead" 
        onclick="btnRead_Click" />
    <br />
</asp:Content>

而該頁的 CodeBehind 程式如下

using System;

namespace WebApplication3
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            
        }

        protected void btnSet_Click(object sender, EventArgs e)
        {
            Session["value"] = txtValue.Text;
        }

        protected void btnRead_Click(object sender, EventArgs e)
        {
            lblValue.Text = (string)Session["value"];
        }
    }
}

很簡單的程式。按「Set Value」鍵時,將值寫到 Session中,而按「Read Value」鍵時,將值顯示到網頁上。

在 IIS 上,該Application 的名稱為 WebApplication3。以建立 Application 的方式,建立出使用相同路徑但不同應用程式名稱的 WebApplication4。如下圖

image

未使用 AppFabric 的 Cache 功能時

先打開 WebApplication3 的應用程式 Default.aspx 頁。輸入值為 aaa,再按 Read Value 鍵後,網頁會顯示剛剛輸入的值 aaa。

image

再打開 WebApplication4的網頁 Default.aspx 頁,按Read Value 鍵,網頁顯示值為空白。

image

請注意到,即使用相同的Application,不同的 SessionId代表不同的 session,當然會讀到不同的值。雖然是不同的 Application,但有可能有相同SessionId。為了讓測試有意義,此網頁故意將 SessionId 顯示出來。

使用 AppFabric 的 Cache 作為 ASP.NET 的 Session

將 Web.config 修改如下

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="dataCacheClient"
                 type="Microsoft.ApplicationServer.Caching.DataCacheClientSection,
                        Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, 
                        Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                 allowLocation="true"
                 allowDefinition="Everywhere"/>
    </configSections>
    <dataCacheClient>
        <hosts>
            <host name="AppFabric1" cachePort="22233"/>
        </hosts>
    </dataCacheClient>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
        <sessionState mode="Custom" customProvider="AppFabricCacheSessionStoreProvider">
            <providers>
                <add
                    name="AppFabricCacheSessionStoreProvider"
                    type="Microsoft.ApplicationServer.Caching.DataCacheSessionStoreProvider"
                    cacheName="session" sharedId="MyApp"/>
            </providers>
        </sessionState>
    </system.web>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
    </system.webServer>
</configuration>

此後,如上一測試網頁動作再執行一次。發現可以共用 Session 了。

image

image

注意一下,sharedId="MyApp"/ 兩個應用程式都使用相同的 sharedId。如果不同,就是不同的 session 了.

結論

使用 Windows Server AppFabric 的 Cache 作為 ASP.NET 的 Session,除了享有高可用性(High Avaliability)外,也意外地擁有跟應用程式共用 Session 的效果。

但,這樣好處的前提是:需要同一個Session。如果使用打開兩次 Internet Explorer,甚至不同的 browser,就很容易看不到同一個 session 了。

範例程式下載

2010年9月28日 星期二

建立 AppFabric Cache Cluster

當應用程式使用了Windows Server AppFabric 的快取(Cache)後,為了服務不被中斷,我們通常會被要求高可用性(High availity)的實作。這一次的分享,我就來簡單地實作一下 AppFabric Cache Cluster並且應用在 ASP.NET 的 Session上。

環境

這一次,我們需要四台機器。一台 SQL Server 2008(電腦名稱為SQL2008SP1), 兩台 Windows Server 2008 R2作為 AppFabric,一台Windows 7安裝VS2010作為開發機器。

用途 電腦名稱
資料庫伺服器 SQL2008STDSP1
AppFabric AppFabric1
AppFabric AppFabric2
Windows 7 WinDev

此環境需要 AD domain,因此全部的機器都需要加入domain。電腦名稱可以自行更名,上表的命名為我的習慣。

AppFabric 的安裝

第一台機器命名為 AppFabric1。步驟大致同之前介紹的安裝過程。不過,重要的是需要指定資料庫伺服器到SQL2008SP1,資料庫名稱AppFacbricCaching。由於這是第一台AppFabric,需要指定為 New Cluster(預設)。

image

image

而第二個 AppFabric2 的安裝是一模一樣的,但必須選 “Join Cluster”

image

建立Cache

這裡需要以管理員的權限來執行 Caching Administration Windows PowerShell

New-Cache –CacheName session

設定 Web.Config

在 ASP.NET 上,我們需要設定 web.config。

第一段需要註冊 config section

<configSections>
<section name="dataCacheClient"
         type="Microsoft.ApplicationServer.Caching.DataCacheClientSection,
                Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, 
                Culture=neutral, PublicKeyToken=31bf3856ad364e35"
         allowLocation="true"
             allowDefinition="Everywhere"/>
</configSections>
第二段增加 dataCacheClient 的設定
<dataCacheClient>
    <hosts>
        <host
             name="AppFabric1"
             cachePort="22233"/>
    </hosts>
</dataCacheClient>

第三段則將 sessionstate 改用自訂的 session provider

<sessionState mode="Custom" customProvider="AppFabricCacheSessionStoreProvider">
    <providers>
        <add
            name="AppFabricCacheSessionStoreProvider"
            type="Microsoft.ApplicationServer.Caching.DataCacheSessionStoreProvider"
            cacheName="session" sharedId="MyApp"/>
    </providers>
</sessionState>
注意到 cacheName 使用在上一個指令新增的 Cache “session”

Application Pool 的 Identity

最後,在 IIS 上必須讓該ASP.NET 應用程式跑在一個帳號上,而該帳號必須是資料庫AppFacbricCaching的 owner。

SNAGHTML3725fb

執行

我使用的是 Hyper-V ,很容易地可以暫停/繼續電腦。試著將其中一台電腦暫停,並不會影響 ASP.NET 的運作。

image

2010年9月21日 星期二

Spam Email

最近垃圾信愈來愈多,當然一看就刪除。其中有一封假裝是我寄出去的信被退件,內容如下。

SNAGHTML19b2b47

還蠻像的。一看zip 的內容,果然內含 .exe 檔,想騙我執行它,門都沒有。

image

不過說實話,不熟電腦的人,會不會去執行它呢?100個人中有1個去執行它的話,這種技倆就相當有效了。

IE 9 Beta 上無法使用 Project Server 2003 來回報工時

IE 9 Beta 的效能的確相當快。但畢竟是 Beta 版,難免有許多的 bug,造就一些奇特現象。

我們公司是使用 Project Sever 2003 來管理專案進度。回報專案工時是專案成員的每日工作之一。無奈,一旦安裝了 IE 9 Beta,一進到「任務」頁,網頁就被迫回到首頁,相當莫名其妙。

解法呢?正確的解法當然是等 IE 9 修正這個 bug 了。而目前我得到的暫時解法,是點選按「文件」頁,再點選「任務」頁,就ok 了。

image

真是怪。

This problem will occur when running in 64 bit mode with the 32 bit Oracle client components installed

緣由

開發 Oracle 專案時,發生了下面的錯誤訊息

Attempt to load Oracle client libraries threw BadImageFormatException.  This problem will occur when running in 64 bit mode with the 32 bit Oracle client components installed. Inner exception message: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)

環境

以下是我的開發環境

解決方法

    由於 ODAC 目前只有32 bits的程式,而VS2010方案(solution)指定Build Target 是 AnyCPU,在 IIS 7 上就會執行 64 bits。在 64 bits 的 Process 上載入 32 bits 的 Oracle client library 就會發生這樣的錯誤訊息。

   解決方式也很簡單,就是在 IIS 7 上指定 Application Pool 可以跑 32 bits 的程式。

  1. 找到執行該程式的 Application Pool. 我的例子是使用預設的 ASP.NET v4.0。
  2. 在右方的 Action Panel 上,點擊「Advanced Settings」
  3. 將 Enable 32-Bit Applications 設為 True

   image

結論

    此方法只適用在開發機器上,目的是為了使用 Oracle Developer Tools For Visual Studio。在正式機上,就應該安裝 64 bits 的 Oracle Client library.

2010年9月16日 星期四

IE 9 的效能

各家廠牌最喜歡比效能了。我也來試一試。

圖形

這一點真的沒話說,IE 9 完全勝出。由微軟提供的測試頁Speed Read,分別以 IE 9 beta 與 Firefox 4 beta 測試,IE 9 得到的 FPS 為30左右,而 Firefox 4 beta 則只有 5。天差地遠。

Javascript

我使用 Sun spider 的 benchmark 來測試。IE 9 跑完所有 javascript 需要558.2 ms,而 Firefox 4 需求 697.4ms。嗯,IE 9 再勝出。

image

CSS

http://www.howtocreate.co.uk/csstest.html 來測兩個牌子在 CSS 讀取 div 的效能。IE 9 約 16ms,而 Firefox 為10ms。在 CSS selector 的效能上 Firefox 勝出。

使用http://tools.css3.info/selectors-test/test.html 來測時,則兩者時間差不多,也都通過了測試。

ACID3

ACID3 是用來評量瀏覽器是否符合標準的指標。http://acid3.acidtests.org/ 來測試,IE 9 Beta 得到 95 分,而 FireFox 4 Beta 得到 97 分。差異雖不大,但 FireFox 勝出。

結論

雖然兩者互有勝負,但在圖形處理上,IE 9 實在勝太多了。目前來說,IE 9 整體表現較佳。

Share with Facebook