在ASP.NET中编写自己的Provider类

时间:2011-05-27 17:20:52

标签: asp.net membership-provider dataprovider

注意:我不想编写自定义成员资格提供程序。

我想编写自己的Provider类,因此我可以在web.config中定义它并像Membership类一样访问它。

以下是我的类的示例(它有许多其他静态方法):

public static class MySqlHelper
{
    private static string constring = ConfigurationManager.ConnectionStrings["MyConnString"].ConnectionString;

    public static int ExecuteNonQuery(string mysqlquery)
    {
        SqlConnection conn = new SqlConnection(connString);
    SqlCommand cmd = new SqlCommand(mysqlquery, conn);
    int result;

    try
    {
        conn.Open();
        result= cmd.ExecuteNonQuery();
    }
    finally
    {
        conn.Close();
    }
    return result;

    }
}

用法:MySqlHelper.ExecuteNonQuery("select * from customers");

现在你看到我已经硬编码了连接字符串的名称,即“MyConnString”。我打算让它充满活力。

所以我想知道我是否可以像静态内置的Membership类一样,我可以在web.config中定义connectionStringName。通过这种方式,可以使类重用,而无需始终将web.config中的connectionstring命名为“MyConnString”。

1:我不想在每个静态方法中传递connectionstring作为参数。

2:我必须能够访问类似于Membership.CreateUser的方法,即静态。

我正在并行浏览网页,但任何输入/指导都会有所帮助。

编辑:我已更新了我的代码示例,以清除使用静态类的问题的一些混淆。这是我发布的new question以澄清这一点。抱歉混淆。

3 个答案:

答案 0 :(得分:2)

我能想到的唯一符合你所规定的资格的是使用依赖注入,静态构造函数,并注入像IConnectionStringProvider这样的东西。这似乎是我能想到的最令人费解的事情,所以你可能会喜欢它。 :)

修改

阅读完评论后,您似乎只想引用任何连接字符串,但每个应用程序只能引用一个连接字符串。我只想添加一个名为appSettings的{​​{1}}元素,其值为您要使用的连接字符串的名称。

然后在你的助手中,检查appsetting是否存在,获取其值,并将其传递给MySqlProviderConnection来电。这样您的提供商就可以使用您想要的任何连接,而无需更改任何代码。

答案 1 :(得分:1)

我通常不鼓励在多个请求之间共享一个SqlConnection实例。即使您启用MARS,也可能会遇到性能问题。我认为当您的连接收到非读取命令时,连接缓冲区将暂停所有当前读取,直到写入完成。您真正节省的唯一事情就是建立连接所需的时间。

SqlConnections为pooled,因此您可以将提供程序配置为具有可用于请求客户端的最小/最大实例数。请记住,这也是由您连接的数据库控制的;假设您正在连接到SQL Server实例,SQL Server有自己的最大连接允许设置。

我建议您的公共成员接受命令字符串或命令参数,而不是允许客户端确定何时打开/关闭共享SqlConnection实例。然后,类似于您的示例建议,从池中打开连接并执行命令。

public IEnumerable<SqlResults> ExecuteStoredProcedure(string procedure, params SqlParameter[] parameters) {
    using(SqlConnection connection = new SqlConnection(MyConnectionStringProperty)) {
        try {
            connection.Open();

            using(SqlCommand command = new SqlCommand(procedure, connection)) {
                command.CommandType = CommandType.StoredProcedure;

                if(parameters != null) {
                    command.Parameters.AddRange(parameters);
                }

                // yield return to handle whatever results from proc execution
                // can also consider expanding to support reader.NextResult()
                using(SqlDataReader reader = command.ExecuteReader()) {
                    yield return new SqlResults {
                        Reader = reader;
                    };
                }
            }
        }
        finally {
            if(connection.State != ConnectionState.Closed) {
                connection.Close();
            }
        }
    }
}

上面的示例代码就是 - 我在工作中使用的概念示例。该示例现在具有最大化的错误处理,但在返回和处理结果方面非常灵活。 SqlResults类只包含SqlDataReader属性,可以扩展为包含错误。

至于制作这个static中的任何一个,只要你启用一种方法来创建提供者类的单例实例并且继续不共享任何可变属性(可能是各种各样的),它应该没问题。请求/线程)。您可能需要考虑某种IoC或依赖注入方法,以便根据您的请求提供连接字符串。

修改

Yield允许调用者在执行上下文返回到生成返回以继续执行的方法之前使用返回的对象。因此,在上面的示例中,调用者可以执行以下操作:

// Since it's an IEnumerable we can handle multiple result sets
foreach(SqlResults results in MySqlHelper.ExecuteStoredProcedure(myProcedureName, new SqlParameter("myParamName", myParamValue)) {
    // handle results
}

在我们处理结果时没有连接关闭。如果您在示例中注意到,我们的using对象有SqlClient个语句。这种方法允许结果集处理与MySqlHelper分离,因为提供者类将处理可能重复的SQL提供代码,将结果处理委托给调用者,然后继续它必须做的事情(即关闭连接)。

至于IoC / DI,我个人使用Castle Windsor。您可以将依赖项对象注入属性或构造参数。注册Inversion of Control容器作为依赖项资源管理器将允许您(在其他情况下)在请求资源类型时返回相同的对象。基本上,对于需要使用MySqlHelper的每个调用者类,可以在实例化调用者类时或调用者类引用其公共MySqlHelper属性时注入相同的实例。我个人而言,尽可能地更喜欢构造函数注入。另外,当我说注射时,我的意思是你不必担心设置属性值,因为你的IoC / DI会为你做(如果配置正确)。有关更深入的解释,请参阅here

另外需要注意的是,如果您的类是非静态的,那么IoC / DI方法才真正发挥作用,这样每个应用程序都可以拥有自己的单例实例。如果MySqlHelper是静态的,那么除非您将其传入,否则您只能支持一个连接字符串,在原始问题中,您不希望这样做。 IoC / DI将允许您使用MySqlHelper属性成员,就好像它是静态的,因为已注册的容器将确保该属性具有正确的实例。

答案 2 :(得分:0)

以下是我在一些小项目中使用的SqlHelper的完整代码。 但对这类课程要谨慎static。如果您将它用于Web项目,请记住连接将在所有用户的同一实例上共享,这可能会导致错误的问题......

using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;

public class SqlHelper
{
    private SqlConnection connection;

    public SqlHelper()
    {
        connection = new SqlConnection();
    }

    public void OpenConnection()
    {
        // Updated code getting the ConnectionString without hard naming it.
        // Yes, if you have more than 1 you'll have problems... But, how many times it happens?

        if (WebConfigurationManager.ConnectionStrings.Length == 0)
            throw new ArgumentNullException("You need to configure the ConnectionString on your Web.config.");
        else
        {
            connection.ConnectionString = WebConfigurationManager.ConnectionStrings[0].ConnectionString;
            connection.Open();
        }
    }

    public void CloseConnection()
    {
        if (connection != null && connection.State != ConnectionState.Closed)
            connection.Close();
    }

    public DataTable ExecuteToDataTable(string sql)
    {
        DataTable data;

        SqlCommand command = null;
        SqlDataAdapter adapter = null;

        try
        {
            if (connection.State != ConnectionState.Open)
                OpenConnection();

            command = new SqlCommand(sql, connection);
            adapter = new SqlDataAdapter(command);

            retorno = new DataTable();
            adapter.Fill(data);
        }
        finally
        {
            if (command != null)
                command.Dispose();

            if (adapter != null)
                adapter.Dispose();

            CloseConnection();
        }

        return data;
    }

    public int ExecuteNonQuery(string sql)
    {
        SqlCommand command = null;

        try
        {
            if (connection.State != ConnectionState.Open)
                OpenConnection();

            command = new SqlCommand(sql, connection);
            return command.ExecuteNonQuery();
        }
        finally
        {
            if (command != null)
                command.Dispose();

            CloseConnection();
        }
    }

    public object ExecuteScalar(string sql)
    {
        SqlCommand command = null;

        try
        {
            if (connection.State != ConnectionState.Open)
                OpenConnection();

            command = new SqlCommand(sql, connection);
            return command.ExecuteScalar();
        }
        finally
        {
            if (command != null)
                command.Dispose();

            CloseConnection();
        }
    }
}

样本用法:

SqlHelper sql = new SqlHelper();
DataTable data = sql.ExecuteToDataTable("SELECT * FROM Customers");
int affected = sql.ExecuteNonQuery("INSERT Customers VALUES ('Test')");

但是如果你真的想要static(如果你是一个用户环境),那就把static放在所有方法上。