Documentation Coverage Report
Current view: top level - Cqrs/DataStores - SqlDataStore.cs Hit Total Coverage
Version: 2.2 Artefacts: 12 12 100.0 %
Date: 2018-08-07 15:04:50

          Line data    Source code
       1             : #region Copyright
       2             : // // -----------------------------------------------------------------------
       3             : // // <copyright company="Chinchilla Software Limited">
       4             : // //   Copyright Chinchilla Software Limited. All rights reserved.
       5             : // // </copyright>
       6             : // // -----------------------------------------------------------------------
       7             : #endregion
       8             : 
       9             : using System;
      10             : using System.Collections;
      11             : using System.Collections.Generic;
      12             : using System.Data.Linq;
      13             : using System.Linq;
      14             : using System.Linq.Expressions;
      15             : using System.Transactions;
      16             : using cdmdotnet.Logging;
      17             : using Cqrs.Configuration;
      18             : using Cqrs.Entities;
      19             : 
      20             : namespace Cqrs.DataStores
      21             : {
      22             :         /// <summary>
      23             :         /// A <see cref="IDataStore{TData}"/> using simplified SQL.
      24             :         /// </summary>
      25             :         public class SqlDataStore<TData> : IDataStore<TData>
      26             :                 where TData : Entity
      27           1 :         {
      28             :                 internal const string SqlDataStoreDbFileOrServerOrConnectionApplicationKey = @"SqlDataStoreDbFileOrServerOrConnection";
      29             : 
      30             :                 internal const string SqlDataStoreConnectionNameApplicationKey = @"Cqrs.SqlDataStore.ConnectionStringName";
      31             : 
      32             :                 internal const string SqlReadableDataStoreConnectionStringKey = "Cqrs.SqlDataStore.Read.ConnectionStringName";
      33             : 
      34             :                 internal const string SqlWritableDataStoreConnectionStringKey = "Cqrs.SqlDataStore.Write.ConnectionStringName";
      35             : 
      36             :                 /// <summary />
      37             :                 protected IConfigurationManager ConfigurationManager { get; private set; }
      38             : 
      39             :                 /// <summary>
      40             :                 /// Instantiates a new instance of the <see cref="SqlDataStore{TData}"/> class
      41             :                 /// </summary>
      42           1 :                 public SqlDataStore(IConfigurationManager configurationManager, ILogger logger)
      43             :                 {
      44             :                         ConfigurationManager = configurationManager;
      45             :                         Logger = logger;
      46             :                         // ReSharper disable DoNotCallOverridableMethodsInConstructor
      47             :                         DbDataContext = CreateDbDataContext();
      48             :                         WriteableConnectionStrings = GetWriteableConnectionStrings();
      49             :                         // ReSharper restore DoNotCallOverridableMethodsInConstructor
      50             : 
      51             :                         // Get a typed table to run queries.
      52             :                         Table = DbDataContext.GetTable<TData>();
      53             :                 }
      54             : 
      55             :                 /// <summary>
      56             :                 /// Gets or sets the DataContext.
      57             :                 /// </summary>
      58             :                 protected DataContext DbDataContext { get; private set; }
      59             : 
      60             :                 /// <summary>
      61             :                 /// Gets or sets the list of writeable connection strings for data mirroring
      62             :                 /// </summary>
      63             :                 protected IEnumerable<string> WriteableConnectionStrings { get; private set; }
      64             : 
      65             :                 /// <summary>
      66             :                 /// Gets or sets the list of writeable DataContexts for data mirroring
      67             :                 /// </summary>
      68             :                 private IList<DataContext> _writeableConnections;
      69             : 
      70             :                 /// <summary>
      71             :                 /// Gets or sets the list of writeable DataContexts for data mirroring
      72             :                 /// </summary>
      73             :                 protected IEnumerable<DataContext> WriteableConnections
      74             :                 {
      75             :                         get
      76             :                         {
      77             :                                 if (_writeableConnections == null)
      78             :                                 {
      79             :                                         _writeableConnections = new List<DataContext>();
      80             :                                         foreach (string writeableConnectionString in WriteableConnectionStrings)
      81             :                                                 _writeableConnections.Add(new DataContext(writeableConnectionString));
      82             :                                 }
      83             :                                 return _writeableConnections;
      84             :                         }
      85             :                 }
      86             : 
      87             :                 /// <summary>
      88             :                 /// Gets or sets the readable Table
      89             :                 /// </summary>
      90             :                 protected Table<TData> Table { get; private set; }
      91             : 
      92             :                 /// <summary>
      93             :                 /// Gets or sets the Logger
      94             :                 /// </summary>
      95             :                 protected ILogger Logger { get; private set; }
      96             : 
      97             :                 /// <summary>
      98             :                 /// Locate the connection settings and create a <see cref="DataContext"/>.
      99             :                 /// </summary>
     100           1 :                 protected virtual DataContext CreateDbDataContext()
     101             :                 {
     102             :                         string connectionStringKey;
     103             :                         string applicationKey;
     104             : 
     105             :                         // Try read/write connection values first
     106             :                         if (!ConfigurationManager.TryGetSetting(SqlReadableDataStoreConnectionStringKey, out applicationKey) || string.IsNullOrEmpty(applicationKey))
     107             :                         {
     108             :                                 // Try single connection value
     109             :                                 if (!ConfigurationManager.TryGetSetting(SqlDataStoreConnectionNameApplicationKey, out applicationKey) || string.IsNullOrEmpty(applicationKey))
     110             :                                 {
     111             :                                         // Default to old connection value
     112             :                                         if (!ConfigurationManager.TryGetSetting(SqlDataStoreDbFileOrServerOrConnectionApplicationKey, out connectionStringKey) || string.IsNullOrEmpty(connectionStringKey))
     113             :                                                 throw new NullReferenceException(string.Format("No application setting named '{0}' was found in the configuration file with the name of a connection string to look for.", SqlDataStoreConnectionNameApplicationKey));
     114             :                                         return new DataContext(connectionStringKey);
     115             :                                 }
     116             :                         }
     117             : 
     118             :                         try
     119             :                         {
     120             :                                 connectionStringKey = System.Configuration.ConfigurationManager.ConnectionStrings[applicationKey].ConnectionString;
     121             :                         }
     122             :                         catch (NullReferenceException exception)
     123             :                         {
     124             :                                 throw new NullReferenceException(string.Format("No connection string setting named '{0}' was found in the configuration file with the SQL Data Store connection string.", applicationKey), exception);
     125             :                         }
     126             : 
     127             :                         return new DataContext(connectionStringKey);
     128             :                 }
     129             : 
     130             :                 /// <summary>
     131             :                 /// Locate the connection settings for persisting data.
     132             :                 /// </summary>
     133           1 :                 protected virtual IEnumerable<string> GetWriteableConnectionStrings()
     134             :                 {
     135             :                         Logger.LogDebug("Getting SQL data store writeable connection strings", "SqlDataStore\\GetWritableConnectionStrings");
     136             : 
     137             :                         string connectionStringKey;
     138             :                         string applicationKey;
     139             : 
     140             :                         // Try read/write connection values first
     141             :                         if (!ConfigurationManager.TryGetSetting(SqlWritableDataStoreConnectionStringKey, out applicationKey) || string.IsNullOrEmpty(applicationKey))
     142             :                         {
     143             :                                 Logger.LogDebug(string.Format("No application setting named '{0}' was found in the configuration file with the name of a connection string to look for.", SqlWritableDataStoreConnectionStringKey), "SqlDataStore\\GetWriteableConnectionStrings");
     144             :                                 // Try single connection value
     145             :                                 if (!ConfigurationManager.TryGetSetting(SqlDataStoreConnectionNameApplicationKey, out applicationKey) || string.IsNullOrEmpty(applicationKey))
     146             :                                 {
     147             :                                         Logger.LogDebug(string.Format("No application setting named '{0}' was found in the configuration file with the name of a connection string to look for.", SqlDataStoreConnectionNameApplicationKey), "SqlDataStore\\GetWriteableConnectionStrings");
     148             :                                         // Default to old connection value
     149             :                                         if (!ConfigurationManager.TryGetSetting(SqlDataStoreDbFileOrServerOrConnectionApplicationKey, out connectionStringKey) || string.IsNullOrEmpty(connectionStringKey))
     150             :                                                 throw new NullReferenceException(string.Format("No application setting named '{0}' was found in the configuration file with the name of a connection string to look for.", SqlDataStoreConnectionNameApplicationKey));
     151             :                                         _writeableConnections = new List<DataContext> { DbDataContext };
     152             :                                         return new List<string> {connectionStringKey};
     153             :                                 }
     154             :                         }
     155             : 
     156             :                         try
     157             :                         {
     158             :                                 var collection = new List<string>();
     159             :                                 string rootApplicationKey = applicationKey;
     160             : 
     161             :                                 int writeIndex = 1;
     162             :                                 while (!string.IsNullOrWhiteSpace(applicationKey))
     163             :                                 {
     164             :                                         try
     165             :                                         {
     166             :                                                 connectionStringKey = System.Configuration.ConfigurationManager.ConnectionStrings[applicationKey].ConnectionString;
     167             :                                                 collection.Add(connectionStringKey);
     168             :                                         }
     169             :                                         catch (NullReferenceException exception)
     170             :                                         {
     171             :                                                 throw new NullReferenceException(string.Format("No connection string setting named '{0}' was found in the configuration file with a SQL connection string.", applicationKey), exception);
     172             :                                         }
     173             : 
     174             :                                         if (!ConfigurationManager.TryGetSetting(string.Format("{0}.{1}", rootApplicationKey, writeIndex), out applicationKey))
     175             :                                                 applicationKey = null;
     176             : 
     177             :                                         writeIndex++;
     178             :                                 }
     179             : 
     180             :                                 if (!collection.Any())
     181             :                                         throw new NullReferenceException();
     182             : 
     183             :                                 return collection;
     184             :                         }
     185             :                         catch (NullReferenceException exception)
     186             :                         {
     187             :                                 throw new NullReferenceException(string.Format("No application setting named '{0}' was found in the configuration file with a SQL connection string.", SqlWritableDataStoreConnectionStringKey), exception);
     188             :                         }
     189             :                         finally
     190             :                         {
     191             :                                 Logger.LogDebug("Getting SQL writeable connection string... Done", "SqlDataStore\\GetWriteableConnectionStrings");
     192             :                         }
     193             :                 }
     194             : 
     195             :                 #region Implementation of IEnumerable
     196             : 
     197             :                 /// <summary>
     198             :                 /// Returns an enumerator that iterates through the collection.
     199             :                 /// </summary>
     200             :                 /// <returns>
     201             :                 /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
     202             :                 /// </returns>
     203           1 :                 public IEnumerator<TData> GetEnumerator()
     204             :                 {
     205             :                         return Table.AsQueryable().GetEnumerator();
     206             :                 }
     207             : 
     208             :                 /// <summary>
     209             :                 /// Returns an enumerator that iterates through a collection.
     210             :                 /// </summary>
     211             :                 /// <returns>
     212             :                 /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
     213             :                 /// </returns>
     214             :                 IEnumerator IEnumerable.GetEnumerator()
     215             :                 {
     216             :                         return GetEnumerator();
     217             :                 }
     218             : 
     219             :                 #endregion
     220             : 
     221             :                 #region Implementation of IQueryable
     222             : 
     223             :                 /// <summary>
     224             :                 /// Gets the expression tree that is associated with the instance of <see cref="T:System.Linq.IQueryable"/>.
     225             :                 /// </summary>
     226             :                 /// <returns>
     227             :                 /// The <see cref="T:System.Linq.Expressions.Expression"/> that is associated with this instance of <see cref="T:System.Linq.IQueryable"/>.
     228             :                 /// </returns>
     229             :                 public Expression Expression
     230             :                 {
     231             :                         get { return Table.AsQueryable().Expression; }
     232             :                 }
     233             : 
     234             :                 /// <summary>
     235             :                 /// Gets the type of the element(s) that are returned when the expression tree associated with this instance of <see cref="T:System.Linq.IQueryable"/> is executed.
     236             :                 /// </summary>
     237             :                 /// <returns>
     238             :                 /// A <see cref="T:System.Type"/> that represents the type of the element(s) that are returned when the expression tree associated with this object is executed.
     239             :                 /// </returns>
     240             :                 public Type ElementType
     241             :                 {
     242             :                         get { return Table.AsQueryable().ElementType; }
     243             :                 }
     244             : 
     245             :                 /// <summary>
     246             :                 /// Gets the query provider that is associated with this data source.
     247             :                 /// </summary>
     248             :                 /// <returns>
     249             :                 /// The <see cref="T:System.Linq.IQueryProvider"/> that is associated with this data source.
     250             :                 /// </returns>
     251             :                 public IQueryProvider Provider
     252             :                 {
     253             :                         get { return Table.AsQueryable().Provider; }
     254             :                 }
     255             : 
     256             :                 #endregion
     257             : 
     258             :                 #region Implementation of IDisposable
     259             : 
     260             :                 /// <summary>
     261             :                 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
     262             :                 /// </summary>
     263           1 :                 public void Dispose()
     264             :                 {
     265             :                         Table = null;
     266             :                         DbDataContext.Dispose();
     267             :                         DbDataContext = null;
     268             :                 }
     269             : 
     270             :                 #endregion
     271             : 
     272             :                 #region Implementation of IDataStore<TData>
     273             : 
     274             :                 /// <summary>
     275             :                 /// Add the provided <paramref name="data"/> to the data store and persist the change.
     276             :                 /// </summary>
     277           1 :                 public virtual void Add(TData data)
     278             :                 {
     279             :                         Logger.LogDebug("Adding data to the SQL database", "SqlDataStore\\Add");
     280             :                         try
     281             :                         {
     282             :                                 DateTime start = DateTime.Now;
     283             :                                 using (var transaction = new TransactionScope())
     284             :                                 {
     285             :                                         foreach (DataContext writeableConnection in WriteableConnections)
     286             :                                         {
     287             :                                                 Table<TData> table = Table;
     288             :                                                 // This optimises for single connection handling
     289             :                                                 if (writeableConnection != DbDataContext)
     290             :                                                         table = writeableConnection.GetTable<TData>();
     291             :                                                 table.InsertOnSubmit(data);
     292             :                                                 writeableConnection.SubmitChanges();
     293             :                                         }
     294             :                                         transaction.Complete();
     295             :                                 }
     296             :                                 DateTime end = DateTime.Now;
     297             :                                 Logger.LogDebug(string.Format("Adding data in the SQL database took {0}.", end - start), "SqlDataStore\\Add");
     298             :                         }
     299             :                         finally
     300             :                         {
     301             :                                 Logger.LogDebug("Adding data to the SQL database... Done", "SqlDataStore\\Add");
     302             :                         }
     303             :                 }
     304             : 
     305             :                 /// <summary>
     306             :                 /// Add the provided <paramref name="data"/> to the data store and persist the change.
     307             :                 /// </summary>
     308           1 :                 public virtual void Add(IEnumerable<TData> data)
     309             :                 {
     310             :                         Logger.LogDebug("Adding data collection to the SQL database", "SqlDataStore\\Add\\Collection");
     311             :                         try
     312             :                         {
     313             :                                 DateTime start = DateTime.Now;
     314             :                                 // Multiple enumeration optimisation
     315             :                                 data = data.ToList();
     316             :                                 using (var transaction = new TransactionScope())
     317             :                                 {
     318             :                                         foreach (DataContext writeableConnection in WriteableConnections)
     319             :                                         {
     320             :                                                 Table<TData> table = Table;
     321             :                                                 // This optimises for single connection handling
     322             :                                                 if (writeableConnection != DbDataContext)
     323             :                                                         table = writeableConnection.GetTable<TData>();
     324             :                                                 table.InsertAllOnSubmit(data);
     325             :                                                 writeableConnection.SubmitChanges();
     326             :                                         }
     327             :                                         transaction.Complete();
     328             :                                 }
     329             :                                 DateTime end = DateTime.Now;
     330             :                                 Logger.LogDebug(string.Format("Adding data in the SQL database took {0}.", end - start), "SqlDataStore\\Add\\Collection");
     331             :                         }
     332             :                         finally
     333             :                         {
     334             :                                 Logger.LogDebug("Adding data collection to the SQL database... Done", "SqlDataStore\\Add\\Collection");
     335             :                         }
     336             :                 }
     337             : 
     338             :                 /// <summary>
     339             :                 /// Will mark the <paramref name="data"/> as logically (or soft) deleted by setting <see cref="Entity.IsLogicallyDeleted"/> to true in the data store and persist the change.
     340             :                 /// </summary>
     341           1 :                 public virtual void Remove(TData data)
     342             :                 {
     343             :                         Logger.LogDebug("Removing data from the Sql database", "SqlDataStore\\Remove");
     344             :                         try
     345             :                         {
     346             :                                 DateTime start = DateTime.Now;
     347             :                                 data.IsLogicallyDeleted = true;
     348             :                                 Update(data);
     349             :                                 DateTime end = DateTime.Now;
     350             :                                 Logger.LogDebug(string.Format("Removing data from the Sql database took {0}.", end - start), "SqlDataStore\\Remove");
     351             :                         }
     352             :                         finally
     353             :                         {
     354             :                                 Logger.LogDebug("Removing data from the Sql database... Done", "SqlDataStore\\Remove");
     355             :                         }
     356             :                 }
     357             : 
     358             :                 /// <summary>
     359             :                 /// Remove the provided <paramref name="data"/> (normally by <see cref="IEntity.Rsn"/>) from the data store and persist the change.
     360             :                 /// </summary>
     361           1 :                 public void Destroy(TData data)
     362             :                 {
     363             :                         Logger.LogDebug("Removing data from the SQL database", "SqlDataStore\\Destroy");
     364             :                         try
     365             :                         {
     366             :                                 DateTime start = DateTime.Now;
     367             :                                 using (var transaction = new TransactionScope())
     368             :                                 {
     369             :                                         foreach (DataContext writeableConnection in WriteableConnections)
     370             :                                         {
     371             :                                                 Table<TData> table = Table;
     372             :                                                 // This optimises for single connection handling
     373             :                                                 if (writeableConnection != DbDataContext)
     374             :                                                         table = writeableConnection.GetTable<TData>();
     375             :                                                 try
     376             :                                                 {
     377             :                                                         table.DeleteOnSubmit(data);
     378             :                                                 }
     379             :                                                 catch (InvalidOperationException exception)
     380             :                                                 {
     381             :                                                         if (exception.Message != "Cannot remove an entity that has not been attached.")
     382             :                                                                 throw;
     383             :                                                         try
     384             :                                                         {
     385             :                                                                 table.Attach(data);
     386             :                                                                 writeableConnection.Refresh(RefreshMode.KeepCurrentValues, data);
     387             :                                                         }
     388             :                                                         catch (DuplicateKeyException)
     389             :                                                         {
     390             :                                                                 // We're using the same context apparently
     391             :                                                         }
     392             :                                                         table.DeleteOnSubmit(data);
     393             :                                                 }
     394             :                                                 try
     395             :                                                 {
     396             :                                                         writeableConnection.SubmitChanges();
     397             :                                                 }
     398             :                                                 catch (ChangeConflictException)
     399             :                                                 {
     400             :                                                         writeableConnection.Refresh(RefreshMode.KeepCurrentValues, data);
     401             :                                                         writeableConnection.SubmitChanges();
     402             :                                                 }
     403             :                                         }
     404             :                                         transaction.Complete();
     405             :                                 }
     406             :                                 DateTime end = DateTime.Now;
     407             :                                 Logger.LogDebug(string.Format("Removing data from the SQL database took {0}.", end - start), "SqlDataStore\\Destroy");
     408             :                         }
     409             :                         finally
     410             :                         {
     411             :                                 Logger.LogDebug("Removing data from the SQL database... Done", "SqlDataStore\\Destroy");
     412             :                         }
     413             :                 }
     414             : 
     415             :                 /// <summary>
     416             :                 /// Remove all contents (normally by use of a truncate operation) from the data store and persist the change.
     417             :                 /// </summary>
     418           1 :                 public virtual void RemoveAll()
     419             :                 {
     420             :                         Logger.LogDebug("Removing all from the SQL database", "SqlDataStore\\RemoveAll");
     421             :                         try
     422             :                         {
     423             :                                 using (var transaction = new TransactionScope())
     424             :                                 {
     425             :                                         foreach (DataContext writeableConnection in WriteableConnections)
     426             :                                         {
     427             :                                                 Table<TData> table = Table;
     428             :                                                 // This optimises for single connection handling
     429             :                                                 if (writeableConnection != DbDataContext)
     430             :                                                         table = writeableConnection.GetTable<TData>();
     431             :                                                 table.Truncate();
     432             :                                                 writeableConnection.SubmitChanges();
     433             :                                         }
     434             :                                         transaction.Complete();
     435             :                                 }
     436             :                         }
     437             :                         finally
     438             :                         {
     439             :                                 Logger.LogDebug("Removing all from the SQL database... Done", "SqlDataStore\\RemoveAll");
     440             :                         }
     441             :                 }
     442             : 
     443             :                 /// <summary>
     444             :                 /// Update the provided <paramref name="data"/> in the data store and persist the change.
     445             :                 /// </summary>
     446           1 :                 public virtual void Update(TData data)
     447             :                 {
     448             :                         Logger.LogDebug("Updating data in the SQL database", "SqlDataStore\\Update");
     449             :                         try
     450             :                         {
     451             :                                 DateTime start = DateTime.Now;
     452             :                                 using (var transaction = new TransactionScope())
     453             :                                 {
     454             :                                         foreach (DataContext writeableConnection in WriteableConnections)
     455             :                                         {
     456             :                                                 Table<TData> table = Table;
     457             :                                                 // This optimises for single connection handling
     458             :                                                 if (writeableConnection != DbDataContext)
     459             :                                                         table = writeableConnection.GetTable<TData>();
     460             :                                                 try
     461             :                                                 {
     462             :                                                         table.Attach(data);
     463             :                                                         writeableConnection.Refresh(RefreshMode.KeepCurrentValues, data);
     464             :                                                 }
     465             :                                                 catch (DuplicateKeyException)
     466             :                                                 {
     467             :                                                         // We're using the same context apparently
     468             :                                                 }
     469             :                                                 writeableConnection.SubmitChanges();
     470             :                                         }
     471             :                                         transaction.Complete();
     472             :                                 }
     473             :                                 DateTime end = DateTime.Now;
     474             :                                 Logger.LogDebug(string.Format("Updating data in the SQL database took {0}.", end - start), "SqlDataStore\\Update");
     475             :                         }
     476             :                         finally
     477             :                         {
     478             :                                 Logger.LogDebug("Updating data to the SQL database... Done", "SqlDataStore\\Update");
     479             :                         }
     480             :                 }
     481             : 
     482             :                 #endregion
     483             :         }
     484             : }

Generated by: LCOV version 1.12