文章標題很長,但主要是要記錄出現「OracleParameterCollection 只接受非 Null 的 OracleParameter 型別物件,不接受 OracleParameter 物件。」這個錯誤時的解決方式,這是出現在專案使用 Enterprise Library Data Access Application Block 存取 Oracale 資料庫的情況,同樣的存取方式如果是換成是對 MS SQL Server 做存取時就不會有問題,而對 Oracle 資料庫存取時就必須要使用另一種方式來解決。
錯誤畫面:
開發使用環境:
Visual Studio 2012, Oracle XE, ASP.NET MVC 4, 
.NET Framework 4.5, 
Enterprise Library 6 - Data Access Application.
對 MS SQL Server 取得資料的處理
先看看對 MS SQL Server 資料庫取得資料的程式,
CategoryRepository.cs - GetOne Method
/// <summary>/// Gets the one./// </summary>/// <param name="id">The id.</param>/// <returns></returns>public Category GetOne(int id)
{string sqlStatement = "select * from Categories where CategoryID = @CategoryID";
    try    {DataAccessor<Category> accessor =
            this.Db.CreateSqlStringAccessor<Category>(sqlStatement,
                new CategoryIDParameterMapper(),                new CategoryMapper());var data = accessor.Execute(new object[] { id }).FirstOrDefault();
        return data;}
    catch    {        throw;}
}
public class CategoryIDParameterMapper : IParameterMapper
{public void AssignParameters(DbCommand command, object[] parameterValues)
    {command.Parameters.Add(new SqlParameter("CategoryID", parameterValues[0]));
}
}
public class CategoryMapper : IRowMapper<Category>
{    public Category MapRow(IDataRecord reader)    {        Category item = new Category();item.CategoryID = int.Parse(reader["CategoryID"].ToString());
        item.CategoryName = reader["CategoryName"].ToString();        item.Description = reader["Description"].ToString();        return item;}
}
這邊我所使用了 EntLib DAAB 裡面的 DataAccessor, IRowMapper, IParameterMapper,能夠讓程式碼更為簡潔,而且可以重複使用 IRowMapper 與 IParameterMapper,當其他 Method 使用到相同 Parameter 結構或是資料對應輸出時,就可以不必重複輸入一樣的程式碼內容。
為了做個比對,以下是一樣使用 EntLib DAAB 但沒有使用 IParamMapper 與 IRowMapper 的程式碼內容,
/// <summary>/// Gets the categories./// </summary>/// <returns></returns>public List<Category> GetCategories(){    List<Category> categories = new List<Category>();string sqlStatement = "select * from Categories order by CategoryID";
    using (DbCommand comm = Db.GetSqlStringCommand(sqlStatement))using (IDataReader reader = this.Db.ExecuteReader(comm))
    {        while (reader.Read())        {            Category item = new Category();item.CategoryID = int.Parse(reader["CategoryID"].ToString());
            item.CategoryName = reader["CategoryName"].ToString();            item.Description = reader["Description"].ToString();categories.Add(item);
}
}
    return categories;}
/// <summary>/// Gets the one./// </summary>/// <param name="id">The id.</param>/// <returns></returns>public Category GetOne(int id)
{string sqlStatement = "select * from Categories where CategoryID = @CategoryID";
    Category item = new Category();    using (DbCommand comm = Db.GetSqlStringCommand(sqlStatement))    {comm.Parameters.Add(new SqlParameter("CategoryID", id));
using (IDataReader reader = this.Db.ExecuteReader(comm))
        {            if (reader.Read())            {item.CategoryID = int.Parse(reader["CategoryID"].ToString());
                item.CategoryName = reader["CategoryName"].ToString();                item.Description = reader["Description"].ToString();}
}
}
    return item;}
在上面的程式碼裡可以看到兩個 Method 裡都有做資料對應的處理,同樣的處理作法卻重複出現,如果是改用 IRowMapper, IParameterMapper, DataAccessor 的方式來處理就會簡潔許多,如下:
/// <summary>/// Gets the categories ./// </summary>/// <returns></returns>public List<Category> GetCategories(){string sqlStatement = "select * from Categories order by CategoryID";
    try    {DataAccessor<Category> accessor =
this.Db.CreateSqlStringAccessor<Category>(sqlStatement, new CategoryMapper());
var categories = accessor.Execute().ToList();
        return categories;}
    catch    {        throw;}
}
/// <summary>/// Gets the one./// </summary>/// <param name="id">The id.</param>/// <returns></returns>public Category GetOne(int id)
{string sqlStatement = "select * from Categories where CategoryID = @CategoryID";
    try    {DataAccessor<Category> accessor =
            this.Db.CreateSqlStringAccessor<Category>(sqlStatement,
                new CategoryIDParameterMapper(),                new CategoryMapper());var data = accessor.Execute(new object[] { id }).FirstOrDefault();
        return data;}
    catch    {        throw;}
}
而如果是不使用 EntLib DAAB 而是使用傳統的 ADO.NET 方式呢?
/// <summary>/// Gets the categories./// </summary>/// <returns></returns>public List<Category> GetCategories(){    List<Category> categories = new List<Category>();string sqlStatement = "select * from Categories order by CategoryID";
using (SqlConnection conn = new SqlConnection(this.ConnectionString))
using (SqlCommand command = new SqlCommand(sqlStatement, conn))
    {command.CommandType = CommandType.Text;
command.CommandTimeout = 180;
        if (conn.State != ConnectionState.Open) conn.Open();        using (SqlDataReader reader = command.ExecuteReader())        {            while (reader.Read())            {                Category item = new Category();item.CategoryID = int.Parse(reader["CategoryID"].ToString());
                item.CategoryName = reader["CategoryName"].ToString();                item.Description = reader["Description"].ToString();categories.Add(item);
}
}
}
    return categories;}
/// <summary>/// Gets the one./// </summary>/// <param name="id">The id.</param>/// <returns></returns>public Category GetOne(int id)
{string sqlStatement = "select * from Categories where CategoryID = @CategoryID";
    Category item = new Category();using (SqlConnection conn = new SqlConnection(this.ConnectionString))
using (SqlCommand comm = new SqlCommand(sqlStatement, conn))
    {comm.Parameters.Add(new SqlParameter("CategoryID", id));
        if (conn.State != ConnectionState.Open) conn.Open();        using (IDataReader reader = comm.ExecuteReader())        {            if (reader.Read())            {item.CategoryID = int.Parse(reader["CategoryID"].ToString());
                item.CategoryName = reader["CategoryName"].ToString();                item.Description = reader["Description"].ToString();}
}
}
    return item;}
對 Oracle 取得資料的處理
離題有點太遠了,趕緊拉回來……
上面所看到的是資料庫為 MS SQL Server 的情況,如果資料庫換做是使用 Oracle XE 的情況,一樣是使用 EntLib DAAB, DataAccessor, IRowMapper, IParameterMapper,程式碼如下:
CategoryRepository.cs - GetOne() Method
/// <summary>/// Gets the one./// </summary>/// <param name="id">The id.</param>/// <returns></returns>public Category GetOne(int id)
{string sqlStatement = "select * from \"Categories\" where \"CategoryID\" = :CategoryID";
    try    {DataAccessor<Category> accessor =
            this.Db.CreateSqlStringAccessor<Category>(sqlStatement,
                new CategoryIDParameterMapper(),                new CategoryMapper());var data = accessor.Execute(new object[] { id }).FirstOrDefault();
        return data;}
    catch    {        throw;}
}
CategoryIDParameterMapper, CategoryMapper
public class CategoryIDParameterMapper : IParameterMapper
{public void AssignParameters(DbCommand command, object[] parameterValues)
    {command.Parameters.Add(new OracleParameter("CategoryID", parameterValues[0]));
}
}
public class CategoryMapper : IRowMapper<Category>
{    public Category MapRow(IDataRecord reader)    {        Category item = new Category();item.CategoryID = int.Parse(reader["CategoryID"].ToString());
        item.CategoryName = reader["CategoryName"].ToString();        item.Description = reader["Description"].ToString();        return item;}
}
問題發生在 CategoryIDParameterMapper 裡面,如果將 OracleParamerer 加入到 DbCommand 的方式改使用一般的方式,如下:
public class CategoryIDParameterMapper : IParameterMapper
{public void AssignParameters(DbCommand command, object[] parameterValues)
    {OracleParameter param = new OracleParameter("CategoryID", OracleDbType.Long, 11);
param.Value = parameterValues[0];
command.Parameters.Add(param);
}
}
還是會出現同樣的錯誤,
DbCommand.CreateParameter 方法
解決的方式就是使用 DbCommand.CreateParameter 方法,透過 DbCommand 建立 DbParameter 物件的新執行個體,修改後的 CategoryIDParameterMapper 如下:
public class CategoryIDParameterMapper : IParameterMapper
{public void AssignParameters(DbCommand command, object[] parameterValues)
    {var param = command.CreateParameter();
        param.ParameterName = "CategoryID";param.Value = parameterValues[0];
command.Parameters.Add(param);
}
}
修改後就不會出現錯誤,執行結果如下:
參考資料:
MSDN - DbCommand.CreateParameter 方法
簡述 Oracle + Enterprise Library 5.0 Data Access Application Block 的操作
Enterprise Library DAAB + Oracle:Provider 使用 ODP.NET
以上
沒有留言:
張貼留言