【问题标题】:Issue creating a generic Data Wrapper创建通用数据包装器的问题
【发布时间】:2014-02-27 11:30:39
【问题描述】:

我在创建(主要是)通用数据访问包装器时遇到问题

代码

/// <summary>
/// Get the results of a stronly-typed IList Object
/// </summary>
/// <typeparam name="T">Strongly-Typed class of objects that should be returned</typeparam>
/// <param name="_DBType">The type of database to use</param>
/// <param name="_Qry">The query to run</param>
/// <param name="_QryType">The Query Type to run</param>
/// <param name="_ParamNames">The Parameters' names to pass to the query, if any</param>
/// <param name="_ParamVals">The Parameters' values to pass to the query, if any</param>
/// <param name="_ParamDTs">The Parameters' data types to pass to the query, if any</param>
/// <param name="_ShouldCache">Should we cache the response</param>
/// <param name="_CacheID">Cache item name</param>
/// <returns>Strongly Typed ilist of objects</returns>
public static IList<T> GetResults<T>(Enumerators.DatabaseTypes _DBType, 
                                    string _Qry, 
                                    CommandType _QryType,
                                    string[] _ParamNames = null,
                                    object[] _ParamVals = null,
                                    Enumerators.DataTypes[] _ParamDTs = null,
                                    bool _ShouldCache = false,
                                    string _CacheID = "") where T : new()
{
    // Create a reference to a potential already cached IList
    IList<T> _CachedItem = _Cache.Get<IList<T>>(_CacheID);
    // If we're already cached, there's no need to fire up the data access objects, so return the cached item instead
    if (_CachedItem != null && _ShouldCache)
    {
        return _CachedItem;
    }
    else
    {
        // Fire up our data access object
        switch (_DBType)
        { 
            case Enumerators.DatabaseTypes.SqlServer:
                SqlServer.Access db = new SqlServer.Access();
                break;
            case Enumerators.DatabaseTypes.SqlCE:
                SqlCE.Access db = new SqlCE.Access();
                break;
            case Enumerators.DatabaseTypes.MySql:
                MySql.Access db = new MySql.Access();
                break;
            case Enumerators.DatabaseTypes.OLE:
                Ole.Access db = new Ole.Access();
                break;
            case Enumerators.DatabaseTypes.ODBC:
                Odbc.Access db = new Odbc.Access();
                break;
            default:
                db = null;
        }
        using (db)
        {
            try
            {
                // create a new ilist reference of our strongly typed class
                IList<T> _Query = default(IList<T>);
                // set the query type
                db.QueryType = _QryType;
                // set the query text
                db.Query = _Qry;
                // make sure we've got some parameters, if we do the set them to our db access object
                if (_ParamNames != null)
                {
                    // set the parameter names
                    db.ParameterNames = _ParamNames;
                    // set the parameter values
                    db.ParameterValues = _ParamVals;
                    // set the parameter data types
                    db.ParameterDataTypes = _ParamDTs;
                }
                // start using our db access :)  Fire off the GetResults method and return back a SqlDataReader to work on
                using (DbDataReader r = db.GetResults())
                {
                    // make sure the data reader actually exists and contains some results
                    if (r != null)
                    {
                        // map the data reader to our strongly type(s)
                        _Query = Map<T>(r);
                    }
                    r.Close();
                }
                // check if we should cache the results
                if (_ShouldCache)
                {
                    // if so, set the query object to the cache
                    _Cache.Set<IList<T>>(_Query, _CacheID);
                }
                // return our strongly typed list
                return _Query;
            }
            catch (DbException dEx)
            {
                // Catch an exception if any, an write it out to our logging mechanism, in addition to adding it our returnable message property
                _Msg += "Wrapper.GetResults Exception: " + dEx.Message + db.Message;
                ErrorReporting.WriteEm.WriteItem(dEx, "o7th.Class.Library.Data.Wrapper.GetResults", _Msg);
                // make sure this method returns a default List
                return default(IList<T>);
            }
            catch (Exception ex)
            {
                // Catch an exception if any, an write it out to our logging mechanism, in addition to adding it our returnable message property
                _Msg += "Wrapper.GetResults Exception: " + ex.Message + db.Message;
                ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.Wrapper.GetResults", _Msg);
                // make sure this method returns a default List
                return default(IList<T>);
            }
        }
    }
}

访问类 - SqlServer

using System;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace o7th.Class.Library.Data.SqlServer
{
internal class Access : IDisposable
{

#region "Properties"

    // Set the type of query we are running
    private CommandType _QT;
    internal CommandType QueryType { set { _QT = value; } }

    // Set the actual query text to run
    private string _Qry;
    internal string Query { set { _Qry = value; } }

    // Set the parameter names if there are any
    private string[] _PNs;
    internal string[] ParameterNames { set { _PNs = value; } }

    // Set the parameter values if there are any
    private object[] _PVs;
    internal object[] ParameterValues { set { _PVs = value; } }

    // Set the actual Sql Data Types if there are any
    private DataTypes[] _DTs;
    internal DataTypes[] ParameterDataTypes { set { _DTs = value; } }

    // Check to see if there are any parameters passed
    private bool AreParams() {
        // Check to see if the values and names are null first
        if (_PVs != null && _PNs != null) {
            try {
                Type _t_pv = _PVs.GetType();
                Type _t_pn = _PNs.GetType();
                if (_t_pv.IsArray && _t_pn.IsArray) {
                    return (_PVs.Length > 0 && _PNs.Length > 0) ? true : false;
                } else {
                    return false;
                }
            } catch {
                // yes I meant to do this, we really don't need to get the exception here
                return false;
            }
        } else {
            return false;
        }
    }

    // Get a return message if any
    private string _Msg;
    internal string Message { get { return _Msg; } }

    // Set the official Sql Reader object
    private SqlDataReader _Rdr;
    // Set the official Sql Connection object
    private SqlConnection _Conn;
    // Set the official Sql Command object
    private SqlCommand _Cmd;
    // Hack for seeing if we're disposed already
    private bool disposedValue;

#endregion

    // Constructor
    internal Access() {
        Invoke();
    }

    // Official Constructor.  We can thread these 2 becuase they are not being used yet, and it makes it slightly more efficient
    internal void Invoke() {
        try { 
            Parallel.Invoke(() => {
                _Conn = new SqlConnection(AssemblyProperties.GetConnectionString());
            }, () =>
            {
                _Cmd = new SqlCommand();
            });
        }
        catch (SqlException dEx)
        {
            // Catch an exception if any, an write it out to our logging mechanism, in addition to adding it our returnable message property
            _Msg = "Access.Invoke Exception: " + dEx.Message;
            ErrorReporting.WriteEm.WriteItem(dEx, "o7th.Class.Library.Data.SqlServer.Access.Invoke", _Msg);
        }
        catch (Exception ex)
        {
            _Msg = "Access.Invoke Exception: " + ex.Message;
            ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.Invoke", _Msg);
        }
    }

    /// <summary>
    /// Return a SqlDataReader based on the properties passed to this class
    /// </summary>
    /// <returns></returns>
    internal SqlDataReader GetResults()
    {
        try {
            // check for parameters
            if (AreParams()) {
                PrepareParams(_Cmd);
            }
            // set our connection
            _Cmd.Connection = _Conn;
            // set the type of query to run
            _Cmd.CommandType = _QT;
            // set the actual query to run
            _Cmd.CommandText = _Qry;
            // open the connection
            _Cmd.Connection.Open();
            // prepare the command with any parameters that may have gotten added
            _Cmd.Prepare();
            // Execute the SqlDataReader, and set the connection to close once returned
            _Rdr = _Cmd.ExecuteReader(CommandBehavior.CloseConnection);
            // clear out any parameters
            _Cmd.Parameters.Clear();
            // return our reader object
            return (!_Rdr.HasRows) ? null : _Rdr;
        }
        catch (SqlException SqlEx)
        {
            _Msg += "Acccess.GetResults SqlException: " + SqlEx.Message;
            ErrorReporting.WriteEm.WriteItem(SqlEx, "o7th.Class.Library.Data.SqlServer.Access.GetResults", _Msg);
            return null;
        }
        catch (Exception ex) {
            _Msg += "Acccess.GetResults Exception: " + ex.Message;
            ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.GetResults", _Msg);
            return null;
        }
    }

    /// <summary>
    /// Execute a non-return query, and return the success
    /// </summary>
    /// <returns></returns>
    internal bool Execute() {
        try {
            // check for parameters
            if (AreParams()) {
                PrepareParams(_Cmd);
            }
            // set our connection
            _Cmd.Connection = _Conn;
            // set the type of query to run
            _Cmd.CommandType = _QT;
            // set the actual query to run
            _Cmd.CommandText = _Qry;
            // open the connection
            _Cmd.Connection.Open();
            // prepare the command with any parameters that may have gotten added
            _Cmd.Prepare();
            // execute the non-returnable query against the database
            _Cmd.ExecuteNonQuery();
            // clear out any parameters
            _Cmd.Parameters.Clear();
            // executed successfully (otherwise would have thrown an exception)
            return true;
        }
        catch (SqlException SqlEx)
        {
            _Msg += "Access.Execute SqlException: " + SqlEx.Message;
            ErrorReporting.WriteEm.WriteItem(SqlEx, "o7th.Class.Library.Data.SqlServer.Access.Execute", _Msg);
            return false;
        }
        catch (Exception ex) {
            _Msg += "Access.Execute Exception: " + ex.Message;
            ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.Execute", _Msg);
            return false;
        }
    }

    /// <summary>
    /// Execute a query with a return value.  Used in Selecting the ID of the last inserted record.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="_DefVal"></param>
    /// <returns></returns>
    internal T ExecuteWithReturn<T>(T _DefVal) {
        try {
            T _Ret;
            // check for parameters
            if (AreParams()) {
                PrepareParams(_Cmd);
            }
            // set our connection
            _Cmd.Connection = _Conn;
            // set the type of query to run
            _Cmd.CommandType = _QT;
            // set the actual query to run
            _Cmd.CommandText = _Qry;
            // open the connection
            _Cmd.Connection.Open();
            // prepare the command with any parameters that may have gotten added
            _Cmd.Prepare();
            T _T = (T)_Cmd.ExecuteScalar();
            _Ret = (_T is DBNull) ? default(T) : _T;
            // clear out _T
            _T = default(T);
            // clear out any parameters
            _Cmd.Parameters.Clear();
            // return the single return value from the query run
            return _Ret;
        }
        catch (SqlException SqlEx)
        {
            _Msg += "Access.ExecuteWithReturn SqlException: " + SqlEx.Message;
            ErrorReporting.WriteEm.WriteItem(SqlEx, "o7th.Class.Library.Data.SqlServer.Access.ExecuteWithReturn", _Msg);
            return default(T);
        } catch (Exception ex) {
            _Msg += "Access.ExecuteWithReturn Exception: " + ex.Message;
            ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.ExecuteWithReturn", _Msg);
            return default(T);
        }
    }

    /// <summary>
    /// Prepare our parameters, adding them and forcing a valid data length
    /// </summary>
    /// <param name="objCmd"></param>
    protected void PrepareParams(SqlCommand objCmd) {
        try  {
            // set our initial Data Size
            int _DataSize = 0;
            // get the number of Parameter Values passed in
            int _PCt = _PVs.GetUpperBound(0);
            // begin array check
            Type _t_dt = _DTs.GetType();
            // start looping over our parameters
            for (int i = 0; i <= _PCt; ++i) {
                // make sure that the data types are actually an array
                if (_t_dt.IsArray) {
                    // select which datatype, and force the official size
                    switch ((int)_DTs[i]) {
                        case 0:
                        case 33:
                        case 6:
                        case 9:
                        case 13:
                        case 19:
                            _DataSize = 8;
                            break;
                        case 1:
                        case 3:
                        case 7:
                        case 10:
                        case 12:
                        case 21:
                        case 22:
                        case 23:
                        case 25:
                            _DataSize = _PVs[i].ToString().Length;
                            break;
                        case 2:
                        case 20:
                            _DataSize = 1;
                            break;
                        case 5:
                            _DataSize = 17;
                            break;
                        case 8:
                        case 17:
                        case 15:
                            _DataSize = 4;
                            break;
                        case 14:
                            _DataSize = 16;
                            break;
                        case 31:
                            _DataSize = 3;
                            break;
                        case 32:
                            _DataSize = 5;
                            break;
                        case 16:
                            _DataSize = 2;
                            break;
                    }
                    // add our parameter to the command object
                    objCmd.Parameters.Add(_PNs[i], (SqlDbType)_DTs[i], _DataSize).Value = _PVs[i]; 
                } else {
                    // if the datatypes were not set, try to add them generically
                    objCmd.Parameters.AddWithValue(_PNs[i], _PVs[i]);
                }
            }
            // clean up
            _PNs = null;_PVs = null;_DTs = null;
        } catch (Exception ex) {
            _Msg += "Access.PrepareParams Exception: " + ex.Message;
            ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.PrepareParams", _Msg);
        }
    }

#region "Dispose Support"

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue && disposing) {
            try
            {
                _Qry = string.Empty;
                _Rdr.Close();
                _Rdr.Dispose();
                _Cmd.Connection.Close();
                _Cmd.Dispose();
                if (_Conn.State == ConnectionState.Open)
                {
                    SqlConnection.ClearAllPools();
                    _Conn.Close();
                    _Conn.Dispose();
                }
                _Msg = null;
            }
            catch(Exception ex) {
                ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.SqlServer.Access.Dispose", "");
            }
        }
        disposedValue = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

#endregion

}
}

我遇到的问题是,因为我需要switch 语句来选择应该使用哪个数据库Access 方法,所以它到处抛出The name 'db' does not exist in the current context 错误。

我还尝试在 switch 语句之前 var db; 出现 Implicitly-typed local variables 错误。

在实际使用它之前,我不知道将使用哪种类型的数据库,那么我如何确保它有效?

        IDbAccess db;
        switch (_DBType)
        { 
            case Enumerators.DatabaseTypes.SqlServer:
                SqlServer.Access db = new SqlServer.Access();
                break;
            case Enumerators.DatabaseTypes.SqlCE:
                SqlCE.Access db = new SqlCE.Access();
                break;
            case Enumerators.DatabaseTypes.MySql:
                MySql.Access db = new MySql.Access();
                break;
            case Enumerators.DatabaseTypes.OLE:
                Ole.Access db = new Ole.Access();
                break;
            case Enumerators.DatabaseTypes.ODBC:
                Odbc.Access db = new Odbc.Access();
                break;
            default:
                db = null;
        }

正在产生Cannot implicitly convert type 'SqlCE.Access to SqlServer.Access' 错误,以及A local variable 'db' is already defined in this scopeA local variable named 'db' cannot be declared in this scope because it would give a different meaning to 'db' which is already used in a parent of current scope to denote something else

【问题讨论】:

  • SqlServer.Access、SqlCE.Access、MySql.Access、Ole.Access 和 Odbc.Access 继承/实现什么类/接口?
  • 除了 IDisposable 没有别的。每种数据库类型的 Access 类对每种类型都有不同的作用
  • 我将在问题中发布一个 Access 类
  • @o7thWebDesign 那么您应该真正编辑它们,以便它们确实实现一个具有您在此方法中使用的所有功能的接口,因为它们是您的类型,而您可以编辑它们。
  • 您需要定义一个接口(例如IDbAccess),其中包含它们之间共有的方法/属性,然后让这些类实现该接口。一旦到位,您可以将db 定义为IDbAccess db,所有其他事情都应该到位。

标签: c# .net generics .net-4.0 data-access-layer


【解决方案1】:

所有这些不同的数据库提供者都应该有某种它们都继承自的基类或它们都实现的接口。该基类/接口应该具有您稍后在该方法中使用的所有操作。在switch 之前创建该基本类型/接口的变量,然后在每个case 中为该变量分配一个值。

【讨论】:

  • #1,不太确定你在说什么......(我是一个 c# 菜鸟); #2 这是否适用于 MySql .Net 连接器?
  • @o7thWebDesign 这些是非常基本的概念,所以我建议您在继续之前对您不熟悉的术语进行一些研究;解释这些概念超出了 SO 问题的范围。
猜你喜欢
  • 2015-04-01
  • 2020-05-12
  • 2017-05-18
  • 1970-01-01
  • 2022-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多