aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2013-05-21 11:52:59 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2013-05-21 11:52:59 -0400
commite01202030dcd16cd9c7c3327b4e411be7de02614 (patch)
tree1f905a3471d6e2f9f6e241f491da5c51f2021e47
parent8682ee30dd8d1d654c79467bd5c0939c7339f214 (diff)
removed sql delayed writer in favor of prepared statements
-rw-r--r--MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs1
-rw-r--r--MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs18
-rw-r--r--MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs171
-rw-r--r--MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs165
-rw-r--r--MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs18
-rw-r--r--MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs20
7 files changed, 167 insertions, 228 deletions
diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs
index e9953d135..9b0bf824a 100644
--- a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs
+++ b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs
@@ -279,7 +279,6 @@ namespace MediaBrowser.Controller.Providers.TV
n = n.SelectSingleNode("./BannerPath");
if (n != null)
{
- if (season.BackdropImagePaths == null) season.BackdropImagePaths = new List<string>();
season.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false));
}
}
diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs
index f6a77026b..a50dda066 100644
--- a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs
+++ b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs
@@ -525,7 +525,7 @@ namespace MediaBrowser.Controller.Providers.TV
if (series.BackdropImagePaths.Count < ConfigurationManager.Configuration.MaxBackdrops)
{
- var bdNo = 0;
+ var bdNo = series.BackdropImagePaths.Count;
var xmlNodeList = images.SelectNodes("//Banner[BannerType='fanart']");
if (xmlNodeList != null)
{
diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs
index 93bb174c6..93b246380 100644
--- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs
+++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs
@@ -34,18 +34,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
}
/// <summary>
- /// Gets a value indicating whether [enable delayed commands].
- /// </summary>
- /// <value><c>true</c> if [enable delayed commands]; otherwise, <c>false</c>.</value>
- protected override bool EnableDelayedCommands
- {
- get
- {
- return false;
- }
- }
-
- /// <summary>
/// The _json serializer
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
@@ -132,13 +120,13 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested();
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "replace into displaypreferences (id, data) values (@1, @2)";
cmd.AddParam("@1", displayPreferences.Id);
cmd.AddParam("@2", serialized);
- using (var tran = connection.BeginTransaction())
+ using (var tran = Connection.BeginTransaction())
{
try
{
@@ -174,7 +162,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("displayPreferencesId");
}
- var cmd = connection.CreateCommand();
+ var cmd = Connection.CreateCommand();
cmd.CommandText = "select data from displaypreferences where id = @id";
var idParam = cmd.Parameters.Add("@id", DbType.Guid);
diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs
index 62268c0c3..23ab10499 100644
--- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs
+++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Server.Implementations.Reflection;
using System;
using System.Collections.Generic;
using System.Data;
+using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Threading;
@@ -54,6 +55,19 @@ namespace MediaBrowser.Server.Implementations.Sqlite
private readonly IApplicationPaths _appPaths;
/// <summary>
+ /// The _save item command
+ /// </summary>
+ private SQLiteCommand _saveItemCommand;
+ /// <summary>
+ /// The _delete children command
+ /// </summary>
+ private SQLiteCommand _deleteChildrenCommand;
+ /// <summary>
+ /// The _save children command
+ /// </summary>
+ private SQLiteCommand _saveChildrenCommand;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class.
/// </summary>
/// <param name="appPaths">The app paths.</param>
@@ -100,6 +114,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite
};
RunQueries(queries);
+
+ PrepareStatements();
}
//cascade delete triggers
@@ -117,13 +133,46 @@ namespace MediaBrowser.Server.Implementations.Sqlite
END";
/// <summary>
+ /// The _write lock
+ /// </summary>
+ private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1,1);
+
+ /// <summary>
+ /// Prepares the statements.
+ /// </summary>
+ private void PrepareStatements()
+ {
+ _saveItemCommand = new SQLiteCommand
+ {
+ CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)"
+ };
+
+ _saveItemCommand.Parameters.Add(new SQLiteParameter("@1"));
+ _saveItemCommand.Parameters.Add(new SQLiteParameter("@2"));
+ _saveItemCommand.Parameters.Add(new SQLiteParameter("@3"));
+
+ _deleteChildrenCommand = new SQLiteCommand
+ {
+ CommandText = "delete from children where guid = @guid"
+ };
+ _deleteChildrenCommand.Parameters.Add(new SQLiteParameter("@guid"));
+
+ _saveChildrenCommand = new SQLiteCommand
+ {
+ CommandText = "replace into children (guid, child) values (@guid, @child)"
+ };
+ _saveChildrenCommand.Parameters.Add(new SQLiteParameter("@guid"));
+ _saveChildrenCommand.Parameters.Add(new SQLiteParameter("@child"));
+ }
+
+ /// <summary>
/// Save a standard item in the repo
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
- public Task SaveItem(BaseItem item, CancellationToken cancellationToken)
+ public async Task SaveItem(BaseItem item, CancellationToken cancellationToken)
{
if (item == null)
{
@@ -137,19 +186,51 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested();
- return Task.Run(() =>
+ var serialized = _jsonSerializer.SerializeToBytes(item);
+
+ await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ SQLiteTransaction transaction = null;
+
+ try
{
- var serialized = _jsonSerializer.SerializeToBytes(item);
+ transaction = Connection.BeginTransaction();
- cancellationToken.ThrowIfCancellationRequested();
+ _saveItemCommand.Parameters[0].Value = item.Id;
+ _saveItemCommand.Parameters[1].Value = item.GetType().FullName;
+ _saveItemCommand.Parameters[2].Value = serialized;
- var cmd = connection.CreateCommand();
- cmd.CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)";
- cmd.AddParam("@1", item.Id);
- cmd.AddParam("@2", item.GetType().FullName);
- cmd.AddParam("@3", serialized);
- QueueCommand(cmd);
- });
+ _saveItemCommand.Transaction = transaction;
+
+ await _saveItemCommand.ExecuteNonQueryAsync(cancellationToken);
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+ }
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to save item:", e);
+
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+
+ _writeLock.Release();
+ }
}
/// <summary>
@@ -157,6 +238,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary>
/// <param name="id">The id.</param>
/// <returns>BaseItem.</returns>
+ /// <exception cref="System.ArgumentNullException">id</exception>
/// <exception cref="System.ArgumentException"></exception>
public BaseItem GetItem(Guid id)
{
@@ -189,6 +271,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary>
/// <param name="id">The id.</param>
/// <returns>BaseItem.</returns>
+ /// <exception cref="System.ArgumentNullException">id</exception>
/// <exception cref="System.ArgumentException"></exception>
protected BaseItem RetrieveItemInternal(Guid id)
{
@@ -197,7 +280,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("id");
}
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "select obj_type,data from items where guid = @guid";
var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
@@ -240,7 +323,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException();
}
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)";
var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);
@@ -281,7 +364,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
- public Task SaveChildren(Guid id, IEnumerable<BaseItem> children, CancellationToken cancellationToken)
+ public async Task SaveChildren(Guid id, IEnumerable<BaseItem> children, CancellationToken cancellationToken)
{
if (id == Guid.Empty)
{
@@ -300,27 +383,57 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested();
- return Task.Run(() =>
- {
- var cmd = connection.CreateCommand();
+ await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ SQLiteTransaction transaction = null;
- cmd.CommandText = "delete from children where guid = @guid";
- cmd.AddParam("@guid", id);
+ try
+ {
+ transaction = Connection.BeginTransaction();
- QueueCommand(cmd);
+ // Delete exising children
+ _deleteChildrenCommand.Parameters[0].Value = id;
+ _deleteChildrenCommand.Transaction = transaction;
+ await _deleteChildrenCommand.ExecuteNonQueryAsync(cancellationToken);
+ // Save new children
foreach (var child in children)
{
- var guid = child.Id;
- cmd = connection.CreateCommand();
- cmd.AddParam("@guid", id);
- cmd.CommandText = "replace into children (guid, child) values (@guid, @child)";
- var childParam = cmd.Parameters.Add("@child", DbType.Guid);
-
- childParam.Value = guid;
- QueueCommand(cmd);
+ _saveChildrenCommand.Transaction = transaction;
+
+ _saveChildrenCommand.Parameters[0].Value = id;
+ _saveChildrenCommand.Parameters[1].Value = child.Id;
+
+ await _saveChildrenCommand.ExecuteNonQueryAsync(cancellationToken);
+ }
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
}
- });
+ }
+ catch (Exception e)
+ {
+ Logger.ErrorException("Failed to save item:", e);
+
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+
+ _writeLock.Release();
+ }
}
/// <summary>
diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs
index 6aeb63d9b..bd60c834f 100644
--- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs
+++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs
@@ -18,24 +18,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <summary>
/// The db file name
/// </summary>
- protected string dbFileName;
+ protected string DbFileName;
/// <summary>
/// The connection
/// </summary>
- protected SQLiteConnection connection;
- /// <summary>
- /// The delayed commands
- /// </summary>
- protected ConcurrentQueue<SQLiteCommand> delayedCommands = new ConcurrentQueue<SQLiteCommand>();
- /// <summary>
- /// The flush interval
- /// </summary>
- private const int FlushInterval = 2000;
-
- /// <summary>
- /// The flush timer
- /// </summary>
- private Timer FlushTimer;
+ protected SQLiteConnection Connection;
/// <summary>
/// Gets the logger.
@@ -44,18 +31,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
protected ILogger Logger { get; private set; }
/// <summary>
- /// Gets a value indicating whether [enable delayed commands].
- /// </summary>
- /// <value><c>true</c> if [enable delayed commands]; otherwise, <c>false</c>.</value>
- protected virtual bool EnableDelayedCommands
- {
- get
- {
- return true;
- }
- }
-
- /// <summary>
/// Initializes a new instance of the <see cref="SqliteRepository" /> class.
/// </summary>
/// <param name="logManager">The log manager.</param>
@@ -83,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("dbPath");
}
- dbFileName = dbPath;
+ DbFileName = dbPath;
var connectionstr = new SQLiteConnectionStringBuilder
{
PageSize = 4096,
@@ -93,15 +68,9 @@ namespace MediaBrowser.Server.Implementations.Sqlite
JournalMode = SQLiteJournalModeEnum.Memory
};
- connection = new SQLiteConnection(connectionstr.ConnectionString);
+ Connection = new SQLiteConnection(connectionstr.ConnectionString);
- await connection.OpenAsync().ConfigureAwait(false);
-
- if (EnableDelayedCommands)
- {
- // Run once
- FlushTimer = new Timer(Flush, null, TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1));
- }
+ await Connection.OpenAsync().ConfigureAwait(false);
}
/// <summary>
@@ -117,11 +86,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("queries");
}
- using (var tran = connection.BeginTransaction())
+ using (var tran = Connection.BeginTransaction())
{
try
{
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
foreach (var query in queries)
{
@@ -165,26 +134,15 @@ namespace MediaBrowser.Server.Implementations.Sqlite
{
lock (_disposeLock)
{
- if (connection != null)
+ if (Connection != null)
{
- if (EnableDelayedCommands)
+ if (Connection.IsOpen())
{
- FlushOnDispose();
+ Connection.Close();
}
- if (connection.IsOpen())
- {
- connection.Close();
- }
-
- connection.Dispose();
- connection = null;
- }
-
- if (FlushTimer != null)
- {
- FlushTimer.Dispose();
- FlushTimer = null;
+ Connection.Dispose();
+ Connection = null;
}
}
}
@@ -196,101 +154,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
}
/// <summary>
- /// Flushes the on dispose.
- /// </summary>
- private void FlushOnDispose()
- {
- // If we're not already flushing, do it now
- if (!_isFlushing)
- {
- Flush(null);
- }
-
- // Don't dispose in the middle of a flush
- while (_isFlushing)
- {
- Thread.Sleep(25);
- }
- }
-
- /// <summary>
- /// Queues the command.
- /// </summary>
- /// <param name="cmd">The CMD.</param>
- /// <exception cref="System.ArgumentNullException">cmd</exception>
- protected void QueueCommand(SQLiteCommand cmd)
- {
- if (cmd == null)
- {
- throw new ArgumentNullException("cmd");
- }
-
- delayedCommands.Enqueue(cmd);
- }
-
- /// <summary>
- /// The is flushing
- /// </summary>
- private bool _isFlushing;
-
- /// <summary>
- /// Flushes the specified sender.
- /// </summary>
- /// <param name="sender">The sender.</param>
- private void Flush(object sender)
- {
- // Cannot call Count on a ConcurrentQueue since it's an O(n) operation
- // Use IsEmpty instead
- if (delayedCommands.IsEmpty)
- {
- FlushTimer.Change(TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1));
- return;
- }
-
- if (_isFlushing)
- {
- return;
- }
-
- _isFlushing = true;
- var numCommands = 0;
-
- using (var tran = connection.BeginTransaction())
- {
- try
- {
- while (!delayedCommands.IsEmpty)
- {
- SQLiteCommand command;
-
- delayedCommands.TryDequeue(out command);
-
- command.Connection = connection;
- command.Transaction = tran;
-
- command.ExecuteNonQuery();
-
- command.Dispose();
-
- numCommands++;
- }
-
- tran.Commit();
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to commit transaction.", e);
- tran.Rollback();
- }
- }
-
- Logger.Debug("SQL Delayed writer executed " + numCommands + " commands");
-
- FlushTimer.Change(TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1));
- _isFlushing = false;
- }
-
- /// <summary>
/// Executes the command.
/// </summary>
/// <param name="cmd">The CMD.</param>
@@ -303,11 +166,11 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException("cmd");
}
- using (var tran = connection.BeginTransaction())
+ using (var tran = Connection.BeginTransaction())
{
try
{
- cmd.Connection = connection;
+ cmd.Connection = Connection;
cmd.Transaction = tran;
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs
index 443b15c10..d20b59035 100644
--- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs
+++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs
@@ -36,18 +36,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
}
}
- /// <summary>
- /// Gets a value indicating whether [enable delayed commands].
- /// </summary>
- /// <value><c>true</c> if [enable delayed commands]; otherwise, <c>false</c>.</value>
- protected override bool EnableDelayedCommands
- {
- get
- {
- return false;
- }
- }
-
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
@@ -184,14 +172,14 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested();
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "replace into userdata (key, userId, data) values (@1, @2, @3)";
cmd.AddParam("@1", key);
cmd.AddParam("@2", userId);
cmd.AddParam("@3", serialized);
- using (var tran = connection.BeginTransaction())
+ using (var tran = Connection.BeginTransaction())
{
try
{
@@ -247,7 +235,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <returns>Task{UserItemData}.</returns>
private async Task<UserItemData> RetrieveUserData(Guid userId, string key)
{
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "select data from userdata where key = @key and userId=@userId";
diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs
index eb5868932..baaf9d956 100644
--- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs
+++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs
@@ -46,18 +46,6 @@ namespace MediaBrowser.Server.Implementations.Sqlite
private readonly IApplicationPaths _appPaths;
/// <summary>
- /// Gets a value indicating whether [enable delayed commands].
- /// </summary>
- /// <value><c>true</c> if [enable delayed commands]; otherwise, <c>false</c>.</value>
- protected override bool EnableDelayedCommands
- {
- get
- {
- return false;
- }
- }
-
- /// <summary>
/// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class.
/// </summary>
/// <param name="appPaths">The app paths.</param>
@@ -127,13 +115,13 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested();
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "replace into users (guid, data) values (@1, @2)";
cmd.AddParam("@1", user.Id);
cmd.AddParam("@2", serialized);
- using (var tran = connection.BeginTransaction())
+ using (var tran = Connection.BeginTransaction())
{
try
{
@@ -162,7 +150,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <returns>IEnumerable{User}.</returns>
public IEnumerable<User> RetrieveAllUsers()
{
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "select data from users";
@@ -201,7 +189,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken.ThrowIfCancellationRequested();
- using (var cmd = connection.CreateCommand())
+ using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "delete from users where guid=@guid";
var guidParam = cmd.Parameters.Add("@guid", DbType.Guid);