/
RealmConfiguration.cs
183 lines (164 loc) · 8.58 KB
/
RealmConfiguration.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Realms.Helpers;
using Realms.Native;
using Realms.Schema;
namespace Realms
{
/// <summary>
/// Realm configuration specifying settings that affect the Realm's behavior.
/// <br/>
/// Its main role is generating a canonical path from whatever absolute, relative subdirectory, or just filename the user supplies.
/// </summary>
public class RealmConfiguration : RealmConfigurationBase
{
/// <summary>
/// In order to handle manual migrations, you need to supply a migration callback to your
/// <see cref="RealmConfiguration"/>. It will be called with a <see cref="Migration"/> instance containing
/// the pre- and the post-migration <see cref="Realm"/>. You should make sure that the <see cref="Migration.NewRealm"/>
/// property on it contains a database that is up to date when returning. The <c>oldSchemaVersion</c>
/// parameter will tell you which <see cref="RealmConfigurationBase.SchemaVersion"/> the user is migrating
/// <b>from</b>. They should always be migrating to the current <see cref="RealmConfigurationBase.SchemaVersion"/>.
/// </summary>
/// <param name="migration">
/// The <see cref="Migration"/> instance, containing information about the old and the new <see cref="Realm"/>.
/// </param>
/// <param name="oldSchemaVersion">
/// An unsigned long value indicating the <see cref="RealmConfigurationBase.SchemaVersion"/> of the old
/// <see cref="Realm"/>.
/// </param>
public delegate void MigrationCallbackDelegate(Migration migration, ulong oldSchemaVersion);
/// <summary>
/// A callback, invoked when opening a Realm for the first time during the life
/// of a process to determine if it should be compacted before being returned
/// to the user.
/// </summary>
/// <param name="totalBytes">Total file size (data + free space).</param>
/// <param name="bytesUsed">Total data size.</param>
/// <returns><c>true</c> to indicate that an attempt to compact the file should be made.</returns>
/// <remarks>The compaction will be skipped if another process is accessing it.</remarks>
public delegate bool ShouldCompactDelegate(ulong totalBytes, ulong bytesUsed);
/// <summary>
/// Gets or sets a value indicating whether the database will be deleted if the <see cref="RealmSchema"/>
/// mismatches the one in the code. Use this when debugging and developing your app but never release it with
/// this flag set to <c>true</c>.
/// </summary>
/// <value><c>true</c> to delete the database on schema mismatch; <c>false</c> otherwise.</value>
public bool ShouldDeleteIfMigrationNeeded { get; set; }
/// <summary>
/// Gets or sets a value indicating whether a <see cref="Realm"/> is opened as readonly. This allows opening it
/// from locked locations such as resources, bundled with an application.
/// </summary>
/// <value><c>true</c> if the <see cref="Realm"/> will be opened as readonly; <c>false</c> otherwise.</value>
public bool IsReadOnly { get; set; }
/// <summary>
/// Gets or sets the migration callback.
/// </summary>
/// <value>
/// The <see cref="MigrationCallbackDelegate"/> that will be invoked if the <see cref="Realm"/> needs
/// to be migrated.
/// </value>
public MigrationCallbackDelegate? MigrationCallback { get; set; }
/// <summary>
/// Gets or sets the compact on launch callback.
/// </summary>
/// <value>
/// The <see cref="ShouldCompactDelegate"/> that will be invoked when opening a Realm for the first time
/// to determine if it should be compacted before being returned to the user.
/// </value>
public ShouldCompactDelegate? ShouldCompactOnLaunch { get; set; }
/// <summary>
/// Gets or sets the key, used to encrypt the entire Realm. Once set, must be specified each time the file is used.
/// </summary>
/// <value>Full 64byte (512bit) key for AES-256 encryption.</value>
public new byte[]? EncryptionKey
{
get => base.EncryptionKey;
set => base.EncryptionKey = value;
}
private static RealmConfigurationBase? _defaultConfiguration;
/// <summary>
/// Gets or sets the <see cref="RealmConfigurationBase"/> that is used when creating a new <see cref="Realm"/> without specifying a configuration.
/// </summary>
/// <value>The default configuration.</value>
public static RealmConfigurationBase DefaultConfiguration
{
get => _defaultConfiguration ??= new RealmConfiguration();
set
{
Argument.NotNull(value, nameof(value));
_defaultConfiguration = value;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="RealmConfiguration"/> class.
/// </summary>
/// <param name="optionalPath">Path to the realm, must be a valid full path for the current platform, relative subdirectory, or just filename.</param>
public RealmConfiguration(string? optionalPath = null) : base(optionalPath)
{
}
/// <summary>
/// Clone method allowing you to override or customize the current path.
/// </summary>
/// <returns>An object with a fully-specified, canonical path.</returns>
/// <param name="newConfigPath">Path to the realm, must be a valid full path for the current platform, relative subdirectory, or just filename.</param>
public RealmConfiguration ConfigWithPath(string newConfigPath)
{
var ret = (RealmConfiguration)MemberwiseClone();
ret.DatabasePath = GetPathToRealm(newConfigPath);
return ret;
}
internal override SharedRealmHandle CreateHandle(in Configuration configuration)
=> SharedRealmHandle.Open(configuration);
internal override Configuration CreateNativeConfiguration(Arena arena)
{
var result = base.CreateNativeConfiguration(arena);
result.delete_if_migration_needed = ShouldDeleteIfMigrationNeeded;
result.read_only = IsReadOnly;
result.invoke_migration_callback = MigrationCallback != null;
result.invoke_should_compact_callback = ShouldCompactOnLaunch != null;
result.automatically_migrate_embedded = true;
return result;
}
internal override Task<SharedRealmHandle> CreateHandleAsync(in Configuration configuration, CancellationToken cancellationToken)
{
// Can't use async/await due to mono inliner bugs
// If we are on UI thread will be set but often also set on long-lived workers to use Post back to UI thread.
if (AsyncHelper.TryGetScheduler(out var scheduler))
{
// make a local copy because we can't capture in/ref/out parameters in lambdas
var config = configuration;
return Task.Run(() =>
{
// make another copy so we can open a temporary realm on this worker thread
// to execute any migrations on the background
// without clobbering the original config's managed handle
var configCopy = config;
configCopy.managed_config = GCHandle.ToIntPtr(GCHandle.Alloc(this));
using (CreateHandle(configCopy))
{
}
}, cancellationToken).ContinueWith(_ => CreateHandle(config), scheduler);
}
return Task.FromResult(CreateHandle(configuration));
}
}
}