Free cookie consent management tool by TermsFeed Policy Generator

source: branches/1888_OaaS/HeuristicLab.Services.Hive/3.3/ConnectionProvider.cs @ 16371

Last change on this file since 16371 was 8326, checked in by fschoepp, 12 years ago

#1888:

  • Added IConnectionProvider interface + implementation based on jheinzelreiters transaction management
  • Fixed transaction being promoted to dtc; transactions enabled again
  • Changed HiveDao: transactions are managed properly
File size: 7.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26using HeuristicLab.Services.Hive.Interfaces;
27using System.Configuration;
28using System.Data.Common;
29using System.ServiceModel;
30using System.Transactions;
31
32namespace HeuristicLab.Services.Hive {
33  public class ConnectionProvider : IConnectionProvider {
34    public System.Data.Common.DbConnection GetOpenConnection(string connectionStringName) {
35      ConnectionStringSettings connSettings = ConfigurationManager.ConnectionStrings[connectionStringName];
36      DbProviderFactory dbProvFact = DbProviderFactories.GetFactory(connSettings.ProviderName);
37      return ConnectionUtilsStorage.Current.GetOpenConnection(dbProvFact, connSettings.ConnectionString);
38    }
39
40
41    public void ReleaseConnection(DbConnection connection) {
42      ConnectionUtilsStorage.Current.ReleaseConnection(connection);
43    }
44  }
45
46  /// <summary>
47  /// Based on the connection management developed by jheinzelreiter,
48  /// this class stores transactions + connections, closing connections
49  /// after a transaction has been closed.
50  /// A non-transacted connection might be closed by the user by calling
51  /// ReleaseConnection(connection).
52  /// </summary>
53  class ConnectionUtilsStorage : IExtension<OperationContext> {
54    private readonly IDictionary<Transaction, IDictionary<string, DbConnection>>
55      transactedConnections = new Dictionary<Transaction, IDictionary<string, DbConnection>>();
56
57    /// <summary>
58    /// Returns an opened DbConnection. If a transaction is present, for
59    /// each unique connection string the same connection is returned.
60    /// </summary>
61    /// <param name="providerFactory">DbProviderFactory, which should be used to create
62    /// connection</param>
63    /// <param name="connectionString">connection string of the connection</param>
64    /// <returns>A <see cref="DbConnection"/> instance</returns>
65    public DbConnection GetOpenConnection(DbProviderFactory providerFactory, string connectionString) {
66      DbConnection connection = null;
67      Transaction currentTransaction = Transaction.Current;
68      if (currentTransaction == null) {
69        // no transaction context -> return "ordinary" connection
70        connection = CreateOpenConnection(providerFactory, connectionString);
71      }
72      else {
73        // transaction present
74        IDictionary<string, DbConnection> connections = null;
75        lock (transactedConnections) {
76          // check if current transaction already has at least one connection assigned
77          if (!transactedConnections.TryGetValue(currentTransaction, out connections)) {
78            connections = new Dictionary<string, DbConnection>();
79            transactedConnections[currentTransaction] = connections;
80          }
81
82          if (!connections.TryGetValue(connectionString, out connection)) {
83            // no connection for this connection string in the current transaction
84            connection = CreateOpenConnection(providerFactory, connectionString);
85
86            connections[connectionString] = connection;
87            currentTransaction.TransactionCompleted +=
88              new TransactionCompletedEventHandler(OnTransactionCompleted);
89          }
90        }
91      }
92      return connection;
93    }
94
95    /// <summary>
96    /// Creates a new, opened DbConnection
97    /// </summary>
98    /// <param name="connectionString">connection string of the connection</param>
99    /// <param name="providerFactory">DbProviderFactory, which should be used to create
100    /// connection</param>
101    /// <returns>A <see cref="DbConnection"/> instance </returns>
102    private DbConnection CreateOpenConnection(DbProviderFactory providerFactory,
103                                                     string connectionString) {
104      DbConnection connection = providerFactory.CreateConnection();
105      connection.ConnectionString = connectionString;
106      connection.Open();
107      return connection;
108    }
109
110    /// <summary>
111    /// Releases a connection.
112    /// (Remark: The connection is closed only when not used in a transaction)
113    /// </summary>
114    /// <param name="connection">connection to release</param>
115    public void ReleaseConnection(DbConnection connection) {
116      if (connection == null)
117        return;
118
119      Transaction currentTransaction = Transaction.Current;
120      // close connection if no transaction is active
121      if (currentTransaction == null)
122        connection.Close();
123    }
124
125    /// <summary>
126    /// Determines, if custom connection handling code should close a connection.
127    /// (Remark: Used for DataReader.CommandBehavior)
128    /// </summary>
129    /// <returns></returns>
130    public bool ShouldCloseConnection() {
131      return Transaction.Current == null;
132    }
133
134    /// <summary>
135    /// Eventhandler for TransactionScope.TransactionCompleted.
136    /// Closes connections and removes transaction from observed connections.
137    /// </summary>
138    /// <param name="sender">sender</param>
139    /// <param name="e">TransactionEventArgs</param>
140    private void OnTransactionCompleted(object sender, TransactionEventArgs e) {
141      lock (transactedConnections) {
142        IDictionary<string, DbConnection> connections = null;
143        if (transactedConnections.TryGetValue(e.Transaction, out connections)) {
144          foreach (DbConnection conn in connections.Values)
145            conn.Close();
146          transactedConnections.Remove(e.Transaction);
147        }
148      }
149    }
150    private static object monitor = new object();
151    private static ConnectionUtilsStorage _context = new ConnectionUtilsStorage();
152
153    public static ConnectionUtilsStorage Current {
154      get {
155        ConnectionUtilsStorage context;
156        if (OperationContext.Current != null) {
157          context = OperationContext.Current.Extensions.Find<ConnectionUtilsStorage>();
158          if (context == null) {
159            lock (monitor) {
160              context = OperationContext.Current.Extensions.Find<ConnectionUtilsStorage>();
161              if (context == null) {
162                context = new ConnectionUtilsStorage();
163                OperationContext.Current.Extensions.Add(context);
164              }
165            }
166          }
167        }
168        else {
169          context = _context;
170        }
171        return context;
172      }
173    }
174
175    public void Attach(OperationContext owner) {
176    }
177
178    public void Detach(OperationContext owner) {
179    }
180  }
181}
Note: See TracBrowser for help on using the repository browser.