3

データベースに接続する WCF REST サービスがあります。実際、データベースには複数のインスタンスがあり、すべて同じスキーマを使用しています。

データベース インスタンスごとに 1 つのエンドポイントを設定し、接続文字列をエンドポイントに関連付けます。サービスは接続文字列を読み取り、適切な SQL Server インスタンスに接続します。

これは可能だと確信しています。それは良い考えですか?設定方法を教えてください。MSDN にドキュメントはありますか?

編集:この質問を見つけました。答えは、クライアントの接続情報をヘッダーに追加することを提案しています。セキュリティ上の理由と、データベースごとに異なる uri を使用したいためです。

4

3 に答える 3

2

これは私が思っていたよりも少し大変でした。WCF には非常に多くの拡張ポイントがあり、適切なものを選択するのは困難です。より良い方法があると思われる場合、またはこれに何か問題があると思われる場合は、回答またはコメントしてください。

IEndpointBehaviorIDispatchMessageInspectorを実装するカスタム クラスを使用することにしました。BehaviorExtensionElementから派生したクラスがあり、構成で動作をエンドポイントに関連付けることができます。 このブログ投稿では、ホット ドゥ ドゥ について説明しています。

DatabaseConnectionContextのクラスは次のようになります。

/// <summary>
/// An endpoint behavior that associates a database connection string name with the endpoint and adds it to the
/// properties of incoming messages.
/// </summary>
public class DatabaseConnectionContext : IEndpointBehavior, IDispatchMessageInspector
{
    /// <summary>
    /// Initializes a new instance of the <see cref="DatabaseConnectionContext"/> class with the provided connection string name.
    /// </summary>
    /// <param name="connectionStringName">The name of the connection string to associate with the endpoint.</param>
    public DatabaseConnectionContext(string connectionStringName)
    {
        this.ConnectionStringName = connectionStringName;
    }

    /// <summary>
    /// Gets the name of the connection string to associate with the endpoint.
    /// </summary>
    public string ConnectionStringName { get; private set; }

    /// <inheritdoc />
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    /// <inheritdoc />
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        throw new NotImplementedException();
    }

    /// <inheritdoc />
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
    }

    /// <inheritdoc />
    public void Validate(ServiceEndpoint endpoint)
    {
    }

    /// <inheritdoc />
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        request.Properties["connectionStringName"] = this.ConnectionStringName;
        return null;
    }

    /// <inheritdoc />
    public void BeforeSendReply(ref Message reply, object correlationState)
    {
    }
}

私のサービスクラスには、次のメソッドがあります。

    /// <summary>
    /// Returns the connection string to use for this service call.
    /// </summary>
    /// <returns>A SQL Server database connection string.</returns>
    private string GetConnectionString()
    {
        string connectionStringName = (string)OperationContext.Current.IncomingMessageProperties["connectionStringName"];
        return ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
    }

BehaviorExtensionElementのクラスは次のようになります。

/// <summary>
/// Associates a <see cref="DatabaseConnectionContext"/> with an endpoint in configuration.
/// </summary>
public class DatabaseConnectionContextBehaviorExtension : BehaviorExtensionElement
{
    /// <summary>
    /// The name of the <see cref="ConnectionStringName"/> property when it appears in a configuration file.
    /// </summary>
    private const string ConnectionStringNamePropertyName = "connectionStringName";

    /// <summary>
    /// Gets or sets the name of the configuration string to associate with the endpoint.
    /// </summary>
    [ConfigurationProperty(ConnectionStringNamePropertyName)]
    public string ConnectionStringName
    {
        get
        {
            return (string)this[ConnectionStringNamePropertyName];
        }

        set
        {
            this[ConnectionStringNamePropertyName] = value;
        }
    }

    /// <inheritdoc />
    public override Type BehaviorType
    {
        get { return typeof(DatabaseConnectionContext); }
    }

    /// <inheritdoc />
    protected override object CreateBehavior()
    {
        return new DatabaseConnectionContext(this.ConnectionStringName);
    }
}

私のweb.configには次のようなものが含まれています:

<behaviors>

  <endpointBehaviors>
    <behavior name="DevRestEndpointConfiguration">
      <webHttp helpEnabled="false" />
      <connectionStringInterceptor connectionStringName="myDevConnectionStringName" />
    </behavior>
    <behavior name="ProductionRestEndpointConfiguration">
      <webHttp helpEnabled="false" />
      <connectionStringInterceptor connectionStringName="myProductionConnectionStringName" />
    </behavior>
  </endpointBehaviors>

</behaviors>

<extensions>
  <behaviorExtensions>
    <add name="connectionStringInterceptor" type="DatabaseConnectionContextBehaviorExtension, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </behaviorExtensions>
</extensions>

セクションの各<endpoint />要素には、<services />セクションbehaviorConfigurationの適切な要素の名前が設定されてい<endpointBehaviors />ます。

于 2012-07-04T15:45:15.097 に答える
1

呼び出しが接続するデータベースを指定する新しいパラメーターを追加しないのはなぜですか?

例えば:

  • db数値を取得するパラメーターを追加して、そこから接続することができます
  • 認証方法にそのようなパラメーターを追加できます

最初の項目の例:

public ProductItem GetProduct(int productId, int db = 1)
{
    ProductItem product = new ProductItem();
    string connectionString = getConnectionStringForDb(db);

    using (SqlConnection connection =
        new SqlConnection(connectionString))
    {
        SqlCommand command = new SqlCommand("SELECT name, price FROM Products WHERE productId = @product;", connection);
        command.Parameters.AddWithValue("@product", productId);

        try
        {
            connection.Open();
            SqlDataReader reader = command.ExecuteReader();
            reader.Read();

            product = new product({
                Name = reader[0],
                Price = reader[1]
            });

            reader.Close();
        }
        catch (Exception ex)
        {
            // Log exception
        }
    }

    return product;
}

MSDN から取得

private string getConnectionStringForDb(int type)
{

    System.Configuration.ConnectionStringSettings connString;
    System.Configuration.Configuration rootWebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/MyWebSiteRoot");

    if (rootWebConfig.ConnectionStrings.ConnectionStrings.Count > 0) {

        connString = rootWebConfig.ConnectionStrings.ConnectionStrings["DBConnectionString_" + type];

        if (connString == null) {
            // LOG ERROR
        }
    }
    return connString.ConnectionString;
}

web.config名前に接続文字列を追加するだけで、次のようになります。

DBConnectionString_1, DBConnectionString_2, DBConnectionString_3

またはあなたにとって意味のあるもの。

<connectionStrings>
  <add 
    name="DBConnectionString_1" 
    connectionString="Data Source=serverName;Initial 
    Catalog=Northwind;Persist Security Info=True;User 
    ID=userName;Password=password"
    providerName="System.Data.SqlClient"
  />
  <add 
    name="DBConnectionString_2" 
    connectionString="Data Source=serverName;Initial 
    Catalog=Northwind;Persist Security Info=True;User 
    ID=userName;Password=password"
    providerName="System.Data.SqlClient"
  />
  <add 
    name="DBConnectionString_3" 
    connectionString="Data Source=serverName;Initial 
    Catalog=Northwind;Persist Security Info=True;User 
    ID=userName;Password=password"
    providerName="System.Data.SqlClient"
  />
</connectionStrings>
于 2012-07-04T14:22:20.003 に答える
0

あなたのweb.configでこれを使って:

<configuration>

  <appSettings>
    <add key="Foo.svc" value="tagvalue1"/>
  </appSettings>
  ...

この方法で実行時に値を取得できます。

    private static string GetConfigValue()
    {
        ServiceEndpointCollection ec = OperationContext.Current
            .Host.Description.Endpoints;
        if (ec!=null)
        {
            var segments = ec[0].Address.ToString().Split('/');
            var s = segments[segments.Length-1]; // "Foo.svc"
            return ConfigurationManager.AppSettings[s]; // "tagvalue1"
        } 
        return null;
    }
于 2012-07-04T14:42:48.417 に答える