2012-07-04 50 views
3

我們有一個連接到數據庫的WCF REST服務。實際上,我們有幾個數據庫實例,都具有相同的模式。我可以將連接字符串與WCF中的端點關聯嗎?

我們希望爲每個數據庫實例設置一個端點,並將連接字符串與端點相關聯。該服務將讀取連接字符串並連接到相應的SQL Server實例。

我相信這是可能的;這是個好主意嗎?我如何設置它? MSDN上有文檔嗎?

編輯:我找到了this question,其中答案建議在頭中添加客戶端的連接信息。我不想這樣做 - 出於安全原因,並且因爲我想要爲每個數據庫有不同的uri。

回答

2

這比我想象的有點困難。 WCF有這麼多的可擴展性指出它很難挑選出正確的。如果您認爲有更好的方法,或者有任何問題,請回答或發表評論。

我已經決定使用實現IEndpointBehaviorIDispatchMessageInspector的自定義類。我有一個派生自BehaviorExtensionElement的類,可以讓我將行爲與配置中的端點相關聯。 This blog post描述熱做這個。

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); 
    } 
} 

我的網站.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> 

<services />部分中的每個<endpoint />元件具有其behaviorConfiguration組從<endpointBehaviors />部的適當元素的名稱。

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; 
} 

t阿肯from 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> 
+0

我很喜歡這個建議。它適用於REST服務,其中部分URI可以很容易地作爲參數傳遞給服務方法。但是,我已經採用了不同的解決方案 - 請參閱我自己的回覆 - 因爲如果我們稍後公開SOAP端點,我認爲它會更好。另外,我們不一定希望每個出現在web.config中的連接字符串都可用。 – Olly

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; 
    } 
相關問題