From da438630f9273aba45881a2522fe07bd11def938 Mon Sep 17 00:00:00 2001
From: Glenn <5834289+glennawatson@users.noreply.github.com>
Date: Sun, 26 Jun 2022 01:00:02 +1000
Subject: [PATCH] feature: Add maui and use HttpClient (#788)
---
README.md | 25 +-
packages/repositories.config | 19 --
src/Akavache.Core/Akavache.Core.csproj | 35 ++-
src/Akavache.Core/BlobCache/BlobCache.cs.orig | 230 +++++++++++++++
src/Akavache.Core/DependencyResolverMixin.cs | 2 +-
src/Akavache.Core/HttpMixinExtensions.cs | 66 +++++
.../IAkavacheHttpClientFactory.cs | 19 ++
src/Akavache.Core/IAkavacheHttpMixin.cs | 52 +++-
.../Json/JsonSerializationMixin.cs | 2 +-
.../Platforms/shared/AkavacheHttpMixin.cs | 263 ++++++++----------
.../DefaultAkavacheHttpClientFactory.cs | 19 ++
.../Platforms/shared/Registrations.cs | 1 +
src/Akavache.Drawing/Akavache.Drawing.csproj | 10 +-
src/Akavache.Drawing/BitmapImageMixin.cs | 2 +-
src/Akavache.Mobile/Akavache.Mobile.csproj | 6 +-
src/Akavache.Sqlite3/Akavache.Sqlite3.csproj | 6 +-
src/Akavache.Sqlite3/Registrations.cs | 4 +-
src/Akavache.Sqlite3/SQLite.cs | 2 +-
.../SqlLiteCache/SQLiteEncryptedBlobCache.cs | 4 +-
...ovalTests.AkavacheCore.net6.0.approved.txt | 21 ++
src/Akavache.Tests/API/ApiApprovalTests.cs | 2 +-
src/Akavache.Tests/Akavache.Tests.csproj | 3 +-
.../Helpers/IntegrationTestHelper.cs | 2 +-
src/Akavache.Tests/Performance/ReadTests.cs | 5 +-
src/Akavache.Tests/Performance/WriteTests.cs | 5 +-
src/Akavache.Tests/UtilityTests.cs | 4 +-
src/Akavache/Akavache.csproj | 6 +-
src/Akavache/LinkerPreserve.cs | 3 +-
src/Directory.build.props | 1 +
src/Directory.build.targets | 14 +-
30 files changed, 615 insertions(+), 218 deletions(-)
delete mode 100644 packages/repositories.config
create mode 100644 src/Akavache.Core/BlobCache/BlobCache.cs.orig
create mode 100644 src/Akavache.Core/IAkavacheHttpClientFactory.cs
create mode 100644 src/Akavache.Core/Platforms/shared/DefaultAkavacheHttpClientFactory.cs
diff --git a/README.md b/README.md
index 7ae71a41b..6eb8c2158 100644
--- a/README.md
+++ b/README.md
@@ -26,10 +26,10 @@ settings) as well as cached local data that expires.
Akavache is currently compatible with:
-* Xamarin.iOS / Xamarin.Mac
-* Xamarin.Android
-* .NET 4.5 Desktop (WPF)
-* Windows Phone 8.1 Universal Apps
+* Xamarin.iOS / Xamarin.Mac / Xamarin.Android / Xamarin.TVOS / Xamarin.WatchOS
+* Maui iOS / Mac / Mac Catalyst / Android / TVOS
+* .NET 4.6.2 (and above) and .NET 6 Desktop (WPF and WinForms)
+* .NET 6.0
* Windows 10 (Universal Windows Platform)
* Tizen 4.0
@@ -94,27 +94,14 @@ There are four built-in locations that have some magic applied on some systems:
### Platform-specific notes
-* **Xamarin.iOS / Xamarin.Mac** - No issues.
-
-* **Xamarin.Android** - No issues.
-
-* **.NET 4.5 Desktop (WPF)** - No issues.
-
-* **Windows Phone 8.1 Universal Apps** - You must mark your application as `x86`
- or `ARM`, or else you will get a strange runtime error about SQLitePCL_Raw not
- loading correctly. You must *also* ensure that the Microsoft Visual C++ runtime
- is added to your project.
-
* **Windows 10 (Universal Windows Platform)** - You must mark your application as `x86`
or `ARM`, or else you will get a strange runtime error about SQLitePCL_Raw not
loading correctly. You must *also* ensure that the Microsoft Visual C++ runtime
is added to your project.
-* **Tizen 4.0** - No issues.
-
-#### Handling Xamarin Linker
+#### Handling Xamarin/Maui Linker
-There are two options to ensure the Akavache.Sqlite3 dll will not be removed by Xamarin build tools
+There are two options to ensure the Akavache.Sqlite3 dll will not be removed by Xamarin and Maui build tools
#### 1) Add a file to reference the types
diff --git a/packages/repositories.config b/packages/repositories.config
deleted file mode 100644
index 780b7c045..000000000
--- a/packages/repositories.config
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Akavache.Core/Akavache.Core.csproj b/src/Akavache.Core/Akavache.Core.csproj
index 4d81ae46e..1032d694a 100644
--- a/src/Akavache.Core/Akavache.Core.csproj
+++ b/src/Akavache.Core/Akavache.Core.csproj
@@ -1,13 +1,12 @@
- netstandard2.0;netstandard2.1;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;MonoAndroid11.0;tizen40;net6.0
+ netstandard2.0;netstandard2.1;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;MonoAndroid11.0;tizen40;net6.0;net6.0-android;net6.0-ios;net6.0-tvos;net6.0-macos;net6.0-maccatalyst
$(TargetFrameworks);net462;uap10.0.16299;net6.0-windows
Akavache.Core
Akavache
An asynchronous, persistent key-value store for desktop and mobile applications on .NET
akavache.core
latest
- enable
@@ -17,6 +16,10 @@
+
+
+
+
@@ -41,27 +44,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Akavache.Core/BlobCache/BlobCache.cs.orig b/src/Akavache.Core/BlobCache/BlobCache.cs.orig
new file mode 100644
index 000000000..6e888ba60
--- /dev/null
+++ b/src/Akavache.Core/BlobCache/BlobCache.cs.orig
@@ -0,0 +1,230 @@
+// Copyright (c) 2022 .NET Foundation and Contributors. All rights reserved.
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Reactive.Threading.Tasks;
+
+using Newtonsoft.Json.Bson;
+
+using Splat;
+
+namespace Akavache;
+
+///
+/// A class which represents a blobbed cache.
+///
+public static class BlobCache
+{
+ private static string? _applicationName;
+ private static IBlobCache? _localMachine;
+ private static IBlobCache? _userAccount;
+ private static ISecureBlobCache? _secure;
+ private static bool _shutdownRequested;
+
+ private static IScheduler? _taskPoolOverride;
+
+ [ThreadStatic]
+ private static IBlobCache? _unitTestLocalMachine;
+
+ [ThreadStatic]
+ private static IBlobCache? _unitTestUserAccount;
+
+ [ThreadStatic]
+ private static ISecureBlobCache? _unitTestSecure;
+
+ static BlobCache()
+ {
+ Locator.RegisterResolverCallbackChanged(() =>
+ {
+ if (Locator.CurrentMutable is null)
+ {
+ return;
+ }
+
+ Locator.CurrentMutable.InitializeAkavache(Locator.Current);
+ });
+
+ InMemory = new InMemoryBlobCache(Scheduler.Default);
+ }
+
+ ///
+ /// Gets or sets your application's name. Set this at startup, this defines where
+ /// your data will be stored (usually at %AppData%\[ApplicationName]).
+ ///
+ [SuppressMessage("Design", "CA1065: Properties should not fire exceptions.", Justification = "Extreme non standard case.")]
+ public static string ApplicationName
+ {
+<<<<<<< HEAD
+ get
+ {
+ if (_applicationName is null)
+ {
+ throw new InvalidOperationException("Make sure to set BlobCache.ApplicationName on startup");
+ }
+
+ return _applicationName;
+ }
+=======
+ get => _applicationName ?? throw new("Make sure to set BlobCache.ApplicationName on startup");
+>>>>>>> main
+
+ set => _applicationName = value;
+ }
+
+ ///
+ /// Gets or sets the local machine cache. Store data here that is unrelated to the
+ /// user account or shouldn't be uploaded to other machines (i.e.
+ /// image cache data).
+ ///
+ public static IBlobCache LocalMachine
+ {
+ get => _unitTestLocalMachine ?? _localMachine ?? (_shutdownRequested ? new ShutdownBlobCache() : null) ?? Locator.Current.GetService("LocalMachine") ?? throw new InvalidOperationException("Unable to resolve LocalMachine cache. Make sure Akavache is initialized properly.");
+ set
+ {
+ if (ModeDetector.InUnitTestRunner())
+ {
+ _unitTestLocalMachine = value;
+ _localMachine ??= value;
+ }
+ else
+ {
+ _localMachine = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the user account cache. Store data here that is associated with
+ /// the user; in large organizations, this data will be synced to all
+ /// machines via NT Roaming Profiles.
+ ///
+ public static IBlobCache UserAccount
+ {
+ get => _unitTestUserAccount ?? _userAccount ?? (_shutdownRequested ? new ShutdownBlobCache() : null) ?? Locator.Current.GetService("UserAccount") ?? throw new InvalidOperationException("Unable to resolve UserAccount cache. Make sure Akavache is initialized properly.");
+ set
+ {
+ if (ModeDetector.InUnitTestRunner())
+ {
+ _unitTestUserAccount = value;
+ _userAccount ??= value;
+ }
+ else
+ {
+ _userAccount = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets an IBlobCache that is encrypted - store sensitive data in this
+ /// cache such as login information.
+ ///
+ public static ISecureBlobCache Secure
+ {
+ get => _unitTestSecure ?? _secure ?? (_shutdownRequested ? new ShutdownBlobCache() : null) ?? Locator.Current.GetService() ?? throw new InvalidOperationException("Unable to resolve Secure cache. Make sure Akavache is initialized properly.");
+ set
+ {
+ if (ModeDetector.InUnitTestRunner())
+ {
+ _unitTestSecure = value;
+ _secure ??= value;
+ }
+ else
+ {
+ _secure = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets an IBlobCache that simply stores data in memory. Data stored in
+ /// this cache will be lost when the application restarts.
+ ///
+ public static ISecureBlobCache InMemory { get; set; }
+
+ ///
+ /// Gets or sets the DateTimeKind handling for BSON readers to be forced.
+ ///
+ ///
+ ///
+ /// By default, uses a of and
+ /// uses . Thus, DateTimes are serialized as UTC but deserialized as local time. To force BSON readers to
+ /// use some other DateTimeKind, you can set this value.
+ ///
+ ///
+ public static DateTimeKind? ForcedDateTimeKind { get; set; }
+
+ ///
+ /// Gets or sets the Scheduler used for task pools.
+ ///
+ public static IScheduler TaskpoolScheduler
+ {
+ get => _taskPoolOverride ?? Locator.Current.GetService("Taskpool") ?? TaskPoolScheduler.Default;
+ set => _taskPoolOverride = value;
+ }
+
+ ///
+ /// Makes sure that the system has been initialized.
+ ///
+ public static void EnsureInitialized() =>
+
+ // NB: This method doesn't actually do anything, it just ensures
+ // that the static constructor runs
+ LogHost.Default.Debug("Initializing Akavache");
+
+ ///
+ /// This method shuts down all of the blob caches. Make sure call it
+ /// on app exit and await / Wait() on it.
+ ///
+ /// A Task representing when all caches have finished shutting
+ /// down.
+ public static Task Shutdown()
+ {
+ _shutdownRequested = true;
+ var toDispose = new[] { LocalMachine, UserAccount, Secure, InMemory, };
+
+ var ret = toDispose.Select(x =>
+ {
+ x.Dispose();
+ return x.Shutdown;
+ }).Merge().ToList().Select(_ => Unit.Default);
+
+ return ret.ToTask();
+ }
+
+ private class ShutdownBlobCache : ISecureBlobCache
+ {
+ IObservable IBlobCache.Shutdown => Observable.Return(Unit.Default);
+
+ public IScheduler Scheduler => System.Reactive.Concurrency.Scheduler.Immediate;
+
+ ///
+ public DateTimeKind? ForcedDateTimeKind
+ {
+ get => null;
+ set { }
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public IObservable Insert(string key, byte[] data, DateTimeOffset? absoluteExpiration = null) => Observable.Empty();
+
+ public IObservable Get(string key) => Observable.Empty();
+
+ public IObservable> GetAllKeys() => Observable.Empty>();
+
+ public IObservable GetCreatedAt(string key) => Observable.Empty();
+
+ public IObservable Flush() => Observable.Empty();
+
+ public IObservable Invalidate(string key) => Observable.Empty();
+
+ public IObservable InvalidateAll() => Observable.Empty();
+
+ public IObservable Vacuum() => Observable.Empty();
+ }
+}
diff --git a/src/Akavache.Core/DependencyResolverMixin.cs b/src/Akavache.Core/DependencyResolverMixin.cs
index e6e1fbe8f..c4ee225cf 100644
--- a/src/Akavache.Core/DependencyResolverMixin.cs
+++ b/src/Akavache.Core/DependencyResolverMixin.cs
@@ -37,7 +37,7 @@ public static void InitializeAkavache(this IMutableDependencyResolver resolver,
if (fdr?.AssemblyQualifiedName is null)
{
- throw new($"Cannot find valid assembly name for the {nameof(DependencyResolverMixin)} class.");
+ throw new InvalidOperationException($"Cannot find valid assembly name for the {nameof(DependencyResolverMixin)} class.");
}
var assemblyName = new AssemblyName(
diff --git a/src/Akavache.Core/HttpMixinExtensions.cs b/src/Akavache.Core/HttpMixinExtensions.cs
index c4854e5bf..17a768a50 100644
--- a/src/Akavache.Core/HttpMixinExtensions.cs
+++ b/src/Akavache.Core/HttpMixinExtensions.cs
@@ -75,4 +75,70 @@ public static IObservable DownloadUrl(this IBlobCache blobCache, string
/// The data downloaded from the URL.
public static IObservable DownloadUrl(this IBlobCache blobCache, string key, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null) =>
HttpMixin.DownloadUrl(blobCache, key, url, headers, fetchAlways, absoluteExpiration);
+
+ ///
+ /// Download data from an HTTP URL and insert the result into the
+ /// cache. If the data is already in the cache, this returns
+ /// a cached value. The URL itself is used as the key.
+ ///
+ /// The blob cache to perform the operation on.
+ /// The type of HTTP Method.
+ /// The URL to download.
+ /// An optional Dictionary containing the HTTP
+ /// request headers.
+ /// Force a web request to always be issued, skipping the cache.
+ /// An optional expiration date.
+ /// The data downloaded from the URL.
+ public static IObservable DownloadUrl(this IBlobCache blobCache, HttpMethod method, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null) =>
+ HttpMixin.DownloadUrl(blobCache, method, new Uri(url), headers, fetchAlways, absoluteExpiration);
+
+ ///
+ /// Download data from an HTTP URL and insert the result into the
+ /// cache. If the data is already in the cache, this returns
+ /// a cached value. The URL itself is used as the key.
+ ///
+ /// The blob cache to perform the operation on.
+ /// The type of HTTP Method.
+ /// The URL to download.
+ /// An optional Dictionary containing the HTTP
+ /// request headers.
+ /// Force a web request to always be issued, skipping the cache.
+ /// An optional expiration date.
+ /// The data downloaded from the URL.
+ public static IObservable DownloadUrl(this IBlobCache blobCache, HttpMethod method, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null) =>
+ HttpMixin.DownloadUrl(blobCache, method, url, headers, fetchAlways, absoluteExpiration);
+
+ ///
+ /// Download data from an HTTP URL and insert the result into the
+ /// cache. If the data is already in the cache, this returns
+ /// a cached value. An explicit key is provided rather than the URL itself.
+ ///
+ /// The blob cache to perform the operation on.
+ /// The type of HTTP Method.
+ /// The key to store with.
+ /// The URL to download.
+ /// An optional Dictionary containing the HTTP
+ /// request headers.
+ /// Force a web request to always be issued, skipping the cache.
+ /// An optional expiration date.
+ /// The data downloaded from the URL.
+ public static IObservable DownloadUrl(this IBlobCache blobCache, HttpMethod method, string key, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null) =>
+ HttpMixin.DownloadUrl(blobCache, method, key, new Uri(url), headers, fetchAlways, absoluteExpiration);
+
+ ///
+ /// Download data from an HTTP URL and insert the result into the
+ /// cache. If the data is already in the cache, this returns
+ /// a cached value. An explicit key is provided rather than the URL itself.
+ ///
+ /// The blob cache to perform the operation on.
+ /// The type of HTTP Method.
+ /// The key to store with.
+ /// The URL to download.
+ /// An optional Dictionary containing the HTTP
+ /// request headers.
+ /// Force a web request to always be issued, skipping the cache.
+ /// An optional expiration date.
+ /// The data downloaded from the URL.
+ public static IObservable DownloadUrl(this IBlobCache blobCache, HttpMethod method, string key, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null) =>
+ HttpMixin.DownloadUrl(blobCache, method, key, url, headers, fetchAlways, absoluteExpiration);
}
diff --git a/src/Akavache.Core/IAkavacheHttpClientFactory.cs b/src/Akavache.Core/IAkavacheHttpClientFactory.cs
new file mode 100644
index 000000000..a0492963c
--- /dev/null
+++ b/src/Akavache.Core/IAkavacheHttpClientFactory.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2022 .NET Foundation and Contributors. All rights reserved.
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+namespace Akavache;
+
+///
+/// A factory abstraction for a component that can create instances with custom configuration for a given logical name.
+///
+public interface IAkavacheHttpClientFactory
+{
+ ///
+ /// Creates and configures an instance using the configuration that corresponds to the logical name specified by name.
+ ///
+ /// The logical name of the client to create.
+ /// A new instance.
+ HttpClient CreateClient(string name);
+}
diff --git a/src/Akavache.Core/IAkavacheHttpMixin.cs b/src/Akavache.Core/IAkavacheHttpMixin.cs
index eb3fdb8c2..887c98ee2 100644
--- a/src/Akavache.Core/IAkavacheHttpMixin.cs
+++ b/src/Akavache.Core/IAkavacheHttpMixin.cs
@@ -21,6 +21,18 @@ public interface IAkavacheHttpMixin
/// A observable that signals when there is byte data.
IObservable DownloadUrl(IBlobCache blobCache, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null);
+ ///
+ /// Gets a observable for a download.
+ ///
+ /// The blob cache where to get the value from if available.
+ /// The type of method.
+ /// The url where to get the resource if not available in the cache.
+ /// The headers to use in the HTTP action.
+ /// If we should just fetch and not bother checking the cache first.
+ /// A optional expiration date time.
+ /// A observable that signals when there is byte data.
+ IObservable DownloadUrl(IBlobCache blobCache, HttpMethod method, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null);
+
///
/// Gets a observable for a download.
///
@@ -32,6 +44,18 @@ public interface IAkavacheHttpMixin
/// A observable that signals when there is byte data.
IObservable DownloadUrl(IBlobCache blobCache, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null);
+ ///
+ /// Gets a observable for a download.
+ ///
+ /// The blob cache where to get the value from if available.
+ /// The type of method.
+ /// The url where to get the resource if not available in the cache.
+ /// The headers to use in the HTTP action.
+ /// If we should just fetch and not bother checking the cache first.
+ /// A optional expiration date time.
+ /// A observable that signals when there is byte data.
+ IObservable DownloadUrl(IBlobCache blobCache, HttpMethod method, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null);
+
///
/// Gets a observable for a download.
///
@@ -44,6 +68,19 @@ public interface IAkavacheHttpMixin
/// A observable that signals when there is byte data.
IObservable DownloadUrl(IBlobCache blobCache, string key, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null);
+ ///
+ /// Gets a observable for a download.
+ ///
+ /// The blob cache where to get the value from if available.
+ /// The type of method.
+ /// The key to use for the download cache entry.
+ /// The url where to get the resource if not available in the cache.
+ /// The headers to use in the HTTP action.
+ /// If we should just fetch and not bother checking the cache first.
+ /// A optional expiration date time.
+ /// A observable that signals when there is byte data.
+ IObservable DownloadUrl(IBlobCache blobCache, HttpMethod method, string key, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null);
+
///
/// Gets a observable for a download.
///
@@ -55,4 +92,17 @@ public interface IAkavacheHttpMixin
/// A optional expiration date time.
/// A observable that signals when there is byte data.
IObservable DownloadUrl(IBlobCache blobCache, string key, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null);
-}
\ No newline at end of file
+
+ ///
+ /// Gets a observable for a download.
+ ///
+ /// The blob cache where to get the value from if available.
+ /// The type of method.
+ /// The key to use for the download cache entry.
+ /// The url where to get the resource if not available in the cache.
+ /// The headers to use in the HTTP action.
+ /// If we should just fetch and not bother checking the cache first.
+ /// A optional expiration date time.
+ /// A observable that signals when there is byte data.
+ IObservable DownloadUrl(IBlobCache blobCache, HttpMethod method, string key, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null);
+}
diff --git a/src/Akavache.Core/Json/JsonSerializationMixin.cs b/src/Akavache.Core/Json/JsonSerializationMixin.cs
index 1c839aa72..5f3b6df86 100644
--- a/src/Akavache.Core/Json/JsonSerializationMixin.cs
+++ b/src/Akavache.Core/Json/JsonSerializationMixin.cs
@@ -277,7 +277,7 @@ public static IObservable> GetAllObjects(this IBlobCache blobC
if (fetch is null)
{
- return Observable.Throw(new("Could not find a valid way to fetch the value"));
+ return Observable.Throw(new InvalidOperationException("Could not find a valid way to fetch the value"));
}
var result = blobCache.GetObject(key).Select(x => (x, true))
diff --git a/src/Akavache.Core/Platforms/shared/AkavacheHttpMixin.cs b/src/Akavache.Core/Platforms/shared/AkavacheHttpMixin.cs
index 1384fdab5..6bca52d68 100644
--- a/src/Akavache.Core/Platforms/shared/AkavacheHttpMixin.cs
+++ b/src/Akavache.Core/Platforms/shared/AkavacheHttpMixin.cs
@@ -3,8 +3,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
-using System.Net;
-
using Splat;
namespace Akavache;
@@ -14,18 +12,9 @@ namespace Akavache;
///
public class AkavacheHttpMixin : IAkavacheHttpMixin
{
- ///
- /// Download data from an HTTP URL and insert the result into the
- /// cache. If the data is already in the cache, this returns
- /// a cached value. The URL itself is used as the key.
- ///
- /// The blob cache associated with the action.
- /// The URL to download.
- /// An optional Dictionary containing the HTTP
- /// request headers.
- /// Force a web request to always be issued, skipping the cache.
- /// An optional expiration date.
- /// The data downloaded from the URL.
+ private static IAkavacheHttpClientFactory HttpFactoryClient => Locator.Current.GetService() ?? throw new InvalidOperationException("Unable to resolve IAkavacheHttpClientFactory, probably Akavache is not initialized.");
+
+ ///
public IObservable DownloadUrl(IBlobCache blobCache, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null)
{
if (blobCache is null)
@@ -36,18 +25,7 @@ public IObservable DownloadUrl(IBlobCache blobCache, string url, IDictio
return blobCache.DownloadUrl(url, url, headers, fetchAlways, absoluteExpiration);
}
- ///
- /// Download data from an HTTP URL and insert the result into the
- /// cache. If the data is already in the cache, this returns
- /// a cached value. The URL itself is used as the key.
- ///
- /// The blob cache associated with the action.
- /// The URL to download.
- /// An optional Dictionary containing the HTTP
- /// request headers.
- /// Force a web request to always be issued, skipping the cache.
- /// An optional expiration date.
- /// The data downloaded from the URL.
+ ///
public IObservable DownloadUrl(IBlobCache blobCache, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null)
{
if (blobCache is null)
@@ -63,19 +41,7 @@ public IObservable DownloadUrl(IBlobCache blobCache, Uri url, IDictionar
return blobCache.DownloadUrl(url.ToString(), url, headers, fetchAlways, absoluteExpiration);
}
- ///
- /// Download data from an HTTP URL and insert the result into the
- /// cache. If the data is already in the cache, this returns
- /// a cached value. An explicit key is provided rather than the URL itself.
- ///
- /// The blob cache associated with the action.
- /// The key to store with.
- /// The URL to download.
- /// An optional Dictionary containing the HTTP
- /// request headers.
- /// Force a web request to always be issued, skipping the cache.
- /// An optional expiration date.
- /// The data downloaded from the URL.
+ ///
public IObservable DownloadUrl(IBlobCache blobCache, string key, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null)
{
if (blobCache is null)
@@ -83,29 +49,25 @@ public IObservable DownloadUrl(IBlobCache blobCache, string key, string
throw new ArgumentNullException(nameof(blobCache));
}
- var doFetch = MakeWebRequest(new(url), headers).SelectMany(x => ProcessWebResponse(x, url, absoluteExpiration));
+ var doFetch = MakeWebRequest(HttpMethod.Get, new Uri(url), headers).SelectMany(x => ProcessWebResponse(x, url, absoluteExpiration));
var fetchAndCache = doFetch.SelectMany(x => blobCache.Insert(key, x, absoluteExpiration).Select(_ => x));
- var ret = !fetchAlways ? blobCache.Get(key).Catch(fetchAndCache) : fetchAndCache;
+ IObservable ret;
+ if (!fetchAlways)
+ {
+ ret = blobCache.Get(key).Catch(fetchAndCache);
+ }
+ else
+ {
+ ret = fetchAndCache;
+ }
var conn = ret.PublishLast();
conn.Connect();
return conn;
}
- ///
- /// Download data from an HTTP URL and insert the result into the
- /// cache. If the data is already in the cache, this returns
- /// a cached value. An explicit key is provided rather than the URL itself.
- ///
- /// The blob cache associated with the action.
- /// The key to store with.
- /// The URL to download.
- /// An optional Dictionary containing the HTTP
- /// request headers.
- /// Force a web request to always be issued, skipping the cache.
- /// An optional expiration date.
- /// The data downloaded from the URL.
+ ///
public IObservable DownloadUrl(IBlobCache blobCache, string key, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null)
{
if (blobCache is null)
@@ -113,146 +75,145 @@ public IObservable DownloadUrl(IBlobCache blobCache, string key, Uri url
throw new ArgumentNullException(nameof(blobCache));
}
- var doFetch = MakeWebRequest(url, headers).SelectMany(x => ProcessWebResponse(x, url, absoluteExpiration));
+ var doFetch = MakeWebRequest(HttpMethod.Get, url, headers).SelectMany(x => ProcessWebResponse(x, url, absoluteExpiration));
var fetchAndCache = doFetch.SelectMany(x => blobCache.Insert(key, x, absoluteExpiration).Select(_ => x));
- var ret = !fetchAlways ? blobCache.Get(key).Catch(fetchAndCache) : fetchAndCache;
+ IObservable ret;
+ if (!fetchAlways)
+ {
+ ret = blobCache.Get(key).Catch(fetchAndCache);
+ }
+ else
+ {
+ ret = fetchAndCache;
+ }
var conn = ret.PublishLast();
conn.Connect();
return conn;
}
- private static IObservable MakeWebRequest(
- Uri uri,
- IDictionary? headers = null,
- string? content = null,
- int retries = 3,
- TimeSpan? timeout = null)
+ ///
+ public IObservable DownloadUrl(IBlobCache blobCache, HttpMethod method, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null)
{
- IObservable request;
-
-#if !WINDOWS_UWP
- if (ModeDetector.InUnitTestRunner())
- {
- request = Observable.Defer(() =>
- {
- var hwr = CreateWebRequest(uri, headers);
-
- if (content is null)
- {
- return Observable.Start(() => hwr.GetResponse(), BlobCache.TaskpoolScheduler);
- }
-
- var buf = Encoding.UTF8.GetBytes(content);
- return Observable.Start(
- () =>
- {
- hwr.GetRequestStream().Write(buf, 0, buf.Length);
- return hwr.GetResponse();
- },
- BlobCache.TaskpoolScheduler);
- });
- }
- else
-#endif
+ if (blobCache is null)
{
- request = Observable.Defer(() =>
- {
- var hwr = CreateWebRequest(uri, headers);
-
- if (content is null)
- {
- return Observable.FromAsync(() => Task.Factory.FromAsync(hwr.BeginGetResponse, hwr.EndGetResponse, hwr));
- }
-
- var buf = Encoding.UTF8.GetBytes(content);
-
- // NB: You'd think that BeginGetResponse would never block,
- // seeing as how it's asynchronous. You'd be wrong :-/
- var ret = new AsyncSubject();
- Observable.Start(
- () =>
- {
- Observable.FromAsync(() => Task.Factory.FromAsync(hwr.BeginGetRequestStream, hwr.EndGetRequestStream, hwr))
- .SelectMany(x => x.WriteAsyncRx(buf, 0, buf.Length))
- .SelectMany(_ => Observable.FromAsync(() => Task.Factory.FromAsync(hwr.BeginGetResponse, hwr.EndGetResponse, hwr)))
- .Multicast(ret).Connect();
- },
- BlobCache.TaskpoolScheduler);
-
- return ret;
- });
+ throw new ArgumentNullException(nameof(blobCache));
}
- return request.Timeout(timeout ?? TimeSpan.FromSeconds(15), BlobCache.TaskpoolScheduler).Retry(retries);
+ return blobCache.DownloadUrl(method, url, url, headers, fetchAlways, absoluteExpiration);
}
- private static WebRequest CreateWebRequest(Uri uri, IDictionary? headers)
+ ///
+ public IObservable DownloadUrl(IBlobCache blobCache, HttpMethod method, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null)
{
- var hwr = WebRequest.Create(uri);
- if (headers is not null)
+ if (blobCache is null)
{
- foreach (var x in headers)
- {
- hwr.Headers[x.Key] = x.Value;
- }
+ throw new ArgumentNullException(nameof(blobCache));
}
- return hwr;
+ if (url is null)
+ {
+ throw new ArgumentNullException(nameof(url));
+ }
+
+ return blobCache.DownloadUrl(method, url.ToString(), url, headers, fetchAlways, absoluteExpiration);
}
- private static IObservable ProcessWebResponse(WebResponse wr, string url, DateTimeOffset? absoluteExpiration)
+ ///
+ public IObservable DownloadUrl(IBlobCache blobCache, HttpMethod method, string key, string url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null)
{
- if (wr is not HttpWebResponse hwr)
+ if (blobCache is null)
{
- throw new ArgumentException("The Web Response is somehow null but shouldn't be: " + url + " with expiry: " + absoluteExpiration, nameof(wr));
+ throw new ArgumentNullException(nameof(blobCache));
}
- if ((int)hwr.StatusCode >= 400)
+ var doFetch = MakeWebRequest(method, new Uri(url), headers).SelectMany(x => ProcessWebResponse(x, url, absoluteExpiration));
+ var fetchAndCache = doFetch.SelectMany(x => blobCache.Insert(key, x, absoluteExpiration).Select(_ => x));
+
+ IObservable ret;
+ if (!fetchAlways)
{
- return Observable.Throw(new WebException(hwr.StatusDescription));
+ ret = blobCache.Get(key).Catch(fetchAndCache);
}
-
- using var ms = new MemoryStream();
- using (var responseStream = hwr.GetResponseStream())
+ else
{
- if (responseStream is null)
- {
- throw new InvalidOperationException("The response stream is somehow null: " + url + " with expiry: " + absoluteExpiration);
- }
-
- responseStream.CopyTo(ms);
+ ret = fetchAndCache;
}
- var ret = ms.ToArray();
- return Observable.Return(ret);
+ var conn = ret.PublishLast();
+ conn.Connect();
+ return conn;
}
- private static IObservable ProcessWebResponse(WebResponse wr, Uri url, DateTimeOffset? absoluteExpiration)
+ ///
+ public IObservable DownloadUrl(IBlobCache blobCache, HttpMethod method, string key, Uri url, IDictionary? headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null)
{
- if (wr is not HttpWebResponse hwr)
+ if (blobCache is null)
{
- throw new ArgumentException("The Web Response is somehow null but shouldn't be: " + url + " with expiry: " + absoluteExpiration, nameof(wr));
+ throw new ArgumentNullException(nameof(blobCache));
}
- if ((int)hwr.StatusCode >= 400)
+ var doFetch = MakeWebRequest(method, url, headers).SelectMany(x => ProcessWebResponse(x, url, absoluteExpiration));
+ var fetchAndCache = doFetch.SelectMany(x => blobCache.Insert(key, x, absoluteExpiration).Select(_ => x));
+
+ IObservable ret;
+ if (!fetchAlways)
{
- return Observable.Throw(new WebException(hwr.StatusDescription));
+ ret = blobCache.Get(key).Catch(fetchAndCache);
}
+ else
+ {
+ ret = fetchAndCache;
+ }
+
+ var conn = ret.PublishLast();
+ conn.Connect();
+ return conn;
+ }
+
+ private static IObservable MakeWebRequest(
+ HttpMethod method,
+ Uri uri,
+ IDictionary? headers = null,
+ int retries = 3,
+ TimeSpan? timeout = null)
+ {
+ var request = Observable.Defer(() =>
+ {
+ var client = HttpFactoryClient.CreateClient(nameof(AkavacheHttpMixin));
+
+ var request = CreateWebRequest(method, uri, headers);
+
+ return Observable.FromAsync(() => client.SendAsync(request), BlobCache.TaskpoolScheduler);
+ });
+
+ return request.Timeout(timeout ?? TimeSpan.FromSeconds(15), BlobCache.TaskpoolScheduler).Retry(retries);
+ }
- using var ms = new MemoryStream();
- using (var responseStream = hwr.GetResponseStream())
+ private static HttpRequestMessage CreateWebRequest(HttpMethod method, Uri uri, IDictionary? headers)
+ {
+ var requestMessage = new HttpRequestMessage(method, uri);
+ if (headers is not null)
{
- if (responseStream is null)
+ foreach (var x in headers)
{
- throw new InvalidOperationException("The response stream is somehow null: " + url + " with expiry: " + absoluteExpiration);
+ requestMessage.Headers.TryAddWithoutValidation(x.Key, x.Value);
}
+ }
- responseStream.CopyTo(ms);
+ return requestMessage;
+ }
+
+ private static IObservable ProcessWebResponse(HttpResponseMessage response, string url, DateTimeOffset? absoluteExpiration)
+ {
+ if (!response.IsSuccessStatusCode)
+ {
+ return Observable.Throw(new HttpRequestException("Invalid response: " + url + " reason " + response.ReasonPhrase + " with expiry: " + absoluteExpiration));
}
- var ret = ms.ToArray();
- return Observable.Return(ret);
+ return Observable.FromAsync(() => response.Content.ReadAsByteArrayAsync());
}
+
+ private static IObservable ProcessWebResponse(HttpResponseMessage response, Uri url, DateTimeOffset? absoluteExpiration) => ProcessWebResponse(response, url.ToString(), absoluteExpiration);
}
diff --git a/src/Akavache.Core/Platforms/shared/DefaultAkavacheHttpClientFactory.cs b/src/Akavache.Core/Platforms/shared/DefaultAkavacheHttpClientFactory.cs
new file mode 100644
index 000000000..e9e9b3ee1
--- /dev/null
+++ b/src/Akavache.Core/Platforms/shared/DefaultAkavacheHttpClientFactory.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2022 .NET Foundation and Contributors. All rights reserved.
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System.Collections.Concurrent;
+
+namespace Akavache;
+
+///
+/// The default http client factory. Can be replaced with for example the Microsoft Http Client factory.
+///
+public class DefaultAkavacheHttpClientFactory : IAkavacheHttpClientFactory
+{
+ private static ConcurrentDictionary _instances = new();
+
+ ///
+ public HttpClient CreateClient(string name) => _instances.GetOrAdd(name, _ => new HttpClient());
+}
diff --git a/src/Akavache.Core/Platforms/shared/Registrations.cs b/src/Akavache.Core/Platforms/shared/Registrations.cs
index 1234fb921..d45c81a92 100644
--- a/src/Akavache.Core/Platforms/shared/Registrations.cs
+++ b/src/Akavache.Core/Platforms/shared/Registrations.cs
@@ -54,6 +54,7 @@ public void Register(IMutableDependencyResolver resolver, IReadonlyDependencyRes
resolver.Register(() => secure.Value, typeof(ISecureBlobCache), null);
resolver.Register(() => new AkavacheHttpMixin(), typeof(IAkavacheHttpMixin), null);
+ resolver.Register(() => new DefaultAkavacheHttpClientFactory(), typeof(IAkavacheHttpClientFactory), null);
#if COCOA
BlobCache.ApplicationName = NSBundle.MainBundle.BundleIdentifier;
diff --git a/src/Akavache.Drawing/Akavache.Drawing.csproj b/src/Akavache.Drawing/Akavache.Drawing.csproj
index 9e396361d..6e73e3a30 100644
--- a/src/Akavache.Drawing/Akavache.Drawing.csproj
+++ b/src/Akavache.Drawing/Akavache.Drawing.csproj
@@ -1,6 +1,6 @@
- netstandard2.0;netstandard2.1;MonoAndroid11.0;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;tizen40;
+ netstandard2.0;netstandard2.1;MonoAndroid11.0;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;tizen40;net6.0-android;net6.0-ios;net6.0-tvos;net6.0-macos;net6.0-maccatalyst
$(TargetFrameworks);net462;uap10.0.16299;net6.0-windows
Akavache.Drawing
Akavache
@@ -9,8 +9,16 @@
latest
enable
+
+
+
+
+
+
+
+
diff --git a/src/Akavache.Drawing/BitmapImageMixin.cs b/src/Akavache.Drawing/BitmapImageMixin.cs
index 1aa5cab01..fbf8462ea 100644
--- a/src/Akavache.Drawing/BitmapImageMixin.cs
+++ b/src/Akavache.Drawing/BitmapImageMixin.cs
@@ -115,7 +115,7 @@ public static IObservable LoadImageFromUrl(this IBlobCache blobCache, s
/// too small).
public static IObservable ThrowOnBadImageBuffer(byte[] compressedImage) =>
(compressedImage is null || compressedImage.Length < 64) ?
- Observable.Throw(new("Invalid Image")) :
+ Observable.Throw(new InvalidOperationException("Invalid Image")) :
Observable.Return(compressedImage);
private static IObservable BytesToImage(byte[] compressedImage, float? desiredWidth, float? desiredHeight)
diff --git a/src/Akavache.Mobile/Akavache.Mobile.csproj b/src/Akavache.Mobile/Akavache.Mobile.csproj
index 0d7359192..ce37061ba 100644
--- a/src/Akavache.Mobile/Akavache.Mobile.csproj
+++ b/src/Akavache.Mobile/Akavache.Mobile.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;MonoAndroid11.0;tizen40;net6.0
+ netstandard2.0;netstandard2.1;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;MonoAndroid11.0;tizen40;net6.0;net6.0-android;net6.0-ios;net6.0-tvos;net6.0-macos;net6.0-maccatalyst
$(TargetFrameworks);net462;uap10.0.16299
Akavache.Mobile
Akavache.Mobile
@@ -11,6 +11,10 @@
enable
+
+
+
+
diff --git a/src/Akavache.Sqlite3/Akavache.Sqlite3.csproj b/src/Akavache.Sqlite3/Akavache.Sqlite3.csproj
index 00c98bbeb..14e3c1bcc 100644
--- a/src/Akavache.Sqlite3/Akavache.Sqlite3.csproj
+++ b/src/Akavache.Sqlite3/Akavache.Sqlite3.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;MonoAndroid11.0;tizen40;net6.0
+ netstandard2.0;netstandard2.1;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;MonoAndroid11.0;tizen40;net6.0;net6.0-android;net6.0-ios;net6.0-tvos;net6.0-macos;net6.0-maccatalyst
$(TargetFrameworks);net462;uap10.0.16299
Akavache.Sqlite3
Akavache.Sqlite3
@@ -13,6 +13,10 @@
enable
+
+
+
+
diff --git a/src/Akavache.Sqlite3/Registrations.cs b/src/Akavache.Sqlite3/Registrations.cs
index 698677459..3cd75360f 100644
--- a/src/Akavache.Sqlite3/Registrations.cs
+++ b/src/Akavache.Sqlite3/Registrations.cs
@@ -39,7 +39,7 @@ public void Register(IMutableDependencyResolver resolver, IReadonlyDependencyRes
var fs = Locator.Current.GetService();
if (fs is null)
{
- throw new("Failed to initialize Akavache properly. Do you have a reference to Akavache.dll?");
+ throw new InvalidOperationException("Failed to initialize Akavache properly. Do you have a reference to Akavache.dll?");
}
var localCache = new Lazy(() =>
@@ -84,4 +84,4 @@ public void Register(IMutableDependencyResolver resolver, IReadonlyDependencyRes
});
resolver.Register(() => secure.Value, typeof(ISecureBlobCache));
}
-}
\ No newline at end of file
+}
diff --git a/src/Akavache.Sqlite3/SQLite.cs b/src/Akavache.Sqlite3/SQLite.cs
index fd0bde013..2518affa1 100644
--- a/src/Akavache.Sqlite3/SQLite.cs
+++ b/src/Akavache.Sqlite3/SQLite.cs
@@ -3232,7 +3232,7 @@ public static Result Open(string filename, out Sqlite3DatabaseHandle db, int fla
public static Sqlite3Statement Prepare2(Sqlite3DatabaseHandle db, string query)
{
- Sqlite3Statement stmt = default(Sqlite3Statement);
+ var stmt = default(Sqlite3Statement);
#if USE_WP8_NATIVE_SQLITE || USE_SQLITEPCL_RAW
var r = Sqlite3Raw.sqlite3_prepare_v2(db, query, out stmt);
#else
diff --git a/src/Akavache.Sqlite3/SqlLiteCache/SQLiteEncryptedBlobCache.cs b/src/Akavache.Sqlite3/SqlLiteCache/SQLiteEncryptedBlobCache.cs
index 547957f96..6798f4720 100644
--- a/src/Akavache.Sqlite3/SqlLiteCache/SQLiteEncryptedBlobCache.cs
+++ b/src/Akavache.Sqlite3/SqlLiteCache/SQLiteEncryptedBlobCache.cs
@@ -23,7 +23,7 @@ public class SQLiteEncryptedBlobCache : SqlRawPersistentBlobCache, ISecureBlobCa
/// If there is no encryption provider available.
public SQLiteEncryptedBlobCache(string databaseFile, IEncryptionProvider? encryptionProvider = null, IScheduler? scheduler = null)
: base(databaseFile, scheduler) =>
- _encryption = encryptionProvider ?? Locator.Current.GetService() ?? throw new("No IEncryptionProvider available. This should never happen, your DependencyResolver is broken");
+ _encryption = encryptionProvider ?? Locator.Current.GetService() ?? throw new InvalidOperationException("No IEncryptionProvider available. This should never happen, your DependencyResolver is broken");
///
protected override IObservable BeforeWriteToDiskFilter(byte[] data, IScheduler scheduler)
@@ -56,4 +56,4 @@ protected override IObservable AfterReadFromDiskFilter(byte[] data, ISch
return _encryption.DecryptBlock(data);
}
-}
\ No newline at end of file
+}
diff --git a/src/Akavache.Tests/API/ApiApprovalTests.AkavacheCore.net6.0.approved.txt b/src/Akavache.Tests/API/ApiApprovalTests.AkavacheCore.net6.0.approved.txt
index f31ab5aa7..6fbcff54c 100644
--- a/src/Akavache.Tests/API/ApiApprovalTests.AkavacheCore.net6.0.approved.txt
+++ b/src/Akavache.Tests/API/ApiApprovalTests.AkavacheCore.net6.0.approved.txt
@@ -12,8 +12,12 @@ namespace Akavache
public AkavacheHttpMixin() { }
public System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
public System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ public System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ public System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
public System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, string key, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
public System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, string key, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ public System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string key, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ public System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string key, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
}
public static class BlobCache
{
@@ -49,6 +53,11 @@ namespace Akavache
{
CurrentUser = 0,
}
+ public class DefaultAkavacheHttpClientFactory : Akavache.IAkavacheHttpClientFactory
+ {
+ public DefaultAkavacheHttpClientFactory() { }
+ public System.Net.Http.HttpClient CreateClient(string name) { }
+ }
public static class DependencyResolverMixin
{
public static void InitializeAkavache(this Splat.IMutableDependencyResolver resolver, Splat.IReadonlyDependencyResolver readonlyDependencyResolver) { }
@@ -63,15 +72,27 @@ namespace Akavache
{
public static System.IObservable DownloadUrl(this Akavache.IBlobCache blobCache, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
public static System.IObservable DownloadUrl(this Akavache.IBlobCache blobCache, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ public static System.IObservable DownloadUrl(this Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ public static System.IObservable DownloadUrl(this Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
public static System.IObservable DownloadUrl(this Akavache.IBlobCache blobCache, string key, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
public static System.IObservable DownloadUrl(this Akavache.IBlobCache blobCache, string key, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ public static System.IObservable DownloadUrl(this Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string key, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ public static System.IObservable DownloadUrl(this Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string key, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default) { }
+ }
+ public interface IAkavacheHttpClientFactory
+ {
+ System.Net.Http.HttpClient CreateClient(string name);
}
public interface IAkavacheHttpMixin
{
System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default);
System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default);
+ System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default);
+ System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default);
System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, string key, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default);
System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, string key, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default);
+ System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string key, string url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default);
+ System.IObservable DownloadUrl(Akavache.IBlobCache blobCache, System.Net.Http.HttpMethod method, string key, System.Uri url, System.Collections.Generic.IDictionary? headers = null, bool fetchAlways = false, System.DateTimeOffset? absoluteExpiration = default);
}
public interface IBlobCache : System.IDisposable
{
diff --git a/src/Akavache.Tests/API/ApiApprovalTests.cs b/src/Akavache.Tests/API/ApiApprovalTests.cs
index 89e6b1e8d..972f0c4a7 100644
--- a/src/Akavache.Tests/API/ApiApprovalTests.cs
+++ b/src/Akavache.Tests/API/ApiApprovalTests.cs
@@ -96,4 +96,4 @@ private static string Filter(string text)
!l.StartsWith("[assembly: AssemblyInformationalVersion(", StringComparison.InvariantCulture) &&
!string.IsNullOrWhiteSpace(l)));
}
-}
\ No newline at end of file
+}
diff --git a/src/Akavache.Tests/Akavache.Tests.csproj b/src/Akavache.Tests/Akavache.Tests.csproj
index 876b00856..c4c703f86 100644
--- a/src/Akavache.Tests/Akavache.Tests.csproj
+++ b/src/Akavache.Tests/Akavache.Tests.csproj
@@ -3,7 +3,8 @@
net6.0
$(NoWarn);CA1307;CA2000;CA1062
- latest
+ latest
+ disable
diff --git a/src/Akavache.Tests/Helpers/IntegrationTestHelper.cs b/src/Akavache.Tests/Helpers/IntegrationTestHelper.cs
index dfea1f450..7f84364eb 100644
--- a/src/Akavache.Tests/Helpers/IntegrationTestHelper.cs
+++ b/src/Akavache.Tests/Helpers/IntegrationTestHelper.cs
@@ -62,7 +62,7 @@ public static HttpResponseMessage GetResponse(params string[] paths)
goto foundIt;
}
- throw new("Couldn't find response body");
+ throw new InvalidOperationException("Couldn't find response body");
foundIt:
diff --git a/src/Akavache.Tests/Performance/ReadTests.cs b/src/Akavache.Tests/Performance/ReadTests.cs
index edee615d3..56b5bf392 100644
--- a/src/Akavache.Tests/Performance/ReadTests.cs
+++ b/src/Akavache.Tests/Performance/ReadTests.cs
@@ -108,8 +108,7 @@ private async Task GeneratePerfRangesForBlock(Func
var results = new Dictionary();
var dbName = default(string);
- var dirPath = default(string);
- using (Utility.WithEmptyDirectory(out dirPath))
+ using (Utility.WithEmptyDirectory(out var dirPath))
using (var cache = await GenerateAGiantDatabase(dirPath).ConfigureAwait(false))
{
var keys = await cache.GetAllKeys();
@@ -151,4 +150,4 @@ private async Task GenerateAGiantDatabase(string path)
return cache;
}
-}
\ No newline at end of file
+}
diff --git a/src/Akavache.Tests/Performance/WriteTests.cs b/src/Akavache.Tests/Performance/WriteTests.cs
index d6f29182a..cf1ab1af0 100644
--- a/src/Akavache.Tests/Performance/WriteTests.cs
+++ b/src/Akavache.Tests/Performance/WriteTests.cs
@@ -89,8 +89,7 @@ private async Task GeneratePerfRangesForBlock(Func>
var results = new Dictionary();
var dbName = default(string);
- var dirPath = default(string);
- using (Utility.WithEmptyDirectory(out dirPath))
+ using (Utility.WithEmptyDirectory(out var dirPath))
using (var cache = CreateBlobCache(dirPath))
{
dbName = cache.GetType().Name;
@@ -107,4 +106,4 @@ private async Task GeneratePerfRangesForBlock(Func>
Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Akavache.Tests/UtilityTests.cs b/src/Akavache.Tests/UtilityTests.cs
index 34bb23f1c..ed247925a 100644
--- a/src/Akavache.Tests/UtilityTests.cs
+++ b/src/Akavache.Tests/UtilityTests.cs
@@ -72,9 +72,7 @@ public void UtilitySplitsAbsolutePaths()
[Fact]
public void UtilityResolvesAndSplitsRelativePaths()
{
- string path;
-
- path = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"foo\bar" : "foo/bar";
+ var path = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"foo\bar" : "foo/bar";
var components = new DirectoryInfo(path).SplitFullPath().ToList();
Assert.True(components.Count > 2);
diff --git a/src/Akavache/Akavache.csproj b/src/Akavache/Akavache.csproj
index 3bba8722f..186489d84 100644
--- a/src/Akavache/Akavache.csproj
+++ b/src/Akavache/Akavache.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;MonoAndroid10.0;tizen40;net6.0
+ netstandard2.0;netstandard2.1;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;MonoAndroid10.0;tizen40;net6.0;net6.0-android;net6.0-ios;net6.0-tvos;net6.0-macos;net6.0-maccatalyst
$(TargetFrameworks);net462;uap10.0.16299
Akavache
Akavache
@@ -15,6 +15,10 @@
+
+
+
+
diff --git a/src/Akavache/LinkerPreserve.cs b/src/Akavache/LinkerPreserve.cs
index a144c251c..821c32e43 100644
--- a/src/Akavache/LinkerPreserve.cs
+++ b/src/Akavache/LinkerPreserve.cs
@@ -3,6 +3,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
+using System;
using System.Diagnostics.CodeAnalysis;
using Akavache.Core;
@@ -23,5 +24,5 @@ public static class LinkerPreserve
///
/// A exception due to this being in the non-derived assembly.
[SuppressMessage("FxCop.Design", "CA1065: Don't throw in constructors", Justification = "Shim class, should not happen.")]
- static LinkerPreserve() => throw new(typeof(SQLitePersistentBlobCache).FullName);
+ static LinkerPreserve() => throw new InvalidOperationException(typeof(SQLitePersistentBlobCache).FullName);
}
diff --git a/src/Directory.build.props b/src/Directory.build.props
index 8dc07e2ca..43c3d840d 100644
--- a/src/Directory.build.props
+++ b/src/Directory.build.props
@@ -24,6 +24,7 @@
True
latest
enable
+ enable
diff --git a/src/Directory.build.targets b/src/Directory.build.targets
index a65cad9f1..be00ad844 100644
--- a/src/Directory.build.targets
+++ b/src/Directory.build.targets
@@ -15,19 +15,31 @@
$(DefineConstants);MONO;COCOA
+
+ $(DefineConstants);MONO;COCOA
+
$(DefineConstants);MONO;UIKIT;COCOA;XAMARIN_MOBILE
+
+ $(DefineConstants);MONO;UIKIT;COCOA;XAMARIN_MOBILE
+
+
+ $(DefineConstants);MONO;UIKIT;COCOA;XAMARIN_MOBILE
+
$(DefineConstants);MONO;UIKIT;COCOA;XAMARIN_MOBILE
$(DefineConstants);MONO;ANDROID;XAMARIN_MOBILE
+
+ $(DefineConstants);MONO;ANDROID;XAMARIN_MOBILE
+
$(DefineConstants);TIZEN;XAMARIN_MOBILE
$(DefineConstants);PORTABLE
-
\ No newline at end of file
+