NHibernate Step by Step(二) 單表操作


NHibernate Step by Step(二) 單表操作

NHibernate Step by Step(二) 單表操作

接着第一期,我們繼續。

爲了方便學習測試,從今天開始我將使用MS Test來進行測試,這樣就避免了在一個Console工程裏不停地添加、註釋代碼了。

 

提示:爲了在VS2005IDE中獲得NHibernate配置文件的代碼提示,請將你的$NHibernate/src/NHibernate下的nhibernate-configuration-2.0.xsd、nhibernate-mapping-2.0.xsd拷貝到/Program Files/Microsoft Visual Studio 8/Xml/Schemas下,這樣當你編輯配置文件或者映射文件時,你將得到完整的代碼提示。
VS2003請拷貝到/Program Files/Microsoft Visual Studio .NET 2003/Common7/Packages/schemas/xml下。
 

NHibernat內部使用log4net來進行日誌操作,今天我們將在配置文件中添加log4net的配置,這樣我們在測試的時候將可以清楚地看到NHibernate是如何進行工作的。

應用配置文件修改如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System,Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>

  <nhibernate>
    <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
    <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
    <add key="hibernate.connection.connection_string" value="Server=localhost;Initial Catalog=NHibernate;Integrated Security=SSPI" />
    <add key="hibernate.connection.isolation" value="ReadCommitted"/>
    <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
    <add key="show_sql" value="true" />
  </nhibernate>

  <log4net>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="ConsoleAppender" />
    </root>

  </log4net>

</configuration>


請注意添加:

<add key="show_sql" value="true" />

 

 

關於log4net的使用,我們這裏不做詳細的講解,有興趣的請參考如下地址:

http://logging.apache.org/log4net/

 

接着,我們在上次的工程組中添加一個名爲Test1的測試項目,將其中的不需要的手動測試去掉。請注意:除了NHibernate/Model引用外,還需要添加如下3個引用:

log4net,System.Data,System.Xml.


修改代碼如下:

using System;
using System.Text;
using System.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NHibernate;
using NHibernate.Cfg;
using log4net;
using log4net.Config;
using Test.Model;

namespace Test1
{
    /**//// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class UnitTest1
    {
        static ISessionFactory factory;
        static ILog logger;
        ISession session;

        public UnitTest1()
        {
        }

        Additional test attributes#region Additional test attributes
       
         [ClassInitialize()]
         public static void MyClassInitialize(TestContext testContext)
         {
             XmlConfigurator.Configure();
             logger = LogManager.GetLogger(typeof(Test1.UnitTest1));
             Configuration config = new Configuration().AddAssembly("Test.Model");
             factory = config.BuildSessionFactory();
         }
       
         [ClassCleanup()]
         public static void MyClassCleanup() { }
       
         [TestInitialize()]
         public void MyTestInitialize()
         {
             session = factory.OpenSession();
         }
       
         [TestCleanup()]
         public void MyTestCleanup()
         {
             session.Close();
         }
       
        #endregion
    }
}

 

我們在測試的開始對Configuration/SessionFactory/Log進行初始化。在每一個Test的開始獲取一個新的session,每一個Test結束後即關閉session。


添加如下一個Get測試:
[TestMethod]
public void TestRead()
{
      Person person = (Person)session.Get(typeof(Person), 1);
      Assert.IsTrue(person.Name == "Jackie Chan");
}


我們在前面曾經插入一條名爲“Jackie Chan”的記錄,現在在Test Manager中選中TestRead,運行,ok,Passed!
我們使用了session.Get來獲取記錄,方法如下:

object Get(Type clazz,object id);

很簡單,一目瞭然。


我們切換到Test Results窗口,雙擊測試成功的TestRead方法,這時將會有一個詳細的測試結果顯示出來,NHibernate將使用我們指定的log4net來輸出詳細信息,我們仔細觀察:


2006-04-15 13:52:13,000 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - opened session

2006-04-15 13:52:13,015 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - loading [Person#1]

2006-04-15 13:52:13,015 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - attempting to resolve [Person#1]

2006-04-15 13:52:13,015 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - object not resolved in any cache [Test.Model.Person#1]

2006-04-15 13:52:13,015 [AdpaterExeMgrThread1] DEBUG NHibernate.Persister.EntityPersister [(null)] - Materializing entity: Test.Model.Person#1


2006-04-15 13:52:13,078 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.BatcherImpl [(null)] - Opened new IDbCommand, open IDbCommands :1

2006-04-15 13:52:13,078 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.BatcherImpl [(null)] - Building an IDbCommand object for the SqlString: SELECT person0_.id as id0_, person0_.name as name0_ FROM Person person0_ WHERE person0_.id=:id

2006-04-15 13:52:13,093 [AdpaterExeMgrThread1] DEBUG NHibernate.Type.Int32Type [(null)] - binding '1' to parameter: 0

2006-04-15 13:52:13,093 [AdpaterExeMgrThread1] INFO  NHibernate.Loader.Loader [(null)] - SELECT person0_.id as id0_, person0_.name as name0_ FROM Person person0_ WHERE person0_.id=@p0

2006-04-15 13:52:13,093 [AdpaterExeMgrThread1] DEBUG NHibernate.SQL [(null)] - SELECT person0_.id as id0_, person0_.name as name0_ FROM Person person0_ WHERE person0_.id=@p0

2006-04-15 13:52:13,093 [AdpaterExeMgrThread1] DEBUG NHibernate.SQL [(null)] - @p0 = '1'


2006-04-15 13:52:13,093 [AdpaterExeMgrThread1] DEBUG NHibernate.Connection.DriverConnectionProvider [(null)] - Obtaining IDbConnection from Driver

2006-04-15 13:52:13,859 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.BatcherImpl [(null)] - Opened Reader, open Readers :1

2006-04-15 13:52:13,859 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Loader [(null)] - processing result set

2006-04-15 13:52:13,875 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Loader [(null)] - result row: 1

2006-04-15 13:52:13,875 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Loader [(null)] - Initializing object from DataReader: 1

2006-04-15 13:52:13,875 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Loader [(null)] - Hydrating entity: Test.Model.Person#1

2006-04-15 13:52:13,906 [AdpaterExeMgrThread1] DEBUG NHibernate.Type.StringType [(null)] - returning 'Jackie Chan' as column: name0_

2006-04-15 13:52:13,906 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Loader [(null)] - done processing result set (1 rows)

2006-04-15 13:52:13,937 [AdpaterExeMgrThread1] DEBUG NHibernate.Driver.NHybridDataReader [(null)] - running NHybridDataReader.Dispose()

2006-04-15 13:52:13,937 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.BatcherImpl [(null)] - Closed Reader, open Readers :0

2006-04-15 13:52:13,937 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.BatcherImpl [(null)] - Closed IDbCommand, open IDbCommands :0

2006-04-15 13:52:13,937 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Loader [(null)] - total objects hydrated: 1

2006-04-15 13:52:13,953 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - resolving associations for: [Test.Model.Person#1]

2006-04-15 13:52:13,953 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - done materializing entity [Test.Model.Person#1]

2006-04-15 13:52:13,953 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - initializing non-lazy collections

2006-04-15 13:52:13,953 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - closing session

2006-04-15 13:52:13,953 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - disconnecting session

2006-04-15 13:52:13,953 [AdpaterExeMgrThread1] DEBUG NHibernate.Connection.ConnectionProvider [(null)] - Closing connection

2006-04-15 13:52:13,968 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionImpl [(null)] - transaction completion

 

在其中,我們可以發現:


2006-04-15 13:52:13,093 [AdpaterExeMgrThread1] DEBUG NHibernate.SQL [(null)] - SELECT person0_.id as id0_, person0_.name as name0_ FROM Person person0_ WHERE person0_.id=@p0

2006-04-15 13:52:13,093 [AdpaterExeMgrThread1] DEBUG NHibernate.SQL [(null)] - @p0 = '1'


對了,就是這裏,NHibernate替我們構造了一條sql語句,並添加一個參數,然後將我們在代碼中賦的id值1來填充,這樣,一條完整的可以執行的sql語句產生了。

請注意:在產生sql語句的前面,NHibernate構造了一個IDBCommand,然後在sql語句產生完全後,獲取連接,通過一個DataReader來填充Persion對象給我們使用,這就是NHibernate替我們做的事,是不是很簡單啊?(真的很簡單嗎??看看源代碼吧!!)

請仔細研究輸出的日誌。

 

如法炮製,我們添加另外3個Test,完成單個表的全部CRUD操作,如下完整代碼:

[TestMethod]

        public void TestCreate()

        {

            Person person = new Person();

            person.Name = "Jackie Chan";

 

            ITransaction trans = session.BeginTransaction();

            try

            {

                session.Save(person);

                trans.Commit();

                Assert.IsTrue(person.Id > 0);

            }

            catch (Exception ex)

            {

                trans.Rollback();

                Assert.Fail(ex.Message);

            }

        }

 

        [TestMethod]

        public void TestUpdate()

        {

            Person person = (Person)session.Get(typeof(Person), 1);

            person.Name = "Jet Li";

 

            ITransaction trans = session.BeginTransaction();

            try

            {

                session.Save(person);

                trans.Commit();

                Assert.IsTrue(person.Name == "Jet Li");

            }

            catch (Exception ex)

            {

                trans.Rollback();

                Assert.Fail(ex.Message);

            }

        }

 

        [TestMethod]

        public void TestRead()

        {

            Person person = (Person)session.Get(typeof(Person), 1);

            Assert.IsTrue(person.Name == "Jackie Chan");

        }

 

        [TestMethod]

        public void TestDelete()

        {

            Person person = (Person)session.Get(typeof(Person), 1);

 

            ITransaction trans = session.BeginTransaction();

            try

            {

                session.Delete(person);

                trans.Commit();

            }

            catch (Exception ex)

            {

                trans.Rollback();

                Assert.Fail(ex.Message);

            }

        }

 

Delete的方法如下:

void Delete(object obj);

直接傳入需要delete的對象即可。

 

好了,基本的操作都完成了,是不是很Easy?
好了,這一篇就講這麼多,我們下次再接着練習。
 

Step by Step,顧名思義,是一步一步來的意思,整個教程我將貫徹這一理念。

任何建議或者批評,請e:[email protected]

 

posted on 2006-04-15 14:32 abluedog 閱讀(6681) 評論(21)  編輯 收藏 所屬分類: NHibernate
評論
#1樓  2006-04-15 17:46 tst [未註冊用戶]

強烈支持!   回覆  引用  查看   
#2樓  2006-04-16 11:46 JetLee     

支持,學習中...
期待更多!   回覆  引用  查看   
#3樓  2006-04-16 12:59 javac [未註冊用戶]

沒有Visual Studio 2005 team system版測試怎麼做?   回覆  引用  查看   
#4樓  2006-04-16 13:18 天夢     

期待更多!學習。。。。   回覆  引用  查看   
#5樓 [樓主] 2006-04-16 15:37 abluedog     

@javac
你可以使用nunit,基本上差別不大。之所以使用MS Test,是因爲ms既然在ide裏包含了測試框架,那麼不管我們喜歡還是不喜歡,它肯定會獲得比較廣泛的應用——這也是沒辦法的事。   回覆  引用  查看   
#6樓  2006-04-16 21:18 SOSOS's BLog     

建議:希望abludedog把每次做好的教程也提供下載,這樣更能幫助入門者,更好提高大家的水平!謝謝   回覆  引用  查看   
#7樓  2006-04-19 20:19 mzl [未註冊用戶]

建議:希望abludedog把每次做好的教程也提供下載,這樣更能幫助入門者,更好提高大家的水平!謝謝
~~~~~~~~~~~~~~~~~~~~~~~~~~~~


支持。。。。。。。
謝謝abluedog!!!!!!!!   回覆  引用  查看   
#8樓  2006-04-20 14:14 和地球人一起 [未註冊用戶]

[TestClass]等等這些屬性都怎麼來的。作者也沒說清楚啊。......
可能因爲我也沒用ms test吧,所以總覺得不知所云,突然就出來了個test1,說類也不是類,說工程也不是工程,說應用程序也不是應用程序,而且還那麼屬性不知從哪裏來的餓。。..搞的我趕緊下了個nunit,還沒開始用呢,,,正一頭霧水中。..   回覆  引用  查看   
#9樓 [樓主] 2006-04-22 20:10 abluedog     

@SOSOS's BLog
@mzl
目前還是以文章爲主,測試程序都是說明性的,等這個系列完了後,我會再重新整理出來一份文檔及示例代碼,抱歉了。

@和地球人一起
抱歉讓您理解不清楚了。
我在前面已經說明是使用的vs2005演示的,可能我忘了一點,我使用的是team suite版,如果您使用的是professional版或者更低的版本,可能不會包含完整的測試框架。
至於mstest,我想您看一下sdk就應該很清楚了,我在這裏就不費筆墨在nhibernate外的地方了。   回覆  引用  查看   
#10樓  2006-06-07 16:50 家中慢步     

好文章,找了很久了,這一系列入門教程最清楚了.

請問,如何實現dataGrid的綁定,分頁之類的效果呢?   回覆  引用  查看   
#11樓  2006-08-24 10:16 小草     

呵呵。不錯呀。我試完會繼續發佈補充說明,呵呵。   回覆  引用  查看   
#12樓  2006-08-25 18:19 viptell [未註冊用戶]

類初始化方法 Test1.UnitTest1.MyClassInitialize 引發異常。NHibernate.MappingException: NHibernate.MappingException: The dialect was not set. Set the property hibernate.dialect. ---> NHibernate.HibernateException: The dialect was not set. Set the property hibernate.dialect.


-----------------

怎麼處理啊,大哥?   回覆  引用  查看   
#13樓  2006-08-30 17:58 kegogo     

樓主爲什麼我的這裏沒有那條sql語句
我的測試結果如下:
2006-08-30 17:54:18,796 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Environment [(null)] - NHibernate 1.2.0.1001 (1.2.0.Alpha1)
2006-08-30 17:54:18,890 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Environment [(null)] - Bytecode provider name : lcg
2006-08-30 17:54:18,906 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Environment [(null)] - Using reflection optimizer
2006-08-30 17:54:18,906 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Configuration [(null)] - Searching for mapped documents in assembly: Test.Model
2006-08-30 17:54:18,953 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Configuration [(null)] - Found mapping documents in assembly: Test.Model.Person.hbm.xml
2006-08-30 17:54:19,703 [AdpaterExeMgrThread1] INFO NHibernate.Dialect.Dialect [(null)] - Using dialect: NHibernate.Dialect.MsSql2000Dialect
2006-08-30 17:54:19,843 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.HbmBinder [(null)] - Mapping class: Test.Model.Person -> Person
2006-08-30 17:54:19,890 [AdpaterExeMgrThread1] DEBUG NHibernate.Cfg.HbmBinder [(null)] - Mapped property: Id -> id, type: Int32
2006-08-30 17:54:19,906 [AdpaterExeMgrThread1] DEBUG NHibernate.Cfg.HbmBinder [(null)] - Mapped property: Name -> name, type: String
2006-08-30 17:54:19,921 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Configuration [(null)] - processing one-to-many association mappings
2006-08-30 17:54:19,921 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Configuration [(null)] - processing one-to-one association property references
2006-08-30 17:54:19,921 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Configuration [(null)] - processing foreign key constraints
2006-08-30 17:54:19,953 [AdpaterExeMgrThread1] INFO NHibernate.Dialect.Dialect [(null)] - Using dialect: NHibernate.Dialect.MsSql2000Dialect
2006-08-30 17:54:19,953 [AdpaterExeMgrThread1] INFO NHibernate.Connection.ConnectionProviderFactory [(null)] - Intitializing connection provider: NHibernate.Connection.DriverConnectionProvider
2006-08-30 17:54:19,953 [AdpaterExeMgrThread1] INFO NHibernate.Connection.ConnectionProvider [(null)] - Configuring ConnectionProvider
2006-08-30 17:54:19,953 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.SettingsFactory [(null)] - Optimize cache for minimal puts: False
2006-08-30 17:54:19,953 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.SettingsFactory [(null)] - Query language substitutions: {}
2006-08-30 17:54:19,953 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.SettingsFactory [(null)] - cache provider: NHibernate.Cache.HashtableCacheProvider
2006-08-30 17:54:19,953 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.SettingsFactory [(null)] - Using Isolation Level: ReadCommitted
2006-08-30 17:54:19,953 [AdpaterExeMgrThread1] INFO NHibernate.Cfg.Configuration [(null)] - instantiating and configuring caches
2006-08-30 17:54:19,968 [AdpaterExeMgrThread1] INFO NHibernate.Impl.SessionFactoryImpl [(null)] - building session factory
2006-08-30 17:54:19,968 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionFactoryImpl [(null)] - instantiating session factory with properties: {hibernate.connection.driver_class=NHibernate.Driver.SqlClientDriver, hibernate.dialect=NHibernate.Dialect.MsSql2000Dialect, hibernate.connection.provider=NHibernate.Connection.DriverConnectionProvider, hibernate.connection.isolation=ReadCommitted, hibernate.use_reflection_optimizer=true, show_sql=true, hibernate.connection.connection_string=Server=localhost;Initial Catalog=NHibernate;Integrated Security=SSPI}
2006-08-30 17:54:20,125 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionFactoryObjectFactory [(null)] - initializing class SessionFactoryObjectFactory
2006-08-30 17:54:20,125 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionFactoryObjectFactory [(null)] - registered: 356b1fa6d48642e198ec974f094c7385(unnamed)
2006-08-30 17:54:20,125 [AdpaterExeMgrThread1] INFO NHibernate.Impl.SessionFactoryObjectFactory [(null)] - no name configured
2006-08-30 17:54:20,203 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Entity.AbstractEntityLoader [(null)] - Static select for entity Test.Model.Person: SELECT person0_.id as id0_, person0_.name as name0_0_ FROM Person person0_ WHERE person0_.id=:id
2006-08-30 17:54:20,203 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Entity.AbstractEntityLoader [(null)] - Static select for entity Test.Model.Person: SELECT person0_.id as id0_, person0_.name as name0_0_ FROM Person person0_ WHERE person0_.id=:id
2006-08-30 17:54:20,203 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Entity.AbstractEntityLoader [(null)] - Static select for entity Test.Model.Person: SELECT person0_.id as id0_, person0_.name as name0_0_ FROM Person person0_ WHERE person0_.id=:id
2006-08-30 17:54:20,203 [AdpaterExeMgrThread1] DEBUG NHibernate.Loader.Entity.AbstractEntityLoader [(null)] - Static select for entity Test.Model.Person: SELECT person0_.id as id0_, person0_.name as name0_0_ FROM Person person0_ WHERE person0_.id=:id
2006-08-30 17:54:20,250 [AdpaterExeMgrThread1] DEBUG NHibernate.Impl.SessionFactoryImpl [(null)] - Instantiated session factory   回覆  引用  查看   
#14樓  2006-10-30 16:52 風吹河岸柳輕揚     

我遇到的問題是,在創建測試項目的時候,沒有指定的模板。難道是我裝的模板有問題嗎?   回覆  引用  查看   
#15樓 [TrackBack] 2007-07-15 13:37 大冰

NHibernateStepbyStep(二)單表操作 NHibernateStepbyStep(二)單表操作接着第一期,我們繼續。爲了方便學習測試,從今天開始我將使用MS...
[引用提示]大冰引用了該文章, 地址: http://www.cnblogs.com/hanbing768/archive/2007/07/15/818679.html   回覆  引用  查看   
#16樓  2007-07-30 10:50 lanfker [未註冊用戶]

雖然日誌看不懂,但是正文還是看懂了,很感激LZ   回覆  引用  查看   
#17樓  2007-08-04 22:30 akuan [未註冊用戶]

在step(二)這裏有過地方樓主沒有明確說出來,就是配置文件是放在Test1測試項目裏面的,如果不把配置文件放至測試項目中,測會報NHibernate.Dialect及lognet4的一些異常出來,這樣會導致測試通不過.

向Test1添加配置文件:
右擊[Test1]項目,選擇添加新建項,然後在打開的窗何中選擇"應用程序配置文件",然後再把樓主上面說的配置Copy一份進去就可以了.   回覆  引用  查看   
#18樓  2007-08-10 09:51 高海東     

我也開始學習這個框架了   回覆  引用  查看   
#19樓  2007-09-03 13:58 zhaoyicun [未註冊用戶]

Configuration config = new Configuration().AddAssembly("Test.Model");這一句出現:
“NHibernate.Cfg.Environment”的類型初始值設定項引發異常。
的錯誤,怎麼解決啊?   回覆  引用  查看   
#20樓  2007-10-12 14:25 qc [未註冊用戶]

@viptell
應該是你的配置文件種的key=“hibernate.dialect”中的名字寫錯了   回覆  引用  查看   
#21樓  2008-03-26 14:24 老志     

可惜..我不是 team suit..
順便學了NUnit..感恩

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章