mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Cache is now purely file based. Very fast! Former-commit-id: 6880348d89c725fa1e7a20223fe10eb5fdb72f9daf/merge-core
18 changed files with 230 additions and 828 deletions
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<configuration> |
||||
|
<solution> |
||||
|
<add key="disableSourceControlIntegration" value="true" /> |
||||
|
</solution> |
||||
|
</configuration> |
||||
@ -0,0 +1 @@ |
|||||
|
8f613402956f9802681f150b1cb51f8400eb628e |
||||
@ -0,0 +1,136 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir> |
||||
|
|
||||
|
<!-- Enable the restore command to run before builds --> |
||||
|
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages> |
||||
|
|
||||
|
<!-- Property that enables building a package from a project --> |
||||
|
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage> |
||||
|
|
||||
|
<!-- Determines if package restore consent is required to restore packages --> |
||||
|
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent> |
||||
|
|
||||
|
<!-- Download NuGet.exe if it does not already exist --> |
||||
|
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup Condition=" '$(PackageSources)' == '' "> |
||||
|
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used --> |
||||
|
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list --> |
||||
|
<!-- |
||||
|
<PackageSource Include="https://www.nuget.org/api/v2/" /> |
||||
|
<PackageSource Include="https://my-nuget-source/nuget/" /> |
||||
|
--> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'"> |
||||
|
<!-- Windows specific commands --> |
||||
|
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath> |
||||
|
<PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'"> |
||||
|
<!-- We need to launch nuget.exe with the mono command if we're not on windows --> |
||||
|
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath> |
||||
|
<PackagesConfig>packages.config</PackagesConfig> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<!-- NuGet command --> |
||||
|
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath> |
||||
|
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources> |
||||
|
|
||||
|
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand> |
||||
|
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand> |
||||
|
|
||||
|
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir> |
||||
|
|
||||
|
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch> |
||||
|
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch> |
||||
|
|
||||
|
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir> |
||||
|
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir> |
||||
|
|
||||
|
<!-- Commands --> |
||||
|
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand> |
||||
|
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand> |
||||
|
|
||||
|
<!-- We need to ensure packages are restored prior to assembly resolve --> |
||||
|
<BuildDependsOn Condition="$(RestorePackages) == 'true'"> |
||||
|
RestorePackages; |
||||
|
$(BuildDependsOn); |
||||
|
</BuildDependsOn> |
||||
|
|
||||
|
<!-- Make the build depend on restore packages --> |
||||
|
<BuildDependsOn Condition="$(BuildPackage) == 'true'"> |
||||
|
$(BuildDependsOn); |
||||
|
BuildPackage; |
||||
|
</BuildDependsOn> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<Target Name="CheckPrerequisites"> |
||||
|
<!-- Raise an error if we're unable to locate nuget.exe --> |
||||
|
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" /> |
||||
|
<!-- |
||||
|
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once. |
||||
|
This effectively acts as a lock that makes sure that the download operation will only happen once and all |
||||
|
parallel builds will have to wait for it to complete. |
||||
|
--> |
||||
|
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" /> |
||||
|
</Target> |
||||
|
|
||||
|
<Target Name="_DownloadNuGet"> |
||||
|
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" /> |
||||
|
</Target> |
||||
|
|
||||
|
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites"> |
||||
|
<Exec Command="$(RestoreCommand)" |
||||
|
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" /> |
||||
|
|
||||
|
<Exec Command="$(RestoreCommand)" |
||||
|
LogStandardErrorAsError="true" |
||||
|
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" /> |
||||
|
</Target> |
||||
|
|
||||
|
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites"> |
||||
|
<Exec Command="$(BuildCommand)" |
||||
|
Condition=" '$(OS)' != 'Windows_NT' " /> |
||||
|
|
||||
|
<Exec Command="$(BuildCommand)" |
||||
|
LogStandardErrorAsError="true" |
||||
|
Condition=" '$(OS)' == 'Windows_NT' " /> |
||||
|
</Target> |
||||
|
|
||||
|
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> |
||||
|
<ParameterGroup> |
||||
|
<OutputFilename ParameterType="System.String" Required="true" /> |
||||
|
</ParameterGroup> |
||||
|
<Task> |
||||
|
<Reference Include="System.Core" /> |
||||
|
<Using Namespace="System" /> |
||||
|
<Using Namespace="System.IO" /> |
||||
|
<Using Namespace="System.Net" /> |
||||
|
<Using Namespace="Microsoft.Build.Framework" /> |
||||
|
<Using Namespace="Microsoft.Build.Utilities" /> |
||||
|
<Code Type="Fragment" Language="cs"> |
||||
|
<![CDATA[ |
||||
|
try { |
||||
|
OutputFilename = Path.GetFullPath(OutputFilename); |
||||
|
|
||||
|
Log.LogMessage("Downloading latest version of NuGet.exe..."); |
||||
|
WebClient webClient = new WebClient(); |
||||
|
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
catch (Exception ex) { |
||||
|
Log.LogErrorFromException(ex); |
||||
|
return false; |
||||
|
} |
||||
|
]]> |
||||
|
</Code> |
||||
|
</Task> |
||||
|
</UsingTask> |
||||
|
</Project> |
||||
@ -1,150 +0,0 @@ |
|||||
// --------------------------------------------------------------------------------------------------------------------
|
|
||||
// <copyright file="SQLContext.cs" company="James South">
|
|
||||
// Copyright (c) James South.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
// <summary>
|
|
||||
// Provides a wrapper for the SQLite functionality.
|
|
||||
// </summary>
|
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
|
||||
|
|
||||
namespace ImageProcessor.Web.Caching |
|
||||
{ |
|
||||
#region Using
|
|
||||
using System.IO; |
|
||||
using System.Threading.Tasks; |
|
||||
using System.Web.Hosting; |
|
||||
using ImageProcessor.Web.Config; |
|
||||
using SQLite; |
|
||||
#endregion
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides a wrapper for the SQLite functionality.
|
|
||||
/// </summary>
|
|
||||
internal sealed class SQLContext |
|
||||
{ |
|
||||
#region Fields
|
|
||||
/// <summary>
|
|
||||
/// The default path for cached folders on the server.
|
|
||||
/// </summary>
|
|
||||
private static readonly string VirtualCachePath = ImageProcessorConfig.Instance.VirtualCachePath; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The cached index location.
|
|
||||
/// </summary>
|
|
||||
private static readonly string IndexLocation = Path.Combine(HostingEnvironment.MapPath(VirtualCachePath), "cache.db"); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The connection string.
|
|
||||
/// </summary>
|
|
||||
private static readonly string ConnectionString = IndexLocation; |
|
||||
#endregion
|
|
||||
|
|
||||
#region Methods
|
|
||||
#region Internal
|
|
||||
/// <summary>
|
|
||||
/// Creates the database if it doesn't already exist.
|
|
||||
/// </summary>
|
|
||||
internal static void CreateDatabase() |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
if (!File.Exists(IndexLocation)) |
|
||||
{ |
|
||||
string absolutePath = HostingEnvironment.MapPath(VirtualCachePath); |
|
||||
|
|
||||
if (absolutePath != null) |
|
||||
{ |
|
||||
DirectoryInfo directoryInfo = new DirectoryInfo(absolutePath); |
|
||||
|
|
||||
if (!directoryInfo.Exists) |
|
||||
{ |
|
||||
// Create the directory.
|
|
||||
Directory.CreateDirectory(absolutePath); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
using (SQLiteConnection connection = new SQLiteConnection(IndexLocation)) |
|
||||
{ |
|
||||
connection.CreateTable<CachedImage>(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
throw; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a cached image from the database.
|
|
||||
/// </summary>
|
|
||||
/// <param name="key">
|
|
||||
/// The key for the cached image to get.
|
|
||||
/// </param>
|
|
||||
/// <returns>
|
|
||||
/// The <see cref="CachedImage"/> from the database.
|
|
||||
/// </returns>
|
|
||||
internal static async Task<CachedImage> GetImageAsync(string key) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
SQLiteAsyncConnection connection = new SQLiteAsyncConnection(ConnectionString); |
|
||||
|
|
||||
return await connection.GetAsync<CachedImage>(c => c.Key == key); |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds a cached image to the database.
|
|
||||
/// </summary>
|
|
||||
/// <param name="image">
|
|
||||
/// The cached image to add.
|
|
||||
/// </param>
|
|
||||
/// <returns>
|
|
||||
/// The true if the addition of the cached image is added; otherwise, false.
|
|
||||
/// </returns>
|
|
||||
internal static async Task<int> AddImageAsync(CachedImage image) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
SQLiteAsyncConnection connection = new SQLiteAsyncConnection(ConnectionString); |
|
||||
return await connection.InsertAsync(image); |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
return 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Removes a cached image from the database.
|
|
||||
/// </summary>
|
|
||||
/// <param name="key">
|
|
||||
/// The key for the cached image.
|
|
||||
/// </param>
|
|
||||
/// <returns>
|
|
||||
/// The true if the addition of the cached image is removed; otherwise, false.
|
|
||||
/// </returns>
|
|
||||
internal static async Task<int> RemoveImageAsync(string key) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
SQLiteAsyncConnection connection = new SQLiteAsyncConnection(ConnectionString); |
|
||||
CachedImage cachedImage = await connection.GetAsync<CachedImage>(c => c.Key == key); |
|
||||
|
|
||||
return await connection.DeleteAsync(cachedImage); |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
return 0; |
|
||||
} |
|
||||
} |
|
||||
#endregion
|
|
||||
#endregion
|
|
||||
} |
|
||||
} |
|
||||
@ -1 +0,0 @@ |
|||||
ce0491dcbe39702bf25fb616f76e1b149f670688 |
|
||||
@ -1,486 +0,0 @@ |
|||||
//
|
|
||||
// Copyright (c) 2012 Krueger Systems, Inc.
|
|
||||
//
|
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||
// in the Software without restriction, including without limitation the rights
|
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||
// furnished to do so, subject to the following conditions:
|
|
||||
//
|
|
||||
// The above copyright notice and this permission notice shall be included in
|
|
||||
// all copies or substantial portions of the Software.
|
|
||||
//
|
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||
// THE SOFTWARE.
|
|
||||
//
|
|
||||
|
|
||||
using System; |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Linq.Expressions; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace SQLite |
|
||||
{ |
|
||||
public partial class SQLiteAsyncConnection |
|
||||
{ |
|
||||
SQLiteConnectionString _connectionString; |
|
||||
|
|
||||
public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = false) |
|
||||
{ |
|
||||
_connectionString = new SQLiteConnectionString (databasePath, storeDateTimeAsTicks); |
|
||||
} |
|
||||
|
|
||||
SQLiteConnectionWithLock GetConnection () |
|
||||
{ |
|
||||
return SQLiteConnectionPool.Shared.GetConnection (_connectionString); |
|
||||
} |
|
||||
|
|
||||
public Task<CreateTablesResult> CreateTableAsync<T> () |
|
||||
where T : new () |
|
||||
{ |
|
||||
return CreateTablesAsync (typeof (T)); |
|
||||
} |
|
||||
|
|
||||
public Task<CreateTablesResult> CreateTablesAsync<T, T2> () |
|
||||
where T : new () |
|
||||
where T2 : new () |
|
||||
{ |
|
||||
return CreateTablesAsync (typeof (T), typeof (T2)); |
|
||||
} |
|
||||
|
|
||||
public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3> () |
|
||||
where T : new () |
|
||||
where T2 : new () |
|
||||
where T3 : new () |
|
||||
{ |
|
||||
return CreateTablesAsync (typeof (T), typeof (T2), typeof (T3)); |
|
||||
} |
|
||||
|
|
||||
public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4> () |
|
||||
where T : new () |
|
||||
where T2 : new () |
|
||||
where T3 : new () |
|
||||
where T4 : new () |
|
||||
{ |
|
||||
return CreateTablesAsync (typeof (T), typeof (T2), typeof (T3), typeof (T4)); |
|
||||
} |
|
||||
|
|
||||
public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4, T5> () |
|
||||
where T : new () |
|
||||
where T2 : new () |
|
||||
where T3 : new () |
|
||||
where T4 : new () |
|
||||
where T5 : new () |
|
||||
{ |
|
||||
return CreateTablesAsync (typeof (T), typeof (T2), typeof (T3), typeof (T4), typeof (T5)); |
|
||||
} |
|
||||
|
|
||||
public Task<CreateTablesResult> CreateTablesAsync (params Type[] types) |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
CreateTablesResult result = new CreateTablesResult (); |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
foreach (Type type in types) { |
|
||||
int aResult = conn.CreateTable (type); |
|
||||
result.Results[type] = aResult; |
|
||||
} |
|
||||
} |
|
||||
return result; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<int> DropTableAsync<T> () |
|
||||
where T : new () |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.DropTable<T> (); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<int> InsertAsync (object item) |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.Insert (item); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<int> UpdateAsync (object item) |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.Update (item); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<int> DeleteAsync (object item) |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.Delete (item); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<T> GetAsync<T>(object pk) |
|
||||
where T : new() |
|
||||
{ |
|
||||
return Task.Factory.StartNew(() => |
|
||||
{ |
|
||||
var conn = GetConnection(); |
|
||||
using (conn.Lock()) |
|
||||
{ |
|
||||
return conn.Get<T>(pk); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<T> FindAsync<T> (object pk) |
|
||||
where T : new () |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.Find<T> (pk); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<T> GetAsync<T> (Expression<Func<T, bool>> predicate) |
|
||||
where T : new() |
|
||||
{ |
|
||||
return Task.Factory.StartNew(() => |
|
||||
{ |
|
||||
var conn = GetConnection(); |
|
||||
using (conn.Lock()) |
|
||||
{ |
|
||||
return conn.Get<T> (predicate); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<T> FindAsync<T> (Expression<Func<T, bool>> predicate) |
|
||||
where T : new () |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.Find<T> (predicate); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<int> ExecuteAsync (string query, params object[] args) |
|
||||
{ |
|
||||
return Task<int>.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.Execute (query, args); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<int> InsertAllAsync (IEnumerable items) |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.InsertAll (items); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
[Obsolete("Will cause a deadlock if any call in action ends up in a different thread. Use RunInTransactionAsync(Action<SQLiteConnection>) instead.")] |
|
||||
public Task RunInTransactionAsync (Action<SQLiteAsyncConnection> action) |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
var conn = this.GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
conn.BeginTransaction (); |
|
||||
try { |
|
||||
action (this); |
|
||||
conn.Commit (); |
|
||||
} |
|
||||
catch (Exception) { |
|
||||
conn.Rollback (); |
|
||||
throw; |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task RunInTransactionAsync(Action<SQLiteConnection> action) |
|
||||
{ |
|
||||
return Task.Factory.StartNew(() => |
|
||||
{ |
|
||||
var conn = this.GetConnection(); |
|
||||
using (conn.Lock()) |
|
||||
{ |
|
||||
conn.BeginTransaction(); |
|
||||
try |
|
||||
{ |
|
||||
action(conn); |
|
||||
conn.Commit(); |
|
||||
} |
|
||||
catch (Exception) |
|
||||
{ |
|
||||
conn.Rollback(); |
|
||||
throw; |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public AsyncTableQuery<T> Table<T> () |
|
||||
where T : new () |
|
||||
{ |
|
||||
//
|
|
||||
// This isn't async as the underlying connection doesn't go out to the database
|
|
||||
// until the query is performed. The Async methods are on the query iteself.
|
|
||||
//
|
|
||||
var conn = GetConnection (); |
|
||||
return new AsyncTableQuery<T> (conn.Table<T> ()); |
|
||||
} |
|
||||
|
|
||||
public Task<T> ExecuteScalarAsync<T> (string sql, params object[] args) |
|
||||
{ |
|
||||
return Task<T>.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
var command = conn.CreateCommand (sql, args); |
|
||||
return command.ExecuteScalar<T> (); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<List<T>> QueryAsync<T> (string sql, params object[] args) |
|
||||
where T : new () |
|
||||
{ |
|
||||
return Task<List<T>>.Factory.StartNew (() => { |
|
||||
var conn = GetConnection (); |
|
||||
using (conn.Lock ()) { |
|
||||
return conn.Query<T> (sql, args); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
//
|
|
||||
// TODO: Bind to AsyncConnection.GetConnection instead so that delayed
|
|
||||
// execution can still work after a Pool.Reset.
|
|
||||
//
|
|
||||
public class AsyncTableQuery<T> |
|
||||
where T : new () |
|
||||
{ |
|
||||
TableQuery<T> _innerQuery; |
|
||||
|
|
||||
public AsyncTableQuery (TableQuery<T> innerQuery) |
|
||||
{ |
|
||||
_innerQuery = innerQuery; |
|
||||
} |
|
||||
|
|
||||
public AsyncTableQuery<T> Where (Expression<Func<T, bool>> predExpr) |
|
||||
{ |
|
||||
return new AsyncTableQuery<T> (_innerQuery.Where (predExpr)); |
|
||||
} |
|
||||
|
|
||||
public AsyncTableQuery<T> Skip (int n) |
|
||||
{ |
|
||||
return new AsyncTableQuery<T> (_innerQuery.Skip (n)); |
|
||||
} |
|
||||
|
|
||||
public AsyncTableQuery<T> Take (int n) |
|
||||
{ |
|
||||
return new AsyncTableQuery<T> (_innerQuery.Take (n)); |
|
||||
} |
|
||||
|
|
||||
public AsyncTableQuery<T> OrderBy<U> (Expression<Func<T, U>> orderExpr) |
|
||||
{ |
|
||||
return new AsyncTableQuery<T> (_innerQuery.OrderBy<U> (orderExpr)); |
|
||||
} |
|
||||
|
|
||||
public AsyncTableQuery<T> OrderByDescending<U> (Expression<Func<T, U>> orderExpr) |
|
||||
{ |
|
||||
return new AsyncTableQuery<T> (_innerQuery.OrderByDescending<U> (orderExpr)); |
|
||||
} |
|
||||
|
|
||||
public Task<List<T>> ToListAsync () |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) { |
|
||||
return _innerQuery.ToList (); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<int> CountAsync () |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) { |
|
||||
return _innerQuery.Count (); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<T> ElementAtAsync (int index) |
|
||||
{ |
|
||||
return Task.Factory.StartNew (() => { |
|
||||
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) { |
|
||||
return _innerQuery.ElementAt (index); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<T> FirstAsync () |
|
||||
{ |
|
||||
return Task<T>.Factory.StartNew(() => { |
|
||||
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) { |
|
||||
return _innerQuery.First (); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public Task<T> FirstOrDefaultAsync () |
|
||||
{ |
|
||||
return Task<T>.Factory.StartNew(() => { |
|
||||
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) { |
|
||||
return _innerQuery.FirstOrDefault (); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class CreateTablesResult |
|
||||
{ |
|
||||
public Dictionary<Type, int> Results { get; private set; } |
|
||||
|
|
||||
internal CreateTablesResult () |
|
||||
{ |
|
||||
this.Results = new Dictionary<Type, int> (); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class SQLiteConnectionPool |
|
||||
{ |
|
||||
class Entry |
|
||||
{ |
|
||||
public SQLiteConnectionString ConnectionString { get; private set; } |
|
||||
public SQLiteConnectionWithLock Connection { get; private set; } |
|
||||
|
|
||||
public Entry (SQLiteConnectionString connectionString) |
|
||||
{ |
|
||||
ConnectionString = connectionString; |
|
||||
Connection = new SQLiteConnectionWithLock (connectionString); |
|
||||
} |
|
||||
|
|
||||
public void OnApplicationSuspended () |
|
||||
{ |
|
||||
Connection.Dispose (); |
|
||||
Connection = null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
readonly Dictionary<string, Entry> _entries = new Dictionary<string, Entry> (); |
|
||||
readonly object _entriesLock = new object (); |
|
||||
|
|
||||
static readonly SQLiteConnectionPool _shared = new SQLiteConnectionPool (); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the singleton instance of the connection tool.
|
|
||||
/// </summary>
|
|
||||
public static SQLiteConnectionPool Shared |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _shared; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public SQLiteConnectionWithLock GetConnection (SQLiteConnectionString connectionString) |
|
||||
{ |
|
||||
lock (_entriesLock) { |
|
||||
Entry entry; |
|
||||
string key = connectionString.ConnectionString; |
|
||||
|
|
||||
if (!_entries.TryGetValue (key, out entry)) { |
|
||||
entry = new Entry (connectionString); |
|
||||
_entries[key] = entry; |
|
||||
} |
|
||||
|
|
||||
return entry.Connection; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Closes all connections managed by this pool.
|
|
||||
/// </summary>
|
|
||||
public void Reset () |
|
||||
{ |
|
||||
lock (_entriesLock) { |
|
||||
foreach (var entry in _entries.Values) { |
|
||||
entry.OnApplicationSuspended (); |
|
||||
} |
|
||||
_entries.Clear (); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Call this method when the application is suspended.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>Behaviour here is to close any open connections.</remarks>
|
|
||||
public void ApplicationSuspended () |
|
||||
{ |
|
||||
Reset (); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class SQLiteConnectionWithLock : SQLiteConnection |
|
||||
{ |
|
||||
readonly object _lockPoint = new object (); |
|
||||
|
|
||||
public SQLiteConnectionWithLock (SQLiteConnectionString connectionString) |
|
||||
: base (connectionString.DatabasePath, connectionString.StoreDateTimeAsTicks) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public IDisposable Lock () |
|
||||
{ |
|
||||
return new LockWrapper (_lockPoint); |
|
||||
} |
|
||||
|
|
||||
private class LockWrapper : IDisposable |
|
||||
{ |
|
||||
object _lockPoint; |
|
||||
|
|
||||
public LockWrapper (object lockPoint) |
|
||||
{ |
|
||||
_lockPoint = lockPoint; |
|
||||
Monitor.Enter (_lockPoint); |
|
||||
} |
|
||||
|
|
||||
public void Dispose () |
|
||||
{ |
|
||||
Monitor.Exit (_lockPoint); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@ -1,5 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<packages > |
|
||||
<package id="Csharp-Sqlite" version="3.7.7.1" targetFramework="net40" /> |
|
||||
<package id="sqlite-net" version="1.0.7" targetFramework="net40" /> |
|
||||
</packages> |
|
||||
@ -1,6 +1,5 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<repositories> |
<repositories> |
||||
<repository path="..\ImageProcessor.Web\NET4\packages.config" /> |
<repository path="..\ImageProcessor.Web\NET4\packages.config" /> |
||||
<repository path="..\ImageProcessor.Web\NET45\packages.config" /> |
|
||||
<repository path="..\TestWebsites\NET45\Test_Website_NET45\packages.config" /> |
<repository path="..\TestWebsites\NET45\Test_Website_NET45\packages.config" /> |
||||
</repositories> |
</repositories> |
||||
Loading…
Reference in new issue