Winform開(kāi)發(fā)框架之存儲(chǔ)過(guò)程的支持--存儲(chǔ)過(guò)程的實(shí)現(xiàn)和演化提煉(2)

本篇繼續(xù)上篇《Winform開(kāi)發(fā)框架之存儲(chǔ)過(guò)程的支持--存儲(chǔ)過(guò)程的實(shí)現(xiàn)和演化提煉(1)》來(lái)對(duì)Winform開(kāi)發(fā)框架之存儲(chǔ)過(guò)程的支持進(jìn)行介紹,上篇主要介紹了SQLServer和Oracle兩種數(shù)據(jù)庫(kù)對(duì)常規(guī)存儲(chǔ)過(guò)程的編寫(xiě)和對(duì)比骚秦,本篇主要介紹如何在C#里面,如何對(duì)這些存儲(chǔ)過(guò)程進(jìn)行調(diào)用璧微,并獲取到對(duì)應(yīng)的數(shù)據(jù)類(lèi)型作箍,如輸出參數(shù),單個(gè)數(shù)據(jù)記錄前硫,多個(gè)數(shù)據(jù)記錄等情況胞得。最后在完成實(shí)現(xiàn)功能的基礎(chǔ)上,對(duì)這些實(shí)現(xiàn)進(jìn)行演化提煉屹电,并擴(kuò)展到我的WInform開(kāi)發(fā)框架里面阶剑,實(shí)現(xiàn)功能重用、代碼簡(jiǎn)化的目的危号。

1牧愁、數(shù)據(jù)訪問(wèn)接口的定義

我們整個(gè)實(shí)例是以一個(gè)客戶(hù)表T_Customer為例進(jìn)行講解的,整個(gè)表的框架支持代碼外莲,可以通過(guò)代碼生成工具進(jìn)行快速生成猪半,生成后包括了IDAL、Entity苍狰、DALSQL办龄、BLL層代碼,然后可以利用代碼進(jìn)行測(cè)試存儲(chǔ)過(guò)程是否執(zhí)行成功等功能淋昭。
數(shù)據(jù)訪問(wèn)層的定義俐填,依照框架的分層模式來(lái)處理,后面我們?cè)谠黾覦ALOracle對(duì)Oracle數(shù)據(jù)庫(kù)進(jìn)行支持即可翔忽。
生成后數(shù)據(jù)訪問(wèn)層接口英融,他們通過(guò)基類(lèi)接口繼承的方式盏檐,已經(jīng)具有了常規(guī)的增刪改查、分頁(yè)等系列接口驶悟,但是其他業(yè)務(wù)接口還是需要自己定義的胡野,如數(shù)據(jù)訪問(wèn)接口成的定義如下所示。

namespace WHC.TestProject.IDAL
{
    /// <summary>
    /// 客戶(hù)信息
    /// </summary>
    public interface ICustomer : IBaseDAL<CustomerInfo>
    {
    }
}

這里面的代碼很簡(jiǎn)單痕鳍,沒(méi)有多余的代碼行硫豆,那么里面究竟發(fā)生了什么呢,其中的IBaseDAL又是什么定義呢笼呆?



其實(shí)熊响,IBaseDAL就是定義了很多我們開(kāi)發(fā)用到的基礎(chǔ)接口,如標(biāo)準(zhǔn)的增刪改查诗赌,以及衍生出來(lái)的一些其他接口汗茄,如分頁(yè)查詢(xún),條件查詢(xún)等接口內(nèi)容铭若。這個(gè)ICustomer就是用來(lái)定義一些除了標(biāo)準(zhǔn)接口不能實(shí)現(xiàn)外的業(yè)務(wù)接口洪碳。
如果我們需要實(shí)現(xiàn)基于存儲(chǔ)過(guò)程的接口,我們可能就需要增加一些接口定義叼屠,如下所示瞳腌。

namespace WHC.TestProject.IDAL
{
    /// <summary>
    /// 客戶(hù)信息
    /// </summary>
    public interface ICustomer : IBaseDAL<CustomerInfo>
    {
        #region 使用存儲(chǔ)過(guò)程
        /// <summary>
        /// 使用存儲(chǔ)過(guò)程插入數(shù)據(jù)
        /// </summary>
        /// <param name="info">實(shí)體對(duì)象</param>
        /// <returns></returns>
        bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null);

        /// <summary>
        /// 使用存儲(chǔ)過(guò)程更新數(shù)據(jù)
        /// </summary>
        /// <param name="info">實(shí)體對(duì)象</param>
        /// <returns></returns>
        bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null);

        /// <summary>
        /// 使用存儲(chǔ)過(guò)程獲取所有數(shù)據(jù)
        /// </summary>
        /// <returns></returns>
        List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null);

        /// <summary>
        /// 使用存儲(chǔ)過(guò)程獲取所有數(shù)據(jù)
        /// </summary>
        /// <returns></returns>
        DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null);
            
        /// <summary>
        /// 使用存儲(chǔ)過(guò)程,根據(jù)ID獲取對(duì)應(yīng)記錄
        /// </summary>
        /// <param name="ID"></param>
        /// <returns></returns>
        CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null);

        /// <summary>
        /// 使用存儲(chǔ)過(guò)程环鲤,判斷記錄ID是否存在
        /// </summary>
        /// <param name="ID">記錄ID</param>
        /// <returns></returns>
        bool StorePorc_ExistByID(string ID, DbTransaction trans = null);

        /// <summary>
        /// 使用存儲(chǔ)過(guò)程纯趋,根據(jù)ID刪除對(duì)應(yīng)記錄
        /// </summary>
        /// <param name="ID">記錄ID</param>
        /// <returns></returns>
        bool StorePorc_DeleteByID(string ID, DbTransaction trans = null);

        /// <summary>
        /// 獲取客戶(hù)的最大年齡
        /// </summary>
        /// <returns></returns>
        int StorePorc_GetMaxAge();

        #endregion
    }

對(duì)于插入、更新和刪除這樣的操作冷离,我們只需要返回它是否成功就可以了吵冒,那么它的接口實(shí)現(xiàn)應(yīng)該是如何的呢?

2西剥、SqlServer存儲(chǔ)過(guò)程的調(diào)用實(shí)現(xiàn)

由于我們的Winform開(kāi)發(fā)框架底層是利用微軟企業(yè)庫(kù)EnterpriseLibrary來(lái)訪問(wèn)數(shù)據(jù)的痹栖,那么對(duì)應(yīng)這個(gè)企業(yè)庫(kù)的使用存儲(chǔ)過(guò)程的方法,也就是我們的實(shí)現(xiàn)了瞭空。

下面的代碼就是它們對(duì)應(yīng)的SqlServer實(shí)現(xiàn)了揪阿。

public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
    string procName = "T_Customer_Insert";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    db.AddInParameter(command, "@ID", DbType.String, info.ID);
    db.AddInParameter(command, "@Name", DbType.String, info.Name);
    db.AddInParameter(command, "@Age", DbType.Int32, info.Age);
                           
    bool result = false;
    if (trans != null)
    {
        result = db.ExecuteNonQuery(command, trans) > 0;
    }
    else
    {
        result = db.ExecuteNonQuery(command) > 0;
    }
    return result;
}

public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
    string procName = "T_Customer_UpdateByID";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    db.AddInParameter(command, "@ID", DbType.String, info.ID);
    db.AddInParameter(command, "@Name", DbType.String, info.Name);
    db.AddInParameter(command, "@Age", DbType.Int32, info.Age);
                           
    bool result = false;
    if (trans != null)
    {
        result = db.ExecuteNonQuery(command, trans) > 0;
    }
    else
    {
        result = db.ExecuteNonQuery(command) > 0;
    }
    return result;       
}


public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null)
{
    string procName = "T_Customer_DeleteByID";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);
    db.AddInParameter(command, "@ID", DbType.String, ID);

    bool result = false;
    if (trans != null)
    {
        result = db.ExecuteNonQuery(command, trans) > 0;
    }
    else
    {
        result = db.ExecuteNonQuery(command) > 0;
    }
    return result;  
}

對(duì)于有返回輸出參數(shù)的值,我們的做法有些不同咆畏,不過(guò)最主要的還是最終獲取它的輸出參數(shù)值而已南捂,代碼如下所示。

public bool StorePorc_ExistByID(string ID, DbTransaction trans = null)
{
    bool result = false;
    string procName = "T_Customer_ExistByID";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);
    db.AddInParameter(command, "@ID", DbType.String, ID);
    db.AddOutParameter(command, "@Exist", DbType.Int32, 0);//輸出參數(shù)
                
    if (trans != null)
    {                
        db.ExecuteNonQuery(command, trans);
    }
    else
    {
        db.ExecuteNonQuery(command);
    }

    int iExist = 0; 
    int.TryParse(db.GetParameterValue(command, "@Exist").ToString(), out iExist);

    result = iExist > 0;
    return result;       
}


public int StorePorc_GetMaxAge()
{
    string procName = "T_Customer_MaxAge";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    db.AddOutParameter(command, "@MaxAge", DbType.Int32, 0);//輸出參數(shù)

    db.ExecuteNonQuery(command);

    int maxAge = 0;
    int.TryParse(db.GetParameterValue(command, "@MaxAge").ToString(), out maxAge);

    return maxAge;     
}

上面的代碼溺健,主要就是利用了AddOutParameter對(duì)輸出參數(shù)的信息進(jìn)行設(shè)置鞭缭,輸出參數(shù)的數(shù)據(jù)類(lèi)型要和腳本里面的類(lèi)型定義對(duì)應(yīng),它的AddOutParameter的size參數(shù)值岭辣,可以為0。

最后我們通過(guò)db.GetParameterValue(command, "@MaxAge")的方式獲取它的輸出參數(shù)的值仑濒,并返回即可。

最后一個(gè)例子是介紹如何通過(guò)代碼調(diào)用搞动,獲得它的實(shí)體對(duì)象或者實(shí)體對(duì)象列表躏精,以及DataTable集合對(duì)象的例子了渣刷,這個(gè)也相對(duì)不是很麻煩鹦肿,參照框架里面的做法即可。

獲取實(shí)體對(duì)象信息的代碼如下所示辅柴。

public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
    string procName = "T_Customer_SelectByID";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);
    db.AddInParameter(command, "@ID", DbType.String, ID);

    CustomerInfo entity = null;
    if (trans != null)
    {
        using (IDataReader dr = db.ExecuteReader(command, trans))
        {
            if (dr.Read())
            {
                entity = DataReaderToEntity(dr);
            }
        }
    }
    else
    {
        using (IDataReader dr = db.ExecuteReader(command))
        {
            if (dr.Read())
            {
                entity = DataReaderToEntity(dr);
            }
        }
    }
    return entity;
}

獲取集合的代碼如下所示箩溃。

public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
    string procName = "T_Customer_SelectAll";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    List<CustomerInfo> list = new List<CustomerInfo>();
    CustomerInfo entity = null;
    if (trans != null)
    {
        using (IDataReader dr = db.ExecuteReader(command, trans))
        {
            while (dr.Read())
            {
                entity = DataReaderToEntity(dr);
                list.Add(entity);
            }
        }
    }
    else
    {
        using (IDataReader dr = db.ExecuteReader(command))
        {
            while (dr.Read())
            {
                entity = DataReaderToEntity(dr);
                list.Add(entity);
            }
        }
    }
    return list;
}

public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null)
{
    string procName = "T_Customer_SelectAll";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    DataTable dt = null;
    if (trans != null)
    {
        dt = db.ExecuteDataSet(command, trans).Tables[0];
    }
    else
    {
        dt = db.ExecuteDataSet(command).Tables[0];
    }
    return dt;
}

3、Oracle存儲(chǔ)過(guò)程的調(diào)用實(shí)現(xiàn)

上面是基于SqlServer存儲(chǔ)過(guò)程的調(diào)用碌嘀,前面的一篇文章我們介紹了存儲(chǔ)過(guò)程的Oracle定義涣旨,是增加了一個(gè)游標(biāo)來(lái)進(jìn)行記錄行數(shù)據(jù)的處理的,不管對(duì)于單行記錄股冗,還是多行記錄霹陡,都是用了游標(biāo)的輸出參數(shù)的,那么在客戶(hù)端里面止状,使用EnterpriseLibrary烹棉,應(yīng)該如何調(diào)用,并且不需要傳入這個(gè)輸出參數(shù)的呢怯疤,做法其實(shí)很類(lèi)似浆洗,只是有一點(diǎn)差異而已。

我們先從最簡(jiǎn)單的Oracle存儲(chǔ)過(guò)程調(diào)用案例開(kāi)始集峦,介紹如何調(diào)用插入塔淤、更新和刪除操作的Oracle存儲(chǔ)過(guò)程的調(diào)用聪黎。這里和SqlServer的類(lèi)似挺举,不同的是我們使用了p_前綴來(lái)定義參數(shù)(基于Oracle的通用腳本參數(shù)定義規(guī)則)脂崔。

public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
    string procName = "T_Customer_Insert";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    db.AddInParameter(command, "p_ID", DbType.String, info.ID);
    db.AddInParameter(command, "p_Name", DbType.String, info.Name);
    db.AddInParameter(command, "p_Age", DbType.Int32, info.Age);

    bool result = false;
    if (trans != null)
    {
        result = db.ExecuteNonQuery(command, trans) > 0;
    }
    else
    {
        result = db.ExecuteNonQuery(command) > 0;
    }
    return result;
}

public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
    string procName = "T_Customer_UpdateByID";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    db.AddInParameter(command, "p_ID", DbType.String, info.ID);
    db.AddInParameter(command, "p_Name", DbType.String, info.Name);
    db.AddInParameter(command, "p_Age", DbType.Int32, info.Age);

    bool result = false;
    if (trans != null)
    {
        result = db.ExecuteNonQuery(command, trans) > 0;
    }
    else
    {
        result = db.ExecuteNonQuery(command) > 0;
    }
    return result;
}

Oracle輸出外部參數(shù)的做法也和sqlServer類(lèi)似,具體調(diào)用代碼如下所示。

public int StorePorc_GetMaxAge()
{
    string procName = "T_Customer_MaxAge";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    db.AddOutParameter(command, "p_MaxAge", DbType.Int32, 0);//輸出參數(shù)

    db.ExecuteNonQuery(command);

    int maxAge = 0;
    int.TryParse(db.GetParameterValue(command, "p_MaxAge").ToString(), out maxAge);

    return maxAge;
}  

其他的也就很類(lèi)似产弹,就不再一一贅述了,基本上和SqlServer的一致斤斧,我們節(jié)省篇幅撬讽,用來(lái)看看如何調(diào)用返回記錄的查詢(xún)接口。下面是對(duì)應(yīng)的Oracle存儲(chǔ)過(guò)程的調(diào)用代碼

public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
    string procName = "T_Customer_SelectByID";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);
    db.AddInParameter(command, "p_ID", DbType.String, ID);

    CustomerInfo entity = null;
    if (trans != null)
    {
        using (IDataReader dr = db.ExecuteReader(command, trans))
        {
            if (dr.Read())
            {
                entity = DataReaderToEntity(dr);
            }
        }
    }
    else
    {
        using (IDataReader dr = db.ExecuteReader(command))
        {
            if (dr.Read())
            {
                entity = DataReaderToEntity(dr);
            }
        }
    }
    return entity;
}

返回多條記錄的操作代碼如下所示。

public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
    string procName = "T_Customer_SelectAll";

    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(procName);

    List<CustomerInfo> list = new List<CustomerInfo>();
    CustomerInfo entity = null;
    if (trans != null)
    {
        using (IDataReader dr = db.ExecuteReader(command, trans))
        {
            while (dr.Read())
            {
                entity = DataReaderToEntity(dr);
                list.Add(entity);
            }
        }
    }
    else
    {
        using (IDataReader dr = db.ExecuteReader(command))
        {
            while (dr.Read())
            {
                entity = DataReaderToEntity(dr);
                list.Add(entity);
            }
        }
    }
    return list;
}

看完上面兩個(gè)對(duì)記錄處理的接口扇谣,我們看到,還是對(duì)我們?cè)贠racle存儲(chǔ)過(guò)程里面定義的輸出游標(biāo)參數(shù)忽略處理鸯绿,我們不需要對(duì)它進(jìn)行傳值毒返,它好像是透明的,呵呵盆赤。

這樣它的做法就和SqlServer個(gè)各個(gè)接口實(shí)現(xiàn)也都差不多的了。

下面的腳本是我們之前定義的Oracle存儲(chǔ)過(guò)程腳本淑际,方便對(duì)比參照一下調(diào)用的函數(shù)代碼庸追。

------------------------------------
--作者:伍華聰 http://wuhuacong.cnblogs.com
--創(chuàng)建時(shí)間:2014年11月27日 
--功能描述:以字段ID為關(guān)鍵字簿训,檢索表中的數(shù)據(jù) 
------------------------------------
 Create Or Replace Procedure T_Customer_SelectByID 
 ( 
     cur_OUT OUT MyCURSOR.cur_OUT  ,
     p_ID IN T_CUSTOMER.ID%TYPE 
 ) 
 AS 
 Begin 
 OPEN cur_OUT FOR Select * from T_CUSTOMER Where ID = p_ID ; 
 End; 
 / 

4强品、業(yè)務(wù)邏輯層的實(shí)現(xiàn)

上面我們定義了數(shù)據(jù)訪問(wèn)接口,以及兩種數(shù)據(jù)實(shí)現(xiàn)層夫晌,在框架里面會(huì)根據(jù)不同的數(shù)據(jù)庫(kù)類(lèi)型配置,然后從不同的數(shù)據(jù)庫(kù)訪問(wèn)層構(gòu)建對(duì)象的凶掰,業(yè)務(wù)邏輯層主要就是對(duì)他們的接口進(jìn)行調(diào)用了前翎,具體代碼如下所示。

/// <summary>
/// 客戶(hù)信息
/// </summary>
public class Customer : BaseBLL<CustomerInfo>
{
    public Customer() : base()
    {
        base.Init(this.GetType().FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
    }

    /// <summary>
    /// 根據(jù)客戶(hù)名稱(chēng)獲取客戶(hù)列表
    /// </summary>
    /// <param name="name">客戶(hù)名稱(chēng)</param>
    /// <returns></returns>
    public List<CustomerInfo> FindByName(string name)
    {
        string condition = string.Format("Name like '%{0}%' ", name);
        return baseDal.Find(condition);
    }

    #region 使用存儲(chǔ)過(guò)程
    public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
    {
        ICustomer dal = baseDal as ICustomer;
        return dal.StorePorc_Insert(info, trans);
    }

    public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
    {
        ICustomer dal = baseDal as ICustomer;
        return dal.StorePorc_Update(info, trans);
    }

    public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
    {
        ICustomer dal = baseDal as ICustomer;
        return dal.StorePorc_GetAll(trans);
    }

    public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null)
    {
        ICustomer dal = baseDal as ICustomer;
        return dal.StorePorc_GetAllToDataTable(trans);
    }

    public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
    {
        ICustomer dal = baseDal as ICustomer;
        return dal.StorePorc_FindByID(ID, trans);
    }

    public bool StorePorc_ExistByID(string ID, DbTransaction trans = null)
    {
        ICustomer dal = baseDal as ICustomer;
        return dal.StorePorc_ExistByID(ID, trans);
    }

    public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null)
    {
        ICustomer dal = baseDal as ICustomer;
        return dal.StorePorc_DeleteByID(ID, trans);
    }

    public int StorePorc_GetMaxAge()
    {
        ICustomer dal = baseDal as ICustomer;
        return dal.StorePorc_GetMaxAge();
    }
    #endregion
}

為了驗(yàn)證我們的實(shí)現(xiàn)是否能夠正常處理,并順利獲取對(duì)應(yīng)的對(duì)象或者集合赘理,我們需要編寫(xiě)一些代碼,用來(lái)對(duì)它進(jìn)行測(cè)試施流。

測(cè)試的代碼如下所示瞪醋。

/// <summary>
/// 測(cè)試存儲(chǔ)過(guò)程的插入银受、修改、返回實(shí)體類(lèi)顶霞、返回實(shí)體類(lèi)集合、返回DataTable對(duì)象鲜侥、輸出參數(shù)等接口
/// </summary>
private void btnTestStoreProc_Click(object sender, EventArgs e)
{
    //定義一個(gè)實(shí)體類(lèi)的數(shù)據(jù)
    CustomerInfo info = new CustomerInfo();
    info.Name = "測(cè)試名稱(chēng)";
    info.Age = 20;

    //調(diào)用存儲(chǔ)過(guò)程插入數(shù)據(jù)崎苗,并判斷是否成功
    bool inserted = BLLFactory<Customer>.Instance.StorePorc_Insert(info);
    Debug.Assert(inserted);

    //調(diào)用存儲(chǔ)過(guò)程胆数,獲取輸出參數(shù),獲得最大年齡值
    int maxAge = BLLFactory<Customer>.Instance.StorePorc_GetMaxAge();
    Debug.Assert(maxAge > 0);

    //調(diào)用存儲(chǔ)過(guò)程判莉,修改客戶(hù)名稱(chēng)
    info.Name = "修改名稱(chēng)";
    bool updated = BLLFactory<Customer>.Instance.StorePorc_Update(info);

    //調(diào)用存儲(chǔ)過(guò)程,獲取最新的實(shí)體類(lèi)對(duì)象锰镀,并對(duì)比是否修改成功
    CustomerInfo newInfo = BLLFactory<Customer>.Instance.StorePorc_FindByID(info.ID);
    Debug.Assert(newInfo != null);
    Debug.Assert(newInfo.Name == info.Name);

    //調(diào)用存儲(chǔ)過(guò)程泳炉,獲取輸出參數(shù),判斷指定ID記錄是否存在
    bool exist = BLLFactory<Customer>.Instance.StorePorc_ExistByID(info.ID);
    Debug.Assert(exist);

    //調(diào)用存儲(chǔ)過(guò)程翠胰,獲取全部實(shí)體列表集合斤富,判斷實(shí)體類(lèi)列表是否正確
    List<CustomerInfo> list = BLLFactory<Customer>.Instance.StorePorc_GetAll();
    Debug.Assert(list.Count > 0);

    //調(diào)用存儲(chǔ)過(guò)程焕参,獲取DataTable對(duì)象叠纷,判斷集合不為空
    DataTable dt = BLLFactory<Customer>.Instance.StorePorc_GetAllToDataTable();
    Debug.Assert(dt.Rows.Count > 0);

    //調(diào)用存儲(chǔ)過(guò)程,執(zhí)行刪除操作航厚,并判斷是否成功了
    bool deleted = BLLFactory<Customer>.Instance.StorePorc_DeleteByID(info.ID);
    Debug.Assert(deleted);

    string result = "全部操作完成";
    Console.WriteLine(result);
    MessageUtil.ShowTips(result);
}

5眯漩、具體測(cè)試和驗(yàn)證

為了對(duì)他們進(jìn)行測(cè)試,我們需要分別對(duì)SqlServer和Oracle進(jìn)行測(cè)試队萤,然后才能確認(rèn)我們的實(shí)現(xiàn)是正確的浮禾。
分別在SQLServer和Oracle上運(yùn)行存儲(chǔ)過(guò)程腳本,創(chuàng)建對(duì)應(yīng)的數(shù)據(jù)庫(kù)腳本匆帚,如下所示歪今。




測(cè)試Winform小程序,會(huì)得到成功的標(biāo)志替废,標(biāo)識(shí)所有的斷言全部通過(guò)椎镣。


6冷守、框架基類(lèi)的演化提煉

本來(lái)寫(xiě)到上面小節(jié)译断,應(yīng)該就可以告一段落了,因?yàn)楣δ芤惨呀?jīng)完成了堪唐,而且還是支持了兩種不同的數(shù)據(jù)庫(kù)荤堪,說(shuō)明我們的實(shí)現(xiàn)和原先的想法都是正確的拥知。
但是低剔,我從來(lái)不喜歡臃腫的代碼,我們留心回頭看看前面的代碼肮塞,兩種不同數(shù)據(jù)庫(kù)的實(shí)現(xiàn)很多是相似的襟齿,即使對(duì)于同一個(gè)數(shù)據(jù)庫(kù)(如SQLServer)的存儲(chǔ)過(guò)程接口實(shí)現(xiàn),他們還是有很多優(yōu)化的地方枕赵,代碼依舊不夠精簡(jiǎn)和優(yōu)化猜欺,本小節(jié)就是專(zhuān)門(mén)針對(duì)這些進(jìn)行提煉和優(yōu)化的。
前面的框架介紹文章拷窜,我們可以了解到恋谭,數(shù)據(jù)訪問(wèn)接口實(shí)現(xiàn)層和接口定義層一樣均抽,都有一個(gè)基類(lèi),如基于SqlServer實(shí)現(xiàn)的基類(lèi)為BaseDALSQL殉簸,這個(gè)基于SqlServer的數(shù)據(jù)訪問(wèn)基類(lèi),它也是繼承自一個(gè)超級(jí)基類(lèi)(大多數(shù)的實(shí)現(xiàn)在這里)AbstractBaseDAL排拷。他們之間的繼承關(guān)系如下所示顿乒,最終我們把提煉好的內(nèi)容雄卷,放到這個(gè)AbstractBaseDAL就可以了鳄炉,這樣各個(gè)子類(lèi)都可以進(jìn)行調(diào)用,實(shí)現(xiàn)存儲(chǔ)過(guò)程的處理啼止。



對(duì)于存儲(chǔ)過(guò)程的實(shí)現(xiàn)噪生,我們分析一下各個(gè)接口瞧甩,可以看到务冕,輸入?yún)?shù)是可選的佳谦,因?yàn)橛行┙涌诓恍枰敵鰠?shù);輸出參數(shù)也是可選的,有些接口也不需要輸出參數(shù)诵叁,返回的記錄類(lèi)型主要有bool類(lèi)型,實(shí)體類(lèi)型,實(shí)體集合類(lèi)型灾挨,DataTable類(lèi)型這幾種食磕,當(dāng)然雖然有年齡接口的整形,但是這個(gè)是通過(guò)輸出參數(shù)來(lái)獲得的。
我們于是可以定義一個(gè)類(lèi)似這樣的通用接口參數(shù)集合,用來(lái)處理需要返回是否成功獲取帶有輸出參數(shù)的,事務(wù)對(duì)象的接口,如下所示吨岭。

/// <summary>
/// 執(zhí)行存儲(chǔ)過(guò)程蛋逾,如果影響記錄數(shù)集晚,返回True,否則為False区匣,修改并輸出外部參數(shù)outParameters(如果有)偷拔。
/// </summary>
/// <param name="storeProcName">存儲(chǔ)過(guò)程名稱(chēng)</param>
/// <param name="inParameters">輸入?yún)?shù),可為空</param>
/// <param name="outParameters">輸出參數(shù)亏钩,可為空</param>
/// <param name="trans">事務(wù)對(duì)象莲绰,可為空</param>
/// <returns>如果影響記錄數(shù),返回True姑丑,否則為False</returns>
public bool StorePorcExecute(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)

它的實(shí)現(xiàn)基本上就是分為了幾部分蛤签,第一部分是傳入?yún)?shù)值(包括輸入?yún)?shù)、輸出參數(shù)的值)彻坛,第二部是執(zhí)行存儲(chǔ)過(guò)程顷啼,三部分是獲得輸出參數(shù)并修改值即可。

具體的實(shí)現(xiàn)代碼如下所示昌屉。

/// <summary>
/// 執(zhí)行存儲(chǔ)過(guò)程钙蒙,如果影響記錄數(shù)们镜,返回True勃救,否則為False颜骤,修改并輸出外部參數(shù)outParameters(如果有)宾濒。
/// </summary>
/// <param name="storeProcName">存儲(chǔ)過(guò)程名稱(chēng)</param>
/// <param name="inParameters">輸入?yún)?shù)诫龙,可為空</param>
/// <param name="outParameters">輸出參數(shù)杨帽,可為空</param>
/// <param name="trans">事務(wù)對(duì)象窟哺,可為空</param>
/// <returns>如果影響記錄數(shù)涛菠,返回True屹篓,否則為False</returns>
public bool StorePorcExecute(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(storeProcName);
    //參數(shù)傳入
    SetStoreParameters(db, command, inParameters, outParameters);

    //獲取執(zhí)行結(jié)果
    bool result = false;
    if (trans != null)
    {
        result = db.ExecuteNonQuery(command, trans) > 0;
    }
    else
    {
        result = db.ExecuteNonQuery(command) > 0;
    }

    //獲取輸出參數(shù)的值
    EditOutParameters(db, command, outParameters);

    return result;
}

上面兩部分紅色哪里疙渣,因?yàn)樗麄冊(cè)诤芏嗥渌瘮?shù)里面也通用,所以我就抽離作為一個(gè)私有函數(shù)了堆巧,就是傳入?yún)?shù)妄荔,和傳出結(jié)果的兩部分泼菌。

由于輸入輸出參數(shù)都是可選的,因?yàn)槲覀儾淮_定它是否存在值啦租,所以我們分別對(duì)它進(jìn)行了一定的處理哗伯,具體兩個(gè)函數(shù)的代碼如下所示。

/// <summary>
/// 傳入輸入?yún)?shù)和輸出參數(shù)到Database和DbCommand對(duì)象篷角。
/// </summary>
/// <param name="db">Database對(duì)象</param>
/// <param name="command">DbCommand對(duì)象</param>
/// <param name="inParameters">輸入?yún)?shù)的哈希表</param>
/// <param name="outParameters">輸出參數(shù)的哈希表</param>
private void SetStoreParameters(Database db, DbCommand command, Hashtable inParameters = null, Hashtable outParameters = null)
{
    #region 參數(shù)傳入
    //傳入輸入?yún)?shù)
    if (inParameters != null)
    {
        foreach (string param in inParameters.Keys)
        {
            object value = inParameters[param];
            db.AddInParameter(command, param, TypeToDbType(value.GetType()), value);
        }
    }

    //傳入輸出參數(shù)
    if (outParameters != null)
    {
        foreach (string param in outParameters.Keys)
        {
            object value = outParameters[param];
            db.AddOutParameter(command, param, TypeToDbType(value.GetType()), 0);//size統(tǒng)一設(shè)置為0
        }
    }
    #endregion
}
/// <summary>
/// 執(zhí)行存儲(chǔ)過(guò)程后焊刹,獲取需要輸出的參數(shù)值,修改存儲(chǔ)在哈希表里
/// </summary>
/// <param name="db">Database對(duì)象</param>
/// <param name="command">DbCommand對(duì)象</param>
/// <param name="outParameters">輸出參數(shù)的哈希表</param>
private void EditOutParameters(Database db, DbCommand command, Hashtable outParameters = null)
{
    #region 獲取輸出參數(shù)的值
    if (outParameters != null)
    {
        ArrayList keys = new ArrayList(outParameters.Keys);//使用臨時(shí)集合對(duì)象恳蹲,避免迭代錯(cuò)誤
        foreach (string param in keys)
        {
            object retValue = db.GetParameterValue(command, param);

            object value = outParameters[param];
            outParameters[param] = Convert.ChangeType(retValue, value.GetType());
        }
    }
    #endregion
}

這樣我們就完成了一個(gè)普通存儲(chǔ)過(guò)程該接口的通用處理了虐块,但是我們知道,還有返回列表對(duì)象嘉蕾,列表集合非凌,DataTable對(duì)象的幾種不同方式,我們也應(yīng)該要對(duì)他們進(jìn)行一定的封裝處理荆针,已達(dá)到在子類(lèi)能夠很好使用的目的。

下面我把整個(gè)對(duì)這幾部分封裝的代碼進(jìn)行公布颁糟,它們的封裝的代碼如下所示(記得是放在超級(jí)抽象類(lèi)上AbstractBaseDAL即可航背。

#region 存儲(chǔ)過(guò)程執(zhí)行通用方法

/// <summary>
/// 執(zhí)行存儲(chǔ)過(guò)程,如果影響記錄數(shù)棱貌,返回True玖媚,否則為False,修改并輸出外部參數(shù)outParameters(如果有)婚脱。
/// </summary>
/// <param name="storeProcName">存儲(chǔ)過(guò)程名稱(chēng)</param>
/// <param name="inParameters">輸入?yún)?shù)今魔,可為空</param>
/// <param name="outParameters">輸出參數(shù),可為空</param>
/// <param name="trans">事務(wù)對(duì)象障贸,可為空</param>
/// <returns>如果影響記錄數(shù)错森,返回True,否則為False</returns>
public bool StorePorcExecute(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(storeProcName);
    //參數(shù)傳入
    SetStoreParameters(db, command, inParameters, outParameters);

    //獲取執(zhí)行結(jié)果
    bool result = false;
    if (trans != null)
    {
        result = db.ExecuteNonQuery(command, trans) > 0;
    }
    else
    {
        result = db.ExecuteNonQuery(command) > 0;
    }

    //獲取輸出參數(shù)的值
    EditOutParameters(db, command, outParameters);

    return result;
}

/// <summary>
/// 執(zhí)行存儲(chǔ)過(guò)程篮洁,返回實(shí)體列表集合涩维,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲(chǔ)過(guò)程名稱(chēng)</param>
/// <param name="inParameters">輸入?yún)?shù)袁波,可為空</param>
/// <param name="outParameters">輸出參數(shù)瓦阐,可為空</param>
/// <param name="trans">事務(wù)對(duì)象,可為空</param>
/// <returns>返回實(shí)體列表集合</returns>
public List<T> StorePorcToList(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(storeProcName);
    //參數(shù)傳入
    SetStoreParameters(db, command, inParameters, outParameters);

    #region 獲取執(zhí)行結(jié)果

    List<T> result = new List<T>();
    T entity = null;
    if (trans != null)
    {
        using (IDataReader dr = db.ExecuteReader(command, trans))
        {
            while (dr.Read())
            {
                entity = DataReaderToEntity(dr);
                result.Add(entity);
            }
        }
    }
    else
    {
        using (IDataReader dr = db.ExecuteReader(command))
        {
            while (dr.Read())
            {
                entity = DataReaderToEntity(dr);
                result.Add(entity);
            }
        }
    }
    #endregion

    //獲取輸出參數(shù)的值
    EditOutParameters(db, command, outParameters);

    return result;
}

/// <summary>
/// 執(zhí)行存儲(chǔ)過(guò)程篷牌,返回DataTable集合睡蟋,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲(chǔ)過(guò)程名稱(chēng)</param>
/// <param name="inParameters">輸入?yún)?shù)枷颊,可為空</param>
/// <param name="outParameters">輸出參數(shù)戳杀,可為空</param>
/// <param name="trans">事務(wù)對(duì)象该面,可為空</param>
/// <returns>返回DataTable集合</returns>
public DataTable StorePorcToDataTable(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(storeProcName);
    //參數(shù)傳入
    SetStoreParameters(db, command, inParameters, outParameters);

    #region 獲取執(zhí)行結(jié)果

    DataTable result = null;
    if (trans != null)
    {
        result = db.ExecuteDataSet(command, trans).Tables[0];
    }
    else
    {
        result = db.ExecuteDataSet(command).Tables[0];
    }

    if (result != null)
    {
        result.TableName = "tableName";//增加一個(gè)表名稱(chēng),防止WCF方式因?yàn)門(mén)ableName為空出錯(cuò)
    }
    #endregion

    //獲取輸出參數(shù)的值
    EditOutParameters(db, command, outParameters);

    return result;
}

/// <summary>
/// 執(zhí)行存儲(chǔ)過(guò)程豺瘤,返回實(shí)體對(duì)象吆倦,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲(chǔ)過(guò)程名稱(chēng)</param>
/// <param name="inParameters">輸入?yún)?shù)坐求,可為空</param>
/// <param name="outParameters">輸出參數(shù)蚕泽,可為空</param>
/// <param name="trans">事務(wù)對(duì)象,可為空</param>
/// <returns>返回實(shí)體對(duì)象</returns>
public T StorePorcToEntity(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
    Database db = CreateDatabase();
    DbCommand command = db.GetStoredProcCommand(storeProcName);

    //參數(shù)傳入
    SetStoreParameters(db, command, inParameters, outParameters);

    #region 獲取執(zhí)行結(jié)果

    T result = null;
    if (trans != null)
    {
        using (IDataReader dr = db.ExecuteReader(command, trans))
        {
            if (dr.Read())
            {
                result = DataReaderToEntity(dr);
            }
        }
    }
    else
    {
        using (IDataReader dr = db.ExecuteReader(command))
        {
            if (dr.Read())
            {
                result = DataReaderToEntity(dr);
            }
        }
    }
    #endregion

    //獲取輸出參數(shù)的值
    EditOutParameters(db, command, outParameters);

    return result;
}

/// <summary>
/// 傳入輸入?yún)?shù)和輸出參數(shù)到Database和DbCommand對(duì)象桥嗤。
/// </summary>
/// <param name="db">Database對(duì)象</param>
/// <param name="command">DbCommand對(duì)象</param>
/// <param name="inParameters">輸入?yún)?shù)的哈希表</param>
/// <param name="outParameters">輸出參數(shù)的哈希表</param>
private void SetStoreParameters(Database db, DbCommand command, Hashtable inParameters = null, Hashtable outParameters = null)
{
    #region 參數(shù)傳入
    //傳入輸入?yún)?shù)
    if (inParameters != null)
    {
        foreach (string param in inParameters.Keys)
        {
            object value = inParameters[param];
            db.AddInParameter(command, param, TypeToDbType(value.GetType()), value);
        }
    }

    //傳入輸出參數(shù)
    if (outParameters != null)
    {
        foreach (string param in outParameters.Keys)
        {
            object value = outParameters[param];
            db.AddOutParameter(command, param, TypeToDbType(value.GetType()), 0);//size統(tǒng)一設(shè)置為0
        }
    }
    #endregion
}
/// <summary>
/// 執(zhí)行存儲(chǔ)過(guò)程后须妻,獲取需要輸出的參數(shù)值,修改存儲(chǔ)在哈希表里
/// </summary>
/// <param name="db">Database對(duì)象</param>
/// <param name="command">DbCommand對(duì)象</param>
/// <param name="outParameters">輸出參數(shù)的哈希表</param>
private void EditOutParameters(Database db, DbCommand command, Hashtable outParameters = null)
{
    #region 獲取輸出參數(shù)的值
    if (outParameters != null)
    {
        ArrayList keys = new ArrayList(outParameters.Keys);//使用臨時(shí)集合對(duì)象泛领,避免迭代錯(cuò)誤
        foreach (string param in keys)
        {
            object retValue = db.GetParameterValue(command, param);

            object value = outParameters[param];
            outParameters[param] = Convert.ChangeType(retValue, value.GetType());
        }
    }
    #endregion
}
#endregion

封裝好這些超級(jí)基類(lèi)后荒吏,我們?cè)跀?shù)據(jù)訪問(wèn)層里面,就可以很好地簡(jiǎn)化對(duì)存儲(chǔ)過(guò)程的調(diào)用了渊鞋,而且他們的做法都很類(lèi)似绰更,我們可以對(duì)比一下,它們調(diào)用存儲(chǔ)過(guò)程的實(shí)現(xiàn)真正簡(jiǎn)化了很多锡宋。

例如對(duì)于SqlServer數(shù)據(jù)訪問(wèn)層儡湾,使用超級(jí)基類(lèi)的接口,我們簡(jiǎn)化代碼如下所示执俩。

public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("ID", info.ID);
    inParameters.Add("Name", info.Name);
    inParameters.Add("Age", info.Age);

    return StorePorcExecute("T_Customer_Insert", inParameters, null, trans);
}

public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("ID", info.ID);
    inParameters.Add("Name", info.Name);
    inParameters.Add("Age", info.Age);

    return StorePorcExecute("T_Customer_UpdateByID", inParameters, null, trans);
}

public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
    return StorePorcToList("T_Customer_SelectAll", null, null, trans);
}
public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null)
{
    return StorePorcToDataTable("T_Customer_SelectAll", null, null, trans);
}
public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("ID", ID);

    return StorePorcToEntity("T_Customer_SelectByID", inParameters, null, trans);
}
public bool StorePorc_ExistByID(string ID, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("ID", ID);

    Hashtable outParameters = new Hashtable();
    outParameters.Add("Exist", 0);

    StorePorcExecute("T_Customer_ExistByID", inParameters, outParameters, trans);
    int exist = (int)outParameters["Exist"];
    return exist > 0;
}

public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("ID", ID);

    return StorePorcExecute("T_Customer_DeleteByID", inParameters, null, trans);
}

public int StorePorc_GetMaxAge()
{
    Hashtable outParameters = new Hashtable();
    outParameters.Add("MaxAge", 0);

    StorePorcExecute("T_Customer_MaxAge", null, outParameters, null);
    int MaxAge = (int)outParameters["MaxAge"];
    return MaxAge;
}

對(duì)于Oracle數(shù)據(jù)訪問(wèn)層的實(shí)現(xiàn)來(lái)說(shuō)徐钠,它的接口實(shí)現(xiàn)一樣簡(jiǎn)單,只是參數(shù)命名有所不同而已役首。

public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("p_ID", info.ID);
    inParameters.Add("p_Name", info.Name);
    inParameters.Add("p_Age", info.Age);

    return StorePorcExecute("T_Customer_Insert", inParameters, null, trans);
}
public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("p_ID", info.ID);
    inParameters.Add("p_Name", info.Name);
    inParameters.Add("p_Age", info.Age);

    return StorePorcExecute("T_Customer_UpdateByID", inParameters, null, trans);
}
public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
    return StorePorcToList("T_Customer_SelectAll", null, null, trans);
}
public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null)
{
    return StorePorcToDataTable("T_Customer_SelectAll", null, null, trans);
}
public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("p_ID", ID);

    return StorePorcToEntity("T_Customer_SelectByID", inParameters, null, trans);
}
public bool StorePorc_ExistByID(string ID, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("p_ID", ID);

    Hashtable outParameters = new Hashtable();
    outParameters.Add("p_Exist", 0);

    StorePorcExecute("T_Customer_ExistByID", inParameters, outParameters, trans);
    int exist = (int)outParameters["p_Exist"];
    return exist > 0;
}

public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null)
{
    Hashtable inParameters = new Hashtable();
    inParameters.Add("p_ID", ID);

    return StorePorcExecute("T_Customer_DeleteByID", inParameters, null, trans);
}

public int StorePorc_GetMaxAge()
{
    Hashtable outParameters = new Hashtable();
    outParameters.Add("p_MaxAge", 0);

    StorePorcExecute("T_Customer_MaxAge", null, outParameters, null);
    int MaxAge = (int)outParameters["p_MaxAge"];
    return MaxAge;
}

以上就是我針對(duì)《Winform開(kāi)發(fā)框架之存儲(chǔ)過(guò)程的支持--存儲(chǔ)過(guò)程的實(shí)現(xiàn)和演化提煉》這個(gè)主題進(jìn)行的介紹和分析尝丐,希望對(duì)大家有所幫助,也希望結(jié)合我的框架衡奥,迅速開(kāi)發(fā)各種不同的項(xiàng)目爹袁。

文章內(nèi)容有點(diǎn)長(zhǎng),感謝您的耐心閱讀和支持杰赛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呢簸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子乏屯,更是在濱河造成了極大的恐慌根时,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辰晕,死亡現(xiàn)場(chǎng)離奇詭異蛤迎,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)含友,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)替裆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)校辩,“玉大人,你說(shuō)我怎么就攤上這事辆童∫酥洌” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵把鉴,是天一觀的道長(zhǎng)故黑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)庭砍,這世上最難降的妖魔是什么场晶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮怠缸,結(jié)果婚禮上诗轻,老公的妹妹穿的比我還像新娘。我一直安慰自己揭北,他們只是感情好扳炬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著搔体,像睡著了一般鞠柄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嫉柴,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音奉呛,去河邊找鬼计螺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瞧壮,可吹牛的內(nèi)容都是我干的登馒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼咆槽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼陈轿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起秦忿,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤麦射,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后灯谣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體潜秋,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年胎许,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了峻呛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罗售。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钩述,靈堂內(nèi)的尸體忽然破棺而出寨躁,到底是詐尸還是另有隱情,我是刑警寧澤牙勘,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布职恳,位于F島的核電站,受9級(jí)特大地震影響谜悟,放射性物質(zhì)發(fā)生泄漏话肖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一葡幸、第九天 我趴在偏房一處隱蔽的房頂上張望最筒。 院中可真熱鬧,春花似錦蔚叨、人聲如沸床蜘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)邢锯。三九已至,卻和暖如春搀别,著一層夾襖步出監(jiān)牢的瞬間丹擎,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工歇父, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蒂培,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓榜苫,卻偏偏與公主長(zhǎng)得像护戳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子垂睬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容