Decompiled source of VampireDB v0.1.1
VampireDB.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using LiteDB; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("VampireDB")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Stupid simple wrapper for LiteDB")] [assembly: AssemblyFileVersion("0.1.10.0")] [assembly: AssemblyInformationalVersion("0.1.10+36fa1f86df7af3a70628a68d5ac63188d5334d99")] [assembly: AssemblyProduct("VampireDB")] [assembly: AssemblyTitle("VampireDB")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.10.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace VampireDB { [BepInPlugin("phlebotomist.morphine.VampireDB", "VampireDB", "0.1.10")] public class Plugin : BasePlugin { public static ManualLogSource Logger; public override void Load() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown Logger = ((BasePlugin)this).Log; ManualLogSource logger = Logger; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(24, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Plugin "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("phlebotomist.morphine.VampireDB"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" version "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.1.10"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded!"); } logger.LogInfo(val); } public override bool Unload() { Storage.Instance.Dispose(); return true; } } public static class PluginInfo { public const string PLUGIN_GUID = "phlebotomist.morphine.VampireDB"; public const string PLUGIN_NAME = "VampireDB"; public const string PLUGIN_VERSION = "0.1.10"; } public sealed class Storage : IDisposable { private class Record { public string Id { get; set; } public ulong PlatformId { get; set; } public string Key { get; set; } public BsonValue Value { get; set; } } private static readonly object _sync = new object(); private readonly LiteDatabase _db; private readonly ILiteCollection<Record> _col; private const string DATA_FILE_NAME = "VampireDB.db"; private const string DB_PATH = "BepInEx/config/VampireDB/"; public static Storage Instance { get; } = new Storage(Path.Combine("BepInEx/config/VampireDB/", "VampireDB.db")); private Storage(string filePath) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown string directoryName = Path.GetDirectoryName(filePath); if (!Directory.Exists(directoryName)) { ManualLogSource logger = Plugin.Logger; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(38, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("No storage found creating storage at: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(filePath); } logger.LogInfo(val); Directory.CreateDirectory(Path.GetDirectoryName(filePath)); } _db = new LiteDatabase(new ConnectionString { Filename = filePath, Connection = (ConnectionType)1 }, (BsonMapper)null); _col = _db.GetCollection<Record>("records", (BsonAutoId)10); _col.EnsureIndex((Record r) => new { r.PlatformId, r.Key }, true); } public void RunInTransaction(Action body) { if (!_db.BeginTrans()) { throw new InvalidOperationException("Another transaction in progress"); } try { body(); _db.Commit(); } catch { _db.Rollback(); throw; } } public T Update<T>(ulong platformId, string key, Func<T, T> mutator, T defaultValue = default(T)) { lock (_sync) { T value; T arg = (TryGet<T>(platformId, key, out value) ? value : defaultValue); T val = mutator(arg); Set(platformId, key, val); return val; } } public void Set<T>(ulong platformId, string key, T value) { lock (_sync) { Record record = new Record(); record.Id = $"{platformId}:{key}"; record.PlatformId = platformId; record.Key = key; record.Value = _db.Mapper.Serialize(typeof(T), (object)value); Record record2 = record; _col.Upsert(record2); } } public Dictionary<ulong, T> GetAll<T>(string key) { lock (_sync) { Dictionary<ulong, T> dictionary = new Dictionary<ulong, T>(); foreach (Record item in _col.Find((Expression<Func<Record, bool>>)((Record r) => r.Key == key), 0, int.MaxValue)) { T value = (T)_db.Mapper.Deserialize(typeof(T), item.Value); dictionary[item.PlatformId] = value; } return dictionary; } } public bool TryGet<T>(ulong platformId, string key, out T value) { lock (_sync) { Record record = _col.FindOne((Expression<Func<Record, bool>>)((Record r) => r.PlatformId == platformId && r.Key == key)); if (record != null) { value = (T)_db.Mapper.Deserialize(typeof(T), record.Value); return true; } value = default(T); return false; } } public bool Delete(ulong platformId, string key) { lock (_sync) { return _col.DeleteMany((Expression<Func<Record, bool>>)((Record r) => r.PlatformId == platformId && r.Key == key)) > 0; } } public void Dispose() { LiteDatabase db = _db; if (db != null) { db.Dispose(); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "VampireDB"; public const string PLUGIN_NAME = "VampireDB"; public const string PLUGIN_VERSION = "0.1.10"; } }
LiteDB.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using LiteDB.Engine; using LiteDB.Utils; using LiteDB.Utils.Extensions; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("LiteDB.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010029e66990e22110ce40a7197e37f8f82df3332c399e696df7f27d09e14ee590ac2dda735d4777fe554c427540bde93b14d3d26c04731c963383dcaa18859c8cbcd4a1a9c394d1204f474c2ab6f23a2eaadf81eb8a7a3d3cc73658868b0302163b92a2614ca050ab703be33c3e1d76f55b11f4f87cb73558f3aa69c1ce726d9ee8")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Maurício David")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("MIT")] [assembly: AssemblyDescription("LiteDB - A lightweight embedded .NET NoSQL document store in a single datafile")] [assembly: AssemblyFileVersion("5.0.21")] [assembly: AssemblyInformationalVersion("5.0.21+391cc9318c5be6e56cb71b3ce14b1ed9cb324763")] [assembly: AssemblyProduct("LiteDB")] [assembly: AssemblyTitle("LiteDB")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/mbdavid/LiteDB")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("5.0.21.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LiteDB { public sealed class LiteCollection<T> : ILiteCollection<T> { private readonly string _collection; private readonly ILiteEngine _engine; private readonly List<BsonExpression> _includes; private readonly BsonMapper _mapper; private readonly EntityMapper _entity; private readonly MemberMapper _id; private readonly BsonAutoId _autoId; public string Name => _collection; public BsonAutoId AutoId => _autoId; public EntityMapper EntityMapper => _entity; public int Count() { return Query().Count(); } public int Count(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).Count(); } public int Count(string predicate, BsonDocument parameters) { return Count(BsonExpression.Create(predicate, parameters)); } public int Count(string predicate, params BsonValue[] args) { return Count(BsonExpression.Create(predicate, args)); } public int Count(Expression<Func<T, bool>> predicate) { return Count(_mapper.GetExpression(predicate)); } public int Count(Query query) { return new LiteQueryable<T>(_engine, _mapper, _collection, query).Count(); } public long LongCount() { return Query().LongCount(); } public long LongCount(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).LongCount(); } public long LongCount(string predicate, BsonDocument parameters) { return LongCount(BsonExpression.Create(predicate, parameters)); } public long LongCount(string predicate, params BsonValue[] args) { return LongCount(BsonExpression.Create(predicate, args)); } public long LongCount(Expression<Func<T, bool>> predicate) { return LongCount(_mapper.GetExpression(predicate)); } public long LongCount(Query query) { return new LiteQueryable<T>(_engine, _mapper, _collection, query).Count(); } public bool Exists(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).Exists(); } public bool Exists(string predicate, BsonDocument parameters) { return Exists(BsonExpression.Create(predicate, parameters)); } public bool Exists(string predicate, params BsonValue[] args) { return Exists(BsonExpression.Create(predicate, args)); } public bool Exists(Expression<Func<T, bool>> predicate) { return Exists(_mapper.GetExpression(predicate)); } public bool Exists(Query query) { return new LiteQueryable<T>(_engine, _mapper, _collection, query).Exists(); } public BsonValue Min(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } BsonDocument bsonDocument = Query().OrderBy(keySelector).Select(keySelector).ToDocuments() .First(); return bsonDocument[bsonDocument.Keys.First()]; } public BsonValue Min() { return Min("_id"); } public K Min<K>(Expression<Func<T, K>> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); BsonValue value = Min(expression); return (K)_mapper.Deserialize(typeof(K), value); } public BsonValue Max(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } BsonDocument bsonDocument = Query().OrderByDescending(keySelector).Select(keySelector).ToDocuments() .First(); return bsonDocument[bsonDocument.Keys.First()]; } public BsonValue Max() { return Max("_id"); } public K Max<K>(Expression<Func<T, K>> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); BsonValue value = Max(expression); return (K)_mapper.Deserialize(typeof(K), value); } public bool Delete(BsonValue id) { if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } return _engine.Delete(_collection, new BsonValue[1] { id }) == 1; } public int DeleteAll() { return _engine.DeleteMany(_collection, null); } public int DeleteMany(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return _engine.DeleteMany(_collection, predicate); } public int DeleteMany(string predicate, BsonDocument parameters) { return DeleteMany(BsonExpression.Create(predicate, parameters)); } public int DeleteMany(string predicate, params BsonValue[] args) { return DeleteMany(BsonExpression.Create(predicate, args)); } public int DeleteMany(Expression<Func<T, bool>> predicate) { return DeleteMany(_mapper.GetExpression(predicate)); } public ILiteQueryable<T> Query() { return new LiteQueryable<T>(_engine, _mapper, _collection, new Query()).Include(_includes); } public IEnumerable<T> Find(BsonExpression predicate, int skip = 0, int limit = int.MaxValue) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Include(_includes).Where(predicate).Skip(skip) .Limit(limit) .ToEnumerable(); } public IEnumerable<T> Find(Query query, int skip = 0, int limit = int.MaxValue) { if (query == null) { throw new ArgumentNullException("query"); } if (skip != 0) { query.Offset = skip; } if (limit != int.MaxValue) { query.Limit = limit; } return new LiteQueryable<T>(_engine, _mapper, _collection, query).ToEnumerable(); } public IEnumerable<T> Find(Expression<Func<T, bool>> predicate, int skip = 0, int limit = int.MaxValue) { return Find(_mapper.GetExpression(predicate), skip, limit); } public T FindById(BsonValue id) { if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } return Find(BsonExpression.Create("_id = @0", id)).FirstOrDefault(); } public T FindOne(BsonExpression predicate) { return Find(predicate).FirstOrDefault(); } public T FindOne(string predicate, BsonDocument parameters) { return FindOne(BsonExpression.Create(predicate, parameters)); } public T FindOne(BsonExpression predicate, params BsonValue[] args) { return FindOne(BsonExpression.Create(predicate, args)); } public T FindOne(Expression<Func<T, bool>> predicate) { return FindOne(_mapper.GetExpression(predicate)); } public T FindOne(Query query) { return Find(query).FirstOrDefault(); } public IEnumerable<T> FindAll() { return Query().Include(_includes).ToEnumerable(); } public ILiteCollection<T> Include<K>(Expression<Func<T, K>> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); return Include(expression); } public ILiteCollection<T> Include(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } LiteCollection<T> liteCollection = new LiteCollection<T>(_collection, _autoId, _engine, _mapper); liteCollection._includes.AddRange(_includes); liteCollection._includes.Add(keySelector); return liteCollection; } public bool EnsureIndex(string name, BsonExpression expression, bool unique = false) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } if (expression == null) { throw new ArgumentNullException("expression"); } return _engine.EnsureIndex(_collection, name, expression, unique); } public bool EnsureIndex(BsonExpression expression, bool unique = false) { if (expression == null) { throw new ArgumentNullException("expression"); } string name = Regex.Replace(expression.Source, "[^a-z0-9]", "", RegexOptions.IgnoreCase | RegexOptions.Compiled); return EnsureIndex(name, expression, unique); } public bool EnsureIndex<K>(Expression<Func<T, K>> keySelector, bool unique = false) { BsonExpression indexExpression = GetIndexExpression(keySelector); return EnsureIndex(indexExpression, unique); } public bool EnsureIndex<K>(string name, Expression<Func<T, K>> keySelector, bool unique = false) { BsonExpression indexExpression = GetIndexExpression(keySelector); return EnsureIndex(name, indexExpression, unique); } private BsonExpression GetIndexExpression<K>(Expression<Func<T, K>> keySelector) { BsonExpression bsonExpression = _mapper.GetIndexExpression(keySelector); if (typeof(K).IsEnumerable() && bsonExpression.IsScalar) { if (bsonExpression.Type != BsonExpressionType.Path) { throw new LiteException(0, "Expression `" + bsonExpression.Source + "` must return a enumerable expression"); } bsonExpression = bsonExpression.Source + "[*]"; } return bsonExpression; } public bool DropIndex(string name) { return _engine.DropIndex(_collection, name); } public BsonValue Insert(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bool flag = RemoveDocId(bsonDocument); _engine.Insert(_collection, new BsonDocument[1] { bsonDocument }, _autoId); BsonValue bsonValue = bsonDocument["_id"]; if (flag) { _id.Setter(entity, bsonValue.RawValue); } return bsonValue; } public void Insert(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; _engine.Insert(_collection, new BsonDocument[1] { bsonDocument }, _autoId); } public int Insert(IEnumerable<T> entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Insert(_collection, GetBsonDocs(entities), _autoId); } [Obsolete("Use normal Insert()")] public int InsertBulk(IEnumerable<T> entities, int batchSize = 5000) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Insert(_collection, GetBsonDocs(entities), _autoId); } private IEnumerable<BsonDocument> GetBsonDocs(IEnumerable<T> documents) { foreach (T document in documents) { BsonDocument doc = _mapper.ToDocument(document); bool removed = RemoveDocId(doc); yield return doc; if (removed && _id != null) { _id.Setter(document, doc["_id"].RawValue); } } } private bool RemoveDocId(BsonDocument doc) { if (_id != null && doc.TryGetValue("_id", out var value) && ((_autoId == BsonAutoId.Int32 && value.IsInt32 && value.AsInt32 == 0) || (_autoId == BsonAutoId.ObjectId && (value.IsNull || (value.IsObjectId && value.AsObjectId == ObjectId.Empty))) || (_autoId == BsonAutoId.Guid && value.IsGuid && value.AsGuid == Guid.Empty) || (_autoId == BsonAutoId.Int64 && value.IsInt64 && value.AsInt64 == 0L))) { doc.Remove("_id"); return true; } return false; } public bool Update(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); return _engine.Update(_collection, new BsonDocument[1] { bsonDocument }) > 0; } public bool Update(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; return _engine.Update(_collection, new BsonDocument[1] { bsonDocument }) > 0; } public int Update(IEnumerable<T> entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Update(_collection, entities.Select((T x) => _mapper.ToDocument(x))); } public int UpdateMany(BsonExpression transform, BsonExpression predicate) { if (transform == null) { throw new ArgumentNullException("transform"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } if (transform.Type != BsonExpressionType.Document) { throw new ArgumentException("Extend expression must return a document. Eg: `col.UpdateMany('{ Name: UPPER(Name) }', 'Age > 10')`"); } return _engine.UpdateMany(_collection, transform, predicate); } public int UpdateMany(Expression<Func<T, T>> extend, Expression<Func<T, bool>> predicate) { if (extend == null) { throw new ArgumentNullException("extend"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } BsonExpression expression = _mapper.GetExpression(extend); BsonExpression expression2 = _mapper.GetExpression(predicate); if (expression.Type != BsonExpressionType.Document) { throw new ArgumentException("Extend expression must return an anonymous class to be merge with entities. Eg: `col.UpdateMany(x => new { Name = x.Name.ToUpper() }, x => x.Age > 10)`"); } return _engine.UpdateMany(_collection, expression, expression2); } public bool Upsert(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } return Upsert(new T[1] { entity }) == 1; } public int Upsert(IEnumerable<T> entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Upsert(_collection, GetBsonDocs(entities), _autoId); } public bool Upsert(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; return _engine.Upsert(_collection, new BsonDocument[1] { bsonDocument }, _autoId) > 0; } internal LiteCollection(string name, BsonAutoId autoId, ILiteEngine engine, BsonMapper mapper) { _collection = name ?? mapper.ResolveCollectionName(typeof(T)); _engine = engine; _mapper = mapper; _includes = new List<BsonExpression>(); if (typeof(T) == typeof(BsonDocument)) { _entity = null; _id = null; _autoId = autoId; return; } _entity = mapper.GetEntityMapper(typeof(T)); _id = _entity.Id; if (_id != null && _id.AutoId) { _autoId = ((_id.DataType == typeof(int) || _id.DataType == typeof(int?)) ? BsonAutoId.Int32 : ((_id.DataType == typeof(long) || _id.DataType == typeof(long?)) ? BsonAutoId.Int64 : ((_id.DataType == typeof(Guid) || _id.DataType == typeof(Guid?)) ? BsonAutoId.Guid : BsonAutoId.ObjectId))); } else { _autoId = autoId; } } } public interface ILiteCollection<T> { string Name { get; } BsonAutoId AutoId { get; } EntityMapper EntityMapper { get; } ILiteCollection<T> Include<K>(Expression<Func<T, K>> keySelector); ILiteCollection<T> Include(BsonExpression keySelector); bool Upsert(T entity); int Upsert(IEnumerable<T> entities); bool Upsert(BsonValue id, T entity); bool Update(T entity); bool Update(BsonValue id, T entity); int Update(IEnumerable<T> entities); int UpdateMany(BsonExpression transform, BsonExpression predicate); int UpdateMany(Expression<Func<T, T>> extend, Expression<Func<T, bool>> predicate); BsonValue Insert(T entity); void Insert(BsonValue id, T entity); int Insert(IEnumerable<T> entities); int InsertBulk(IEnumerable<T> entities, int batchSize = 5000); bool EnsureIndex(string name, BsonExpression expression, bool unique = false); bool EnsureIndex(BsonExpression expression, bool unique = false); bool EnsureIndex<K>(Expression<Func<T, K>> keySelector, bool unique = false); bool EnsureIndex<K>(string name, Expression<Func<T, K>> keySelector, bool unique = false); bool DropIndex(string name); ILiteQueryable<T> Query(); IEnumerable<T> Find(BsonExpression predicate, int skip = 0, int limit = int.MaxValue); IEnumerable<T> Find(Query query, int skip = 0, int limit = int.MaxValue); IEnumerable<T> Find(Expression<Func<T, bool>> predicate, int skip = 0, int limit = int.MaxValue); T FindById(BsonValue id); T FindOne(BsonExpression predicate); T FindOne(string predicate, BsonDocument parameters); T FindOne(BsonExpression predicate, params BsonValue[] args); T FindOne(Expression<Func<T, bool>> predicate); T FindOne(Query query); IEnumerable<T> FindAll(); bool Delete(BsonValue id); int DeleteAll(); int DeleteMany(BsonExpression predicate); int DeleteMany(string predicate, BsonDocument parameters); int DeleteMany(string predicate, params BsonValue[] args); int DeleteMany(Expression<Func<T, bool>> predicate); int Count(); int Count(BsonExpression predicate); int Count(string predicate, BsonDocument parameters); int Count(string predicate, params BsonValue[] args); int Count(Expression<Func<T, bool>> predicate); int Count(Query query); long LongCount(); long LongCount(BsonExpression predicate); long LongCount(string predicate, BsonDocument parameters); long LongCount(string predicate, params BsonValue[] args); long LongCount(Expression<Func<T, bool>> predicate); long LongCount(Query query); bool Exists(BsonExpression predicate); bool Exists(string predicate, BsonDocument parameters); bool Exists(string predicate, params BsonValue[] args); bool Exists(Expression<Func<T, bool>> predicate); bool Exists(Query query); BsonValue Min(BsonExpression keySelector); BsonValue Min(); K Min<K>(Expression<Func<T, K>> keySelector); BsonValue Max(BsonExpression keySelector); BsonValue Max(); K Max<K>(Expression<Func<T, K>> keySelector); } public interface ILiteDatabase : IDisposable { BsonMapper Mapper { get; } ILiteStorage<string> FileStorage { get; } int UserVersion { get; set; } TimeSpan Timeout { get; set; } bool UtcDate { get; set; } long LimitSize { get; set; } int CheckpointSize { get; set; } Collation Collation { get; } ILiteCollection<T> GetCollection<T>(string name, BsonAutoId autoId = BsonAutoId.ObjectId); ILiteCollection<T> GetCollection<T>(); ILiteCollection<T> GetCollection<T>(BsonAutoId autoId); ILiteCollection<BsonDocument> GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId); bool BeginTrans(); bool Commit(); bool Rollback(); ILiteStorage<TFileId> GetStorage<TFileId>(string filesCollection = "_files", string chunksCollection = "_chunks"); IEnumerable<string> GetCollectionNames(); bool CollectionExists(string name); bool DropCollection(string name); bool RenameCollection(string oldName, string newName); IBsonDataReader Execute(TextReader commandReader, BsonDocument parameters = null); IBsonDataReader Execute(string command, BsonDocument parameters = null); IBsonDataReader Execute(string command, params BsonValue[] args); void Checkpoint(); long Rebuild(RebuildOptions options = null); BsonValue Pragma(string name); BsonValue Pragma(string name, BsonValue value); } public interface ILiteQueryable<T> : ILiteQueryableResult<T> { ILiteQueryable<T> Include(BsonExpression path); ILiteQueryable<T> Include(List<BsonExpression> paths); ILiteQueryable<T> Include<K>(Expression<Func<T, K>> path); ILiteQueryable<T> Where(BsonExpression predicate); ILiteQueryable<T> Where(string predicate, BsonDocument parameters); ILiteQueryable<T> Where(string predicate, params BsonValue[] args); ILiteQueryable<T> Where(Expression<Func<T, bool>> predicate); ILiteQueryable<T> OrderBy(BsonExpression keySelector, int order = 1); ILiteQueryable<T> OrderBy<K>(Expression<Func<T, K>> keySelector, int order = 1); ILiteQueryable<T> OrderByDescending(BsonExpression keySelector); ILiteQueryable<T> OrderByDescending<K>(Expression<Func<T, K>> keySelector); ILiteQueryable<T> GroupBy(BsonExpression keySelector); ILiteQueryable<T> Having(BsonExpression predicate); ILiteQueryableResult<BsonDocument> Select(BsonExpression selector); ILiteQueryableResult<K> Select<K>(Expression<Func<T, K>> selector); } public interface ILiteQueryableResult<T> { ILiteQueryableResult<T> Limit(int limit); ILiteQueryableResult<T> Skip(int offset); ILiteQueryableResult<T> Offset(int offset); ILiteQueryableResult<T> ForUpdate(); BsonDocument GetPlan(); IBsonDataReader ExecuteReader(); IEnumerable<BsonDocument> ToDocuments(); IEnumerable<T> ToEnumerable(); List<T> ToList(); T[] ToArray(); int Into(string newCollection, BsonAutoId autoId = BsonAutoId.ObjectId); T First(); T FirstOrDefault(); T Single(); T SingleOrDefault(); int Count(); long LongCount(); bool Exists(); } public interface ILiteRepository : IDisposable { ILiteDatabase Database { get; } BsonValue Insert<T>(T entity, string collectionName = null); int Insert<T>(IEnumerable<T> entities, string collectionName = null); bool Update<T>(T entity, string collectionName = null); int Update<T>(IEnumerable<T> entities, string collectionName = null); bool Upsert<T>(T entity, string collectionName = null); int Upsert<T>(IEnumerable<T> entities, string collectionName = null); bool Delete<T>(BsonValue id, string collectionName = null); int DeleteMany<T>(BsonExpression predicate, string collectionName = null); int DeleteMany<T>(Expression<Func<T, bool>> predicate, string collectionName = null); ILiteQueryable<T> Query<T>(string collectionName = null); bool EnsureIndex<T>(string name, BsonExpression expression, bool unique = false, string collectionName = null); bool EnsureIndex<T>(BsonExpression expression, bool unique = false, string collectionName = null); bool EnsureIndex<T, K>(Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null); bool EnsureIndex<T, K>(string name, Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null); T SingleById<T>(BsonValue id, string collectionName = null); List<T> Fetch<T>(BsonExpression predicate, string collectionName = null); List<T> Fetch<T>(Expression<Func<T, bool>> predicate, string collectionName = null); T First<T>(BsonExpression predicate, string collectionName = null); T First<T>(Expression<Func<T, bool>> predicate, string collectionName = null); T FirstOrDefault<T>(BsonExpression predicate, string collectionName = null); T FirstOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null); T Single<T>(BsonExpression predicate, string collectionName = null); T Single<T>(Expression<Func<T, bool>> predicate, string collectionName = null); T SingleOrDefault<T>(BsonExpression predicate, string collectionName = null); T SingleOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null); } public class LiteDatabase : ILiteDatabase, IDisposable { private readonly ILiteEngine _engine; private readonly BsonMapper _mapper; private readonly bool _disposeOnClose; private ILiteStorage<string> _fs; public BsonMapper Mapper => _mapper; public ILiteStorage<string> FileStorage => _fs ?? (_fs = GetStorage<string>()); public int UserVersion { get { return _engine.Pragma("USER_VERSION"); } set { _engine.Pragma("USER_VERSION", value); } } public TimeSpan Timeout { get { return TimeSpan.FromSeconds(_engine.Pragma("TIMEOUT").AsInt32); } set { _engine.Pragma("TIMEOUT", (int)value.TotalSeconds); } } public bool UtcDate { get { return _engine.Pragma("UTC_DATE"); } set { _engine.Pragma("UTC_DATE", value); } } public long LimitSize { get { return _engine.Pragma("LIMIT_SIZE"); } set { _engine.Pragma("LIMIT_SIZE", value); } } public int CheckpointSize { get { return _engine.Pragma("CHECKPOINT"); } set { _engine.Pragma("CHECKPOINT", value); } } public Collation Collation => new Collation(_engine.Pragma("COLLATION").AsString); public LiteDatabase(string connectionString, BsonMapper mapper = null) : this(new ConnectionString(connectionString), mapper) { } public LiteDatabase(ConnectionString connectionString, BsonMapper mapper = null) { if (connectionString == null) { throw new ArgumentNullException("connectionString"); } _engine = connectionString.CreateEngine(); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = true; } public LiteDatabase(Stream stream, BsonMapper mapper = null, Stream logStream = null) { EngineSettings settings = new EngineSettings { DataStream = (stream ?? throw new ArgumentNullException("stream")), LogStream = logStream }; _engine = new LiteEngine(settings); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = true; } public LiteDatabase(ILiteEngine engine, BsonMapper mapper = null, bool disposeOnClose = true) { _engine = engine ?? throw new ArgumentNullException("engine"); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = disposeOnClose; } public ILiteCollection<T> GetCollection<T>(string name, BsonAutoId autoId = BsonAutoId.ObjectId) { return new LiteCollection<T>(name, autoId, _engine, _mapper); } public ILiteCollection<T> GetCollection<T>() { return GetCollection<T>(null); } public ILiteCollection<T> GetCollection<T>(BsonAutoId autoId) { return GetCollection<T>(null, autoId); } public ILiteCollection<BsonDocument> GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return new LiteCollection<BsonDocument>(name, autoId, _engine, _mapper); } public bool BeginTrans() { return _engine.BeginTrans(); } public bool Commit() { return _engine.Commit(); } public bool Rollback() { return _engine.Rollback(); } public ILiteStorage<TFileId> GetStorage<TFileId>(string filesCollection = "_files", string chunksCollection = "_chunks") { return new LiteStorage<TFileId>(this, filesCollection, chunksCollection); } public IEnumerable<string> GetCollectionNames() { return (from x in GetCollection("$cols").Query().Where("type = 'user'").ToDocuments() select x["name"].AsString).ToArray(); } public bool CollectionExists(string name) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return GetCollectionNames().Contains<string>(name, StringComparer.OrdinalIgnoreCase); } public bool DropCollection(string name) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return _engine.DropCollection(name); } public bool RenameCollection(string oldName, string newName) { if (oldName.IsNullOrWhiteSpace()) { throw new ArgumentNullException("oldName"); } if (newName.IsNullOrWhiteSpace()) { throw new ArgumentNullException("newName"); } return _engine.RenameCollection(oldName, newName); } public IBsonDataReader Execute(TextReader commandReader, BsonDocument parameters = null) { if (commandReader == null) { throw new ArgumentNullException("commandReader"); } Tokenizer tokenizer = new Tokenizer(commandReader); return new SqlParser(_engine, tokenizer, parameters).Execute(); } public IBsonDataReader Execute(string command, BsonDocument parameters = null) { if (command == null) { throw new ArgumentNullException("command"); } Tokenizer tokenizer = new Tokenizer(command); return new SqlParser(_engine, tokenizer, parameters).Execute(); } public IBsonDataReader Execute(string command, params BsonValue[] args) { BsonDocument bsonDocument = new BsonDocument(); int num = 0; foreach (BsonValue value in args) { bsonDocument[num.ToString()] = value; num++; } return Execute(command, bsonDocument); } public void Checkpoint() { _engine.Checkpoint(); } public long Rebuild(RebuildOptions options = null) { return _engine.Rebuild(options ?? new RebuildOptions()); } public BsonValue Pragma(string name) { return _engine.Pragma(name); } public BsonValue Pragma(string name, BsonValue value) { return _engine.Pragma(name, value); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~LiteDatabase() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (disposing && _disposeOnClose) { _engine.Dispose(); } } } public class LiteQueryable<T> : ILiteQueryable<T>, ILiteQueryableResult<T> { protected readonly ILiteEngine _engine; protected readonly BsonMapper _mapper; protected readonly string _collection; protected readonly Query _query; private readonly bool _isSimpleType = Reflection.IsSimpleType(typeof(T)); internal LiteQueryable(ILiteEngine engine, BsonMapper mapper, string collection, Query query) { _engine = engine; _mapper = mapper; _collection = collection; _query = query; } public ILiteQueryable<T> Include<K>(Expression<Func<T, K>> path) { _query.Includes.Add(_mapper.GetExpression(path)); return this; } public ILiteQueryable<T> Include(BsonExpression path) { _query.Includes.Add(path); return this; } public ILiteQueryable<T> Include(List<BsonExpression> paths) { _query.Includes.AddRange(paths); return this; } public ILiteQueryable<T> Where(BsonExpression predicate) { _query.Where.Add(predicate); return this; } public ILiteQueryable<T> Where(string predicate, BsonDocument parameters) { _query.Where.Add(BsonExpression.Create(predicate, parameters)); return this; } public ILiteQueryable<T> Where(string predicate, params BsonValue[] args) { _query.Where.Add(BsonExpression.Create(predicate, args)); return this; } public ILiteQueryable<T> Where(Expression<Func<T, bool>> predicate) { return Where(_mapper.GetExpression(predicate)); } public ILiteQueryable<T> OrderBy(BsonExpression keySelector, int order = 1) { if (_query.OrderBy != null) { throw new ArgumentException("ORDER BY already defined in this query builder"); } _query.OrderBy = keySelector; _query.Order = order; return this; } public ILiteQueryable<T> OrderBy<K>(Expression<Func<T, K>> keySelector, int order = 1) { return OrderBy(_mapper.GetExpression(keySelector), order); } public ILiteQueryable<T> OrderByDescending(BsonExpression keySelector) { return OrderBy(keySelector, -1); } public ILiteQueryable<T> OrderByDescending<K>(Expression<Func<T, K>> keySelector) { return OrderBy(keySelector, -1); } public ILiteQueryable<T> GroupBy(BsonExpression keySelector) { if (_query.GroupBy != null) { throw new ArgumentException("GROUP BY already defined in this query"); } _query.GroupBy = keySelector; return this; } public ILiteQueryable<T> Having(BsonExpression predicate) { if (_query.Having != null) { throw new ArgumentException("HAVING already defined in this query"); } _query.Having = predicate; return this; } public ILiteQueryableResult<BsonDocument> Select(BsonExpression selector) { _query.Select = selector; return new LiteQueryable<BsonDocument>(_engine, _mapper, _collection, _query); } public ILiteQueryableResult<K> Select<K>(Expression<Func<T, K>> selector) { if (_query.GroupBy != null) { throw new ArgumentException("Use Select(BsonExpression selector) when using GroupBy query"); } _query.Select = _mapper.GetExpression(selector); return new LiteQueryable<K>(_engine, _mapper, _collection, _query); } public ILiteQueryableResult<T> ForUpdate() { _query.ForUpdate = true; return this; } public ILiteQueryableResult<T> Offset(int offset) { _query.Offset = offset; return this; } public ILiteQueryableResult<T> Skip(int offset) { return Offset(offset); } public ILiteQueryableResult<T> Limit(int limit) { _query.Limit = limit; return this; } public IBsonDataReader ExecuteReader() { _query.ExplainPlan = false; return _engine.Query(_collection, _query); } public IEnumerable<BsonDocument> ToDocuments() { using IBsonDataReader reader = ExecuteReader(); while (reader.Read()) { yield return reader.Current as BsonDocument; } } public IEnumerable<T> ToEnumerable() { if (_isSimpleType) { return from x in ToDocuments() select x[x.Keys.First()] into x select (T)_mapper.Deserialize(typeof(T), x); } return from x in ToDocuments() select (T)_mapper.Deserialize(typeof(T), x); } public List<T> ToList() { return ToEnumerable().ToList(); } public T[] ToArray() { return ToEnumerable().ToArray(); } public BsonDocument GetPlan() { _query.ExplainPlan = true; return _engine.Query(_collection, _query).ToEnumerable().FirstOrDefault()?.AsDocument; } public T Single() { return ToEnumerable().Single(); } public T SingleOrDefault() { return ToEnumerable().SingleOrDefault(); } public T First() { return ToEnumerable().First(); } public T FirstOrDefault() { return ToEnumerable().FirstOrDefault(); } public int Count() { BsonExpression select = _query.Select; try { Select("{ count: COUNT(*._id) }"); return ToDocuments().Single()["count"].AsInt32; } finally { _query.Select = select; } } public long LongCount() { BsonExpression select = _query.Select; try { Select("{ count: COUNT(*._id) }"); return ToDocuments().Single()["count"].AsInt64; } finally { _query.Select = select; } } public bool Exists() { BsonExpression select = _query.Select; try { Select("{ exists: ANY(*._id) }"); return ToDocuments().Single()["exists"].AsBoolean; } finally { _query.Select = select; } } public int Into(string newCollection, BsonAutoId autoId = BsonAutoId.ObjectId) { _query.Into = newCollection; _query.IntoAutoId = autoId; using IBsonDataReader bsonDataReader = ExecuteReader(); return bsonDataReader.Current.AsInt32; } } public class LiteRepository : ILiteRepository, IDisposable { private readonly ILiteDatabase _db; public ILiteDatabase Database => _db; public LiteRepository(ILiteDatabase database) { _db = database; } public LiteRepository(string connectionString, BsonMapper mapper = null) { _db = new LiteDatabase(connectionString, mapper); } public LiteRepository(ConnectionString connectionString, BsonMapper mapper = null) { _db = new LiteDatabase(connectionString, mapper); } public LiteRepository(Stream stream, BsonMapper mapper = null, Stream logStream = null) { _db = new LiteDatabase(stream, mapper, logStream); } public BsonValue Insert<T>(T entity, string collectionName = null) { return _db.GetCollection<T>(collectionName).Insert(entity); } public int Insert<T>(IEnumerable<T> entities, string collectionName = null) { return _db.GetCollection<T>(collectionName).Insert(entities); } public bool Update<T>(T entity, string collectionName = null) { return _db.GetCollection<T>(collectionName).Update(entity); } public int Update<T>(IEnumerable<T> entities, string collectionName = null) { return _db.GetCollection<T>(collectionName).Update(entities); } public bool Upsert<T>(T entity, string collectionName = null) { return _db.GetCollection<T>(collectionName).Upsert(entity); } public int Upsert<T>(IEnumerable<T> entities, string collectionName = null) { return _db.GetCollection<T>(collectionName).Upsert(entities); } public bool Delete<T>(BsonValue id, string collectionName = null) { return _db.GetCollection<T>(collectionName).Delete(id); } public int DeleteMany<T>(BsonExpression predicate, string collectionName = null) { return _db.GetCollection<T>(collectionName).DeleteMany(predicate); } public int DeleteMany<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return _db.GetCollection<T>(collectionName).DeleteMany(predicate); } public ILiteQueryable<T> Query<T>(string collectionName = null) { return _db.GetCollection<T>(collectionName).Query(); } public bool EnsureIndex<T>(string name, BsonExpression expression, bool unique = false, string collectionName = null) { return _db.GetCollection<T>(collectionName).EnsureIndex(name, expression, unique); } public bool EnsureIndex<T>(BsonExpression expression, bool unique = false, string collectionName = null) { return _db.GetCollection<T>(collectionName).EnsureIndex(expression, unique); } public bool EnsureIndex<T, K>(Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null) { return _db.GetCollection<T>(collectionName).EnsureIndex(keySelector, unique); } public bool EnsureIndex<T, K>(string name, Expression<Func<T, K>> keySelector, bool unique = false, string collectionName = null) { return _db.GetCollection<T>(collectionName).EnsureIndex(name, keySelector, unique); } public T SingleById<T>(BsonValue id, string collectionName = null) { return _db.GetCollection<T>(collectionName).Query().Where("_id = @0", id) .Single(); } public List<T> Fetch<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).ToList(); } public List<T> Fetch<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).ToList(); } public T First<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).First(); } public T First<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).First(); } public T FirstOrDefault<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).FirstOrDefault(); } public T FirstOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).FirstOrDefault(); } public T Single<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).Single(); } public T Single<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).Single(); } public T SingleOrDefault<T>(BsonExpression predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).SingleOrDefault(); } public T SingleOrDefault<T>(Expression<Func<T, bool>> predicate, string collectionName = null) { return Query<T>(collectionName).Where(predicate).SingleOrDefault(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~LiteRepository() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (disposing) { _db.Dispose(); } } } public class BsonCtorAttribute : Attribute { } public class BsonFieldAttribute : Attribute { public string Name { get; set; } public BsonFieldAttribute(string name) { Name = name; } public BsonFieldAttribute() { } } public class BsonIdAttribute : Attribute { public bool AutoId { get; private set; } public BsonIdAttribute() { AutoId = true; } public BsonIdAttribute(bool autoId) { AutoId = autoId; } } public class BsonIgnoreAttribute : Attribute { } public class BsonRefAttribute : Attribute { public string Collection { get; set; } public BsonRefAttribute(string collection) { Collection = collection; } public BsonRefAttribute() { Collection = null; } } public class BsonMapper { public delegate BsonValue DeserializationCallback(BsonMapper sender, Type target, BsonValue value); private readonly Dictionary<Type, EntityMapper> _entities = new Dictionary<Type, EntityMapper>(); private readonly ConcurrentDictionary<Type, Func<object, BsonValue>> _customSerializer = new ConcurrentDictionary<Type, Func<object, BsonValue>>(); private readonly ConcurrentDictionary<Type, Func<BsonValue, object>> _customDeserializer = new ConcurrentDictionary<Type, Func<BsonValue, object>>(); private readonly Func<Type, object> _typeInstantiator; private readonly ITypeNameBinder _typeNameBinder; public static BsonMapper Global = new BsonMapper(); public Func<string, string> ResolveFieldName; public Action<Type, MemberInfo, MemberMapper> ResolveMember; public Func<Type, string> ResolveCollectionName; private readonly Regex _lowerCaseDelimiter = new Regex("(?!(^[A-Z]))([A-Z])", RegexOptions.Compiled); private readonly HashSet<Type> _bsonTypes = new HashSet<Type> { typeof(string), typeof(int), typeof(long), typeof(bool), typeof(Guid), typeof(DateTime), typeof(byte[]), typeof(ObjectId), typeof(double), typeof(decimal) }; private readonly HashSet<Type> _basicTypes = new HashSet<Type> { typeof(short), typeof(ushort), typeof(uint), typeof(float), typeof(char), typeof(byte), typeof(sbyte) }; public bool SerializeNullValues { get; set; } public bool TrimWhitespace { get; set; } public bool EmptyStringToNull { get; set; } public bool EnumAsInteger { get; set; } public bool IncludeFields { get; set; } public bool IncludeNonPublic { get; set; } public int MaxDepth { get; set; } public DeserializationCallback? OnDeserialization { get; set; } public BsonMapper(Func<Type, object> customTypeInstantiator = null, ITypeNameBinder typeNameBinder = null) { SerializeNullValues = false; TrimWhitespace = true; EmptyStringToNull = true; EnumAsInteger = false; ResolveFieldName = (string s) => s; ResolveMember = delegate { }; ResolveCollectionName = (Type t) => (!Reflection.IsEnumerable(t)) ? t.Name : Reflection.GetListItemType(t).Name; IncludeFields = false; MaxDepth = 20; _typeInstantiator = customTypeInstantiator ?? ((Func<Type, object>)((Type t) => null)); _typeNameBinder = typeNameBinder ?? DefaultTypeNameBinder.Instance; RegisterType((Uri uri) => uri.IsAbsoluteUri ? uri.AbsoluteUri : uri.ToString(), (BsonValue bson) => new Uri(bson.AsString)); RegisterType((DateTimeOffset value) => new BsonValue(value.UtcDateTime), (BsonValue bson) => bson.AsDateTime.ToUniversalTime()); RegisterType((TimeSpan value) => new BsonValue(value.Ticks), (BsonValue bson) => new TimeSpan(bson.AsInt64)); RegisterType((Regex r) => (r.Options != 0) ? new BsonDocument { { "p", r.ToString() }, { "o", (int)r.Options } } : new BsonValue(r.ToString()), (BsonValue value) => (!value.IsString) ? new Regex(value.AsDocument["p"].AsString, (RegexOptions)value.AsDocument["o"].AsInt32) : new Regex(value)); } public void RegisterType<T>(Func<T, BsonValue> serialize, Func<BsonValue, T> deserialize) { _customSerializer[typeof(T)] = (object o) => serialize((T)o); _customDeserializer[typeof(T)] = (BsonValue b) => deserialize(b); } public void RegisterType(Type type, Func<object, BsonValue> serialize, Func<BsonValue, object> deserialize) { _customSerializer[type] = (object o) => serialize(o); _customDeserializer[type] = (BsonValue b) => deserialize(b); } public EntityBuilder<T> Entity<T>() { return new EntityBuilder<T>(this, _typeNameBinder); } public BsonExpression GetExpression<T, K>(Expression<Func<T, K>> predicate) { return new LinqExpressionVisitor(this, predicate).Resolve(typeof(K) == typeof(bool)); } public BsonExpression GetIndexExpression<T, K>(Expression<Func<T, K>> predicate) { return new LinqExpressionVisitor(this, predicate).Resolve(predicate: false); } public BsonMapper UseCamelCase() { ResolveFieldName = (string s) => char.ToLower(s[0]) + s.Substring(1); return this; } public BsonMapper UseLowerCaseDelimiter(char delimiter = '_') { ResolveFieldName = (string s) => _lowerCaseDelimiter.Replace(s, delimiter + "$2").ToLower(); return this; } internal EntityMapper GetEntityMapper(Type type) { if (!_entities.TryGetValue(type, out var value)) { lock (_entities) { if (!_entities.TryGetValue(type, out value)) { return BuildAddEntityMapper(type); } } } return value; } protected virtual EntityMapper BuildAddEntityMapper(Type type) { EntityMapper entityMapper = new EntityMapper(type); _entities[type] = entityMapper; Type typeFromHandle = typeof(BsonIdAttribute); Type typeFromHandle2 = typeof(BsonIgnoreAttribute); Type typeFromHandle3 = typeof(BsonFieldAttribute); Type typeFromHandle4 = typeof(BsonRefAttribute); IEnumerable<MemberInfo> typeMembers = GetTypeMembers(type); MemberInfo idMember = GetIdMember(typeMembers); foreach (MemberInfo item in typeMembers) { if (!CustomAttributeExtensions.IsDefined(item, typeFromHandle2, inherit: true)) { string name = ResolveFieldName(item.Name); BsonFieldAttribute bsonFieldAttribute = (BsonFieldAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle3, inherit: true).FirstOrDefault(); if (bsonFieldAttribute != null && bsonFieldAttribute.Name != null) { name = bsonFieldAttribute.Name; } if (item == idMember) { name = "_id"; } GenericGetter getter = Reflection.CreateGenericGetter(type, item); GenericSetter setter = Reflection.CreateGenericSetter(type, item); BsonIdAttribute bsonIdAttribute = (BsonIdAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle, inherit: true).FirstOrDefault(); Type type2 = ((item is PropertyInfo) ? (item as PropertyInfo).PropertyType : (item as FieldInfo).FieldType); bool flag = Reflection.IsEnumerable(type2); MemberMapper memberMapper = new MemberMapper { AutoId = (bsonIdAttribute?.AutoId ?? true), FieldName = name, MemberName = item.Name, DataType = type2, IsEnumerable = flag, UnderlyingType = (flag ? Reflection.GetListItemType(type2) : type2), Getter = getter, Setter = setter }; BsonRefAttribute bsonRefAttribute = (BsonRefAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle4, inherit: false).FirstOrDefault(); if (bsonRefAttribute != null && item is PropertyInfo) { RegisterDbRef(this, memberMapper, _typeNameBinder, bsonRefAttribute.Collection ?? ResolveCollectionName((item as PropertyInfo).PropertyType)); } ResolveMember?.Invoke(type, item, memberMapper); if (memberMapper.FieldName != null && !entityMapper.Members.Any((MemberMapper x) => x.FieldName.Equals(name, StringComparison.OrdinalIgnoreCase)) && !memberMapper.IsIgnore) { entityMapper.Members.Add(memberMapper); } } } return entityMapper; } protected virtual MemberInfo GetIdMember(IEnumerable<MemberInfo> members) { return Reflection.SelectMember(members, (MemberInfo x) => CustomAttributeExtensions.IsDefined(x, typeof(BsonIdAttribute), inherit: true), (MemberInfo x) => x.Name.Equals("Id", StringComparison.OrdinalIgnoreCase), (MemberInfo x) => x.Name.Equals(x.DeclaringType.Name + "Id", StringComparison.OrdinalIgnoreCase)); } protected virtual IEnumerable<MemberInfo> GetTypeMembers(Type type) { List<MemberInfo> list = new List<MemberInfo>(); BindingFlags bindingAttr = (IncludeNonPublic ? (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public)); list.AddRange((from x in type.GetProperties(bindingAttr) where x.CanRead && x.GetIndexParameters().Length == 0 select x).Select((Func<PropertyInfo, MemberInfo>)((PropertyInfo x) => x))); if (IncludeFields) { list.AddRange((from x in type.GetFields(bindingAttr) where !x.Name.EndsWith("k__BackingField") && !x.IsStatic select x).Select((Func<FieldInfo, MemberInfo>)((FieldInfo x) => x))); } return list; } protected virtual CreateObject GetTypeCtor(EntityMapper mapper) { Type type = mapper.ForType; List<CreateObject> list = new List<CreateObject>(); bool flag = false; ConstructorInfo[] constructors = type.GetConstructors(); foreach (ConstructorInfo constructorInfo in constructors) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (parameters.Length == 0) { flag = true; continue; } KeyValuePair<string, Type>[] paramMap = new KeyValuePair<string, Type>[parameters.Length]; int j; for (j = 0; j < parameters.Length; j++) { ParameterInfo parameterInfo = parameters[j]; MemberMapper memberMapper = null; foreach (MemberMapper member in mapper.Members) { if (member.MemberName.ToLower() == parameterInfo.Name.ToLower() && member.DataType == parameterInfo.ParameterType) { memberMapper = member; break; } } if (memberMapper == null) { break; } paramMap[j] = new KeyValuePair<string, Type>(memberMapper.FieldName, memberMapper.DataType); } if (j < parameters.Length) { continue; } CreateObject createObject = (BsonDocument value) => Activator.CreateInstance(type, paramMap.Select((KeyValuePair<string, Type> x) => Deserialize(x.Value, value[x.Key])).ToArray()); if (constructorInfo.GetCustomAttribute<BsonCtorAttribute>() != null) { return createObject; } list.Add(createObject); } if (flag) { return null; } return list.FirstOrDefault(); } internal static void RegisterDbRef(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { member.IsDbRef = true; if (member.IsEnumerable) { RegisterDbRefList(mapper, member, typeNameBinder, collection); } else { RegisterDbRefItem(mapper, member, typeNameBinder, collection); } } private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { EntityMapper entity = mapper.GetEntityMapper(member.DataType); member.Serialize = delegate(object obj, BsonMapper m) { if (obj == null) { return BsonValue.Null; } object obj2 = (entity.Id ?? throw new LiteException(0, "There is no _id field mapped in your type: " + member.DataType.FullName)).Getter(obj); BsonDocument bsonDocument = new BsonDocument { ["$id"] = m.Serialize(obj2.GetType(), obj2, 0), ["$ref"] = collection }; if (member.DataType != obj.GetType()) { bsonDocument["$type"] = typeNameBinder.GetName(obj.GetType()); } return bsonDocument; }; member.Deserialize = delegate(BsonValue bson, BsonMapper m) { if (bson == null || !bson.IsDocument) { return null; } BsonDocument asDocument = bson.AsDocument; BsonValue value = asDocument["$id"]; bool num = asDocument["$missing"] == true; bool flag = !asDocument.ContainsKey("$ref"); if (num) { return null; } if (flag) { asDocument["_id"] = value; if (asDocument.ContainsKey("$type")) { asDocument["_type"] = bson["$type"]; } return m.Deserialize(entity.ForType, asDocument); } return m.Deserialize(entity.ForType, asDocument.ContainsKey("$type") ? new BsonDocument { ["_id"] = value, ["_type"] = bson["$type"] } : new BsonDocument { ["_id"] = value }); }; } private static void RegisterDbRefList(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { EntityMapper entity = mapper.GetEntityMapper(member.UnderlyingType); member.Serialize = delegate(object list, BsonMapper m) { if (list == null) { return BsonValue.Null; } BsonArray bsonArray2 = new BsonArray(); MemberMapper id = entity.Id; foreach (object item in (IEnumerable)list) { if (item != null) { object obj = id.Getter(item); BsonDocument bsonDocument2 = new BsonDocument { ["$id"] = m.Serialize(obj.GetType(), obj, 0), ["$ref"] = collection }; if (member.UnderlyingType != item.GetType()) { bsonDocument2["$type"] = typeNameBinder.GetName(item.GetType()); } bsonArray2.Add(bsonDocument2); } } return bsonArray2; }; member.Deserialize = delegate(BsonValue bson, BsonMapper m) { if (!bson.IsArray) { return null; } BsonArray asArray = bson.AsArray; if (asArray.Count == 0) { return m.Deserialize(member.DataType, asArray); } BsonArray bsonArray = new BsonArray(); foreach (BsonValue item2 in asArray) { if (item2.IsDocument) { BsonDocument asDocument = item2.AsDocument; BsonValue value = asDocument["$id"]; bool flag = asDocument["$missing"] == true; bool flag2 = !asDocument.ContainsKey("$ref"); if (!flag) { if (flag2) { item2["_id"] = value; if (item2.AsDocument.ContainsKey("$type")) { item2["_type"] = item2["$type"]; } bsonArray.Add(item2); } else { BsonDocument bsonDocument = new BsonDocument { ["_id"] = value }; if (item2.AsDocument.ContainsKey("$type")) { bsonDocument["_type"] = item2["$type"]; } bsonArray.Add(bsonDocument); } } } } return m.Deserialize(member.DataType, bsonArray); }; } public virtual object ToObject(Type type, BsonDocument doc) { if (doc == null) { throw new ArgumentNullException("doc"); } if (type == typeof(BsonDocument)) { return doc; } return Deserialize(type, doc); } public virtual T ToObject<T>(BsonDocument doc) { return (T)ToObject(typeof(T), doc); } public T Deserialize<T>(BsonValue value) { if (value == null) { return default(T); } return (T)Deserialize(typeof(T), value); } public object Deserialize(Type type, BsonValue value) { if (OnDeserialization != null) { BsonValue bsonValue = OnDeserialization(this, type, value); if ((object)bsonValue != null) { value = bsonValue; } } if (value.IsNull) { return null; } if (Reflection.IsNullable(type)) { type = Reflection.UnderlyingTypeOf(type); } if (_customDeserializer.TryGetValue(type, out var value2)) { return value2(value); } TypeInfo typeInfo = type.GetTypeInfo(); if (type == typeof(BsonValue)) { return value; } if (type == typeof(BsonDocument)) { return value.AsDocument; } if (type == typeof(BsonArray)) { return value.AsArray; } if (_bsonTypes.Contains(type)) { return value.RawValue; } if (_basicTypes.Contains(type)) { return Convert.ChangeType(value.RawValue, type); } if (type == typeof(ulong)) { return (ulong)value.AsInt64; } if (typeInfo.IsEnum) { if (value.IsString) { return Enum.Parse(type, value.AsString); } if (value.IsNumber) { return Enum.ToObject(type, value.AsInt32); } } else { if (value.IsArray) { if (type == typeof(object)) { return DeserializeArray(typeof(object), value.AsArray); } if (type.IsArray) { return DeserializeArray(type.GetElementType(), value.AsArray); } return DeserializeList(type, value.AsArray); } if (value.IsDocument) { if (type.IsAnonymousType()) { return DeserializeAnonymousType(type, value.AsDocument); } BsonDocument asDocument = value.AsDocument; if (asDocument.TryGetValue("_type", out var value3) && value3.IsString) { Type type2 = _typeNameBinder.GetType(value3.AsString); if (type2 == null) { throw LiteException.InvalidTypedName(value3.AsString); } if (!type.IsAssignableFrom(type2)) { throw LiteException.DataTypeNotAssignable(type.FullName, type2.FullName); } type = type2; } else if (type == typeof(object)) { type = typeof(Dictionary<string, object>); } EntityMapper entity = GetEntityMapper(type); if (entity.CreateInstance == null) { entity.CreateInstance = GetTypeCtor(entity) ?? ((CreateObject)((BsonDocument v) => Reflection.CreateInstance(entity.ForType))); } object obj = _typeInstantiator(type) ?? entity.CreateInstance(asDocument); if (obj is IDictionary dict) { if (obj.GetType().GetTypeInfo().IsGenericType) { Type k = type.GetGenericArguments()[0]; Type t = type.GetGenericArguments()[1]; DeserializeDictionary(k, t, dict, value.AsDocument); } else { DeserializeDictionary(typeof(object), typeof(object), dict, value.AsDocument); } } else { DeserializeObject(entity, obj, asDocument); } return obj; } } return value.RawValue; } private object DeserializeArray(Type type, BsonArray array) { Array array2 = Array.CreateInstance(type, array.Count); int num = 0; foreach (BsonValue item in array) { array2.SetValue(Deserialize(type, item), num++); } return array2; } private object DeserializeList(Type type, BsonArray value) { Type listItemType = Reflection.GetListItemType(type); IEnumerable enumerable = (IEnumerable)Reflection.CreateInstance(type); if (enumerable is IList list) { foreach (BsonValue item in value) { list.Add(Deserialize(listItemType, item)); } } else { MethodInfo method = type.GetMethod("Add", new Type[1] { listItemType }); foreach (BsonValue item2 in value) { method.Invoke(enumerable, new object[1] { Deserialize(listItemType, item2) }); } } return enumerable; } private void DeserializeDictionary(Type K, Type T, IDictionary dict, BsonDocument value) { bool isEnum = K.GetTypeInfo().IsEnum; foreach (KeyValuePair<string, BsonValue> element in value.GetElements()) { object key = (isEnum ? Enum.Parse(K, element.Key) : ((K == typeof(Uri)) ? new Uri(element.Key) : Convert.ChangeType(element.Key, K))); object value2 = Deserialize(T, element.Value); dict.Add(key, value2); } } private void DeserializeObject(EntityMapper entity, object obj, BsonDocument value) { foreach (MemberMapper item in entity.Members.Where((MemberMapper x) => x.Setter != null)) { if (value.TryGetValue(item.FieldName, out var value2)) { if (item.Deserialize != null) { item.Setter(obj, item.Deserialize(value2, this)); } else { item.Setter(obj, Deserialize(item.DataType, value2)); } } } } private object DeserializeAnonymousType(Type type, BsonDocument value) { List<object> list = new List<object>(); ParameterInfo[] parameters = type.GetConstructors()[0].GetParameters(); foreach (ParameterInfo parameterInfo in parameters) { object obj = Deserialize(parameterInfo.ParameterType, value[parameterInfo.Name]); if (obj == null && StringComparer.OrdinalIgnoreCase.Equals(parameterInfo.Name, "Id") && value.TryGetValue("_id", out var value2)) { obj = Deserialize(parameterInfo.ParameterType, value2); } list.Add(obj); } return Activator.CreateInstance(type, list.ToArray()); } public virtual BsonDocument ToDocument(Type type, object entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (entity is BsonDocument) { return (BsonDocument)entity; } return Serialize(type, entity, 0).AsDocument; } public virtual BsonDocument ToDocument<T>(T entity) { return ToDocument(typeof(T), entity)?.AsDocument; } public BsonValue Serialize<T>(T obj) { return Serialize(typeof(T), obj, 0); } public BsonValue Serialize(Type type, object obj) { return Serialize(type, obj, 0); } internal BsonValue Serialize(Type type, object obj, int depth) { if (++depth > MaxDepth) { throw LiteException.DocumentMaxDepth(MaxDepth, type); } if (obj == null) { return BsonValue.Null; } if (obj is BsonValue result) { return result; } if (_customSerializer.TryGetValue(type, out var value) || _customSerializer.TryGetValue(obj.GetType(), out value)) { return value(obj); } if (obj is string) { string text = (TrimWhitespace ? (obj as string).Trim() : ((string)obj)); if (EmptyStringToNull && text.Length == 0) { return BsonValue.Null; } return new BsonValue(text); } if (obj is int) { return new BsonValue((int)obj); } if (obj is long) { return new BsonValue((long)obj); } if (obj is double) { return new BsonValue((double)obj); } if (obj is decimal) { return new BsonValue((decimal)obj); } if (obj is byte[]) { return new BsonValue((byte[])obj); } if (obj is ObjectId) { return new BsonValue((ObjectId)obj); } if (obj is Guid) { return new BsonValue((Guid)obj); } if (obj is bool) { return new BsonValue((bool)obj); } if (obj is DateTime) { return new BsonValue((DateTime)obj); } if (obj is short || obj is ushort || obj is byte || obj is sbyte) { return new BsonValue(Convert.ToInt32(obj)); } if (obj is uint) { return new BsonValue(Convert.ToInt64(obj)); } if (obj is ulong) { return new BsonValue((long)(ulong)obj); } if (obj is float) { return new BsonValue(Convert.ToDouble(obj)); } if (obj is char) { return new BsonValue(obj.ToString()); } if (obj is Enum) { if (EnumAsInteger) { return new BsonValue((int)obj); } return new BsonValue(obj.ToString()); } if (obj is IDictionary dict) { if (type == typeof(object)) { type = obj.GetType(); } Type type2 = (type.GetTypeInfo().IsGenericType ? type.GetGenericArguments()[1] : typeof(object)); return SerializeDictionary(type2, dict, depth); } if (obj is IEnumerable) { return SerializeArray(Reflection.GetListItemType(type), obj as IEnumerable, depth); } return SerializeObject(type, obj, depth); } private BsonArray SerializeArray(Type type, IEnumerable array, int depth) { BsonArray bsonArray = new BsonArray(); foreach (object item in array) { bsonArray.Add(Serialize(type, item, depth)); } return bsonArray; } private BsonDocument SerializeDictionary(Type type, IDictionary dict, int depth) { BsonDocument bsonDocument = new BsonDocument(); foreach (object key in dict.Keys) { object obj = dict[key]; string name = key.ToString(); if (key is DateTime dateTime) { name = dateTime.ToString("o"); } bsonDocument[name] = Serialize(type, obj, depth); } return bsonDocument; } private BsonDocument SerializeObject(Type type, object obj, int depth) { Type type2 = obj.GetType(); BsonDocument bsonDocument = new BsonDocument(); EntityMapper entityMapper = GetEntityMapper(type2); if (type != type2) { bsonDocument["_type"] = new BsonValue(_typeNameBinder.GetName(type2)); } foreach (MemberMapper item in entityMapper.Members.Where((MemberMapper x) => x.Getter != null)) { object obj2 = item.Getter(obj); if (obj2 != null || SerializeNullValues || !(item.FieldName != "_id")) { if (item.Serialize != null) { bsonDocument[item.FieldName] = item.Serialize(obj2, this); } else { bsonDocument[item.FieldName] = Serialize(item.DataType, obj2, depth); } } } return bsonDocument; } } public class EntityBuilder<T> { private readonly BsonMapper _mapper; private readonly EntityMapper _entity; private readonly ITypeNameBinder _typeNameBinder; internal EntityBuilder(BsonMapper mapper, ITypeNameBinder typeNameBinder) { _mapper = mapper; _typeNameBinder = typeNameBinder; _entity = mapper.GetEntityMapper(typeof(T)); } public EntityBuilder<T> Ignore<K>(Expression<Func<T, K>> member) { return GetMember(member, delegate(MemberMapper p) { _entity.Members.Remove(p); }); } public EntityBuilder<T> Field<K>(Expression<Func<T, K>> member, string field) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return GetMember(member, delegate(MemberMapper p) { p.FieldName = field; }); } public EntityBuilder<T> Id<K>(Expression<Func<T, K>> member, bool autoId = true) { return GetMember(member, delegate(MemberMapper p) { MemberMapper memberMapper = _entity.Members.FirstOrDefault((MemberMapper x) => x.FieldName == "_id"); if (memberMapper != null) { memberMapper.FieldName = _mapper.ResolveFieldName(memberMapper.MemberName); memberMapper.AutoId = false; } p.FieldName = "_id"; p.AutoId = autoId; }); } public EntityBuilder<T> Ctor(Func<BsonDocument, T> createInstance) { _entity.CreateInstance = (BsonDocument v) => createInstance(v); return this; } public EntityBuilder<T> DbRef<K>(Expression<Func<T, K>> member, string collection = null) { return GetMember(member, delegate(MemberMapper p) { BsonMapper.RegisterDbRef(_mapper, p, _typeNameBinder, collection ?? _mapper.ResolveCollectionName(typeof(K))); }); } private EntityBuilder<T> GetMember<TK, K>(Expression<Func<TK, K>> member, Action<MemberMapper> action) { if (member == null) { throw new ArgumentNullException("member"); } MemberMapper member2 = _entity.GetMember(member); if (member2 == null) { throw new ArgumentNullException("Member '" + member.GetPath() + "' not found in type '" + _entity.ForType.Name + "' (use IncludeFields in BsonMapper)"); } action(member2); return this; } } public class EntityMapper { public Type ForType { get; } public List<MemberMapper> Members { get; } = new List<MemberMapper>(); public MemberMapper Id => Members.SingleOrDefault((MemberMapper x) => x.FieldName == "_id"); public CreateObject CreateInstance { get; set; } public EntityMapper(Type forType) { ForType = forType; } public MemberMapper GetMember(Expression expr) { return Members.FirstOrDefault((MemberMapper x) => x.MemberName == expr.GetPath()); } } internal class LinqExpressionVisitor : ExpressionVisitor { private static readonly Dictionary<Type, ITypeResolver> _resolver = new Dictionary<Type, ITypeResolver> { [typeof(BsonValue)] = new BsonValueResolver(), [typeof(BsonArray)] = new BsonValueResolver(), [typeof(BsonDocument)] = new BsonValueResolver(), [typeof(Convert)] = new ConvertResolver(), [typeof(DateTime)] = new DateTimeResolver(), [typeof(int)] = new NumberResolver("INT32"), [typeof(long)] = new NumberResolver("INT64"), [typeof(decimal)] = new NumberResolver("DECIMAL"), [typeof(double)] = new NumberResolver("DOUBLE"), [typeof(ICollection)] = new ICollectionResolver(), [typeof(Enumerable)] = new EnumerableResolver(), [typeof(Guid)] = new GuidResolver(), [typeof(Math)] = new MathResolver(), [typeof(Regex)] = new RegexResolver(), [typeof(ObjectId)] = new ObjectIdResolver(), [typeof(string)] = new StringResolver(), [typeof(Nullable)] = new NullableResolver() }; private readonly BsonMapper _mapper; private readonly Expression _expr; private readonly ParameterExpression _rootParameter; private readonly BsonDocument _parameters = new BsonDocument(); private int _paramIndex; private Type _dbRefType; private readonly StringBuilder _builder = new StringBuilder(); private readonly Stack<Expression> _nodes = new Stack<Expression>(); public LinqExpressionVisitor(BsonMapper mapper, Expression expr) { _mapper = mapper; _expr = expr; if (expr is LambdaExpression lambdaExpression) { _rootParameter = lambdaExpression.Parameters.First(); return; } throw new NotSupportedException("Expression " + expr.ToString() + " must be a lambda expression"); } public BsonExpression Resolve(bool predicate) { Visit(_expr); Constants.ENSURE(_nodes.Count == 0, "node stack must be empty when finish expression resolve"); string text = _builder.ToString(); try { BsonExpression bsonExpression = BsonExpression.Create(text, _parameters); if (predicate && (bsonExpression.Type == BsonExpressionType.Path || bsonExpression.Type == BsonExpressionType.Call || bsonExpression.Type == BsonExpressionType.Parameter)) { text = "(" + text + " = true)"; bsonExpression = BsonExpression.Create(text, _parameters); } return bsonExpression; } catch (Exception innerException) { throw new NotSupportedException("Invalid BsonExpression when converted from Linq expression: " + _expr.ToString() + " - `" + text + "`", innerException); } } protected override Expression VisitLambda<T>(Expression<T> node) { Expression result = base.VisitLambda(node); _builder.Length--; return result; } protected override Expression VisitInvocation(InvocationExpression node) { Expression result = base.VisitInvocation(node); _builder.Length--; return result; } protected override Expression VisitParameter(ParameterExpression node) { _builder.Append(_rootParameter.Equals(node) ? "$" : "@"); return base.VisitParameter(node); } protected override Expression VisitMember(MemberExpression node) { bool flag = ParameterExpressionVisitor.Test(node); MemberInfo member = node.Member; if (TryGetResolver(member.DeclaringType, out var typeResolver)) { string text = typeResolver.ResolveMember(member); if (text == null) { throw new NotSupportedException("Member " + member.Name + " are not support in " + member.DeclaringType.Name + " when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, node.Expression, new Expression[0]); } else if (node.Expression != null) { _nodes.Push(node); base.Visit(node.Expression); if (flag) { string value = ResolveMember(member); _builder.Append(value); } } else { object value2 = Evaluate(node); base.Visit(Expression.Constant(value2)); } if (_nodes.Count > 0) { _nodes.Pop(); } return node; } protected override Expression VisitMethodCall(MethodCallExpression node) { if (IsMethodIndexEval(node, out var obj, out var idx)) { Visit(obj); object obj2 = Evaluate(idx, typeof(string), typeof(int)); if (obj2 is string) { _builder.Append("."); _builder.Append($"['{obj2}']"); } else { _builder.Append($"[{obj2}]"); } return node; } if (!TryGetResolver(node.Method.DeclaringType, out var typeResolver)) { if (ParameterExpressionVisitor.Test(node)) { throw new NotSupportedException("Method " + node.Method.Name + " not available to convert to BsonExpression (" + node.ToString() + ")."); } object value = Evaluate(node); base.Visit(Expression.Constant(value)); return node; } string text = typeResolver.ResolveMethod(node.Method); if (text == null) { throw new NotSupportedException("Method " + Reflection.MethodName(node.Method) + " in " + node.Method.DeclaringType.Name + " are not supported when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, node.Object, node.Arguments); return node; } protected override Expression VisitConstant(ConstantExpression node) { object value = node.Value; while (_nodes.Count > 0 && _nodes.Peek() is MemberExpression memberExpression) { if (memberExpression.Member is FieldInfo fieldInfo) { value = fieldInfo.GetValue(value); } else if (memberExpression.Member is PropertyInfo propertyInfo) { value = propertyInfo.GetValue(value); } _nodes.Pop(); } Constants.ENSURE(_nodes.Count == 0, "counter stack must be zero to eval all properties/field over object"); string text = "p" + _paramIndex++; _builder.AppendFormat("@" + text); Type type = value?.GetType(); BsonValue value2 = ((type == null) ? BsonValue.Null : ((type == typeof(string)) ? new BsonValue((string)value) : _mapper.Serialize(value.GetType(), value))); _parameters[text] = value2; return node; } protected override Expression VisitUnary(UnaryExpression node) { if (node.NodeType == ExpressionType.Not) { if (node.Operand.NodeType == ExpressionType.MemberAccess) { _builder.Append("("); Visit(node.Operand); _builder.Append(" = false)"); } else { _builder.Append("("); Visit(node.Operand); _builder.Append(")"); _builder.Append(" = false"); } } else if (node.NodeType == ExpressionType.Convert) { Type fromType = node.Operand.Type; Type type = node.Type; if ((fromType == typeof(double) || fromType == typeof(decimal)) && (type == typeof(int) || type == typeof(long))) { string methodName = "To" + type.Name.ToString(); MethodInfo methodInfo = (from x in typeof(Convert).GetMethods() where x.Name == methodName where x.GetParameters().Length == 1 && x.GetParameters().Any((ParameterInfo z) => z.ParameterType == fromType) select x).FirstOrDefault(); if (methodInfo == null) { throw new NotSupportedException("Cast from " + fromType.Name + " are not supported when convert to BsonExpression"); } MethodCallExpression node2 = Expression.Call(null, methodInfo, node.Operand); VisitMethodCall(node2); } else { base.VisitUnary(node); } } else if (node.NodeType == ExpressionType.ArrayLength) { _builder.Append("LENGTH("); Visit(node.Operand); _builder.Append(")"); } else { base.VisitUnary(node); } return node; } protected override Expression VisitNew(NewExpression node) { if (node.Members == null) { if (!TryGetResolver(node.Type, out var typeResolver)) { throw new NotSupportedException($"New instance are not supported for {node.Type} when convert to BsonExpression ({node.ToString()})."); } string text = typeResolver.ResolveCtor(node.Constructor); if (text == null) { throw new NotSupportedException("Constructor for " + node.Type.Name + " are not supported when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, null, node.Arguments); } else { _builder.Append("{ "); for (int i = 0; i < node.Members.Count; i++) { MemberInfo memberInfo = node.Members[i]; _builder.Append((i > 0) ? ", " : ""); _builder.AppendFormat("'{0}': ", memberInfo.Name); Visit(node.Arguments[i]); } _builder.Append(" }"); } return node; } protected override Expression VisitMemberInit(MemberInitExpression node) { if (node.NewExpression.Constructor.GetParameters().Length != 0) { throw new NotSupportedException($"New instance of {node.Type} are not supported because contains ctor with parameter. Try use only property initializers: `new {node.Type.Name} {{ PropA = 1, PropB == \"John\" }}`."); } _builder.Append("{"); for (int i = 0; i < node.Bindings.Count; i++) { MemberAssignment memberAssignment = node.Bindings[i] as MemberAssignment; string text = ResolveMember(memberAssignment.Member); _builder.Append((i > 0) ? ", " : ""); _builder.Append(text.Substring(1)); _builder.Append(":"); Visit(memberAssignment.Expression); } _builder.Append("}"); return node; } protected override Expression VisitNewArray(NewArrayExpression node) { _builder.Append("[ "); for (int i = 0; i < node.Expressions.Count; i++) { _builder.Append((i > 0) ? ", " : ""); Visit(node.Expressions[i]); } _builder.Append(" ]"); return node; } protected override Expression VisitBinary(BinaryExpression node) { bool ensurePredicate = node.NodeType == ExpressionType.AndAlso || node.NodeType == ExpressionType.OrElse; if (node.NodeType == ExpressionType.Coalesce) { return VisitCoalesce(node); } if (node.NodeType == ExpressionType.ArrayIndex) { return VisitArrayIndex(node); } string @operator = GetOperator(node.NodeType); _builder.Append("("); VisitAsPredicate(node.Left, ensurePredicate); _builder.Append(@operator); if (!_mapper.EnumAsInteger && node.Left.NodeType == ExpressionType.Convert && node.Left is UnaryExpression unaryExpression && unaryExpression.Operand.Type.GetTypeInfo().IsEnum && unaryExpression.Type == typeof(int)) { VisitAsPredicate(Expression.Constant(Enum.GetName(unaryExpression.Operand.Type, Evaluate(node.Right))), ensurePredicate); } else { VisitAsPredicate(node.Right, ensurePredicate); } _builder.Append(")"); return node; } protected override Expression VisitConditional(ConditionalExpression node) { _builder.Append("IIF("); Visit(node.Test); _builder.Append(", "); Visit(node.IfTrue); _builder.Append(", "); Visit(node.IfFalse); _builder.Append(")"); return node; } private Expression VisitCoalesce(BinaryExpression node) { _builder.Append("COALESCE("); Visit(node.Left); _builder.Append(", "); Visit(node.Right); _builder.Append(")"); return node; } private Expression VisitArrayIndex(BinaryExpression node) { Visit(node.Left); _builder.Append("["); object value = Evaluate(node.Right, typeof(int)); _builder.Append(value); _builder.Append("]"); return node; } private void ResolvePattern(string pattern, Expression obj, IList<Expression> args) { Tokenizer tokenizer = new Tokenizer(pattern); while (!tokenizer.EOF) { Token token = tokenizer.ReadToken(eatWhitespace: false); if (token.Type == TokenType.Hashtag) { Visit(obj); } else if (token.Type == TokenType.At && tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Int) { int index = Convert.ToInt32(tokenizer.ReadToken(eatWhitespace: false).Expect(TokenType.Int).Value); Visit(args[index]); } else if (token.Type == TokenType.Percent) { VisitEnumerablePredicate(args[1] as LambdaExpression); } else { _builder.Append((token.Type == TokenType.String) ? ("'" + token.Value + "'") : token.Value); } } } private void VisitEnumerablePredicate(LambdaExpression lambda) { Expression body = lambda.Body; if (body is BinaryExpression binaryExpression) { if (binaryExpression.Left.NodeType != ExpressionType.Parameter) { throw new LiteException(0, "Any/All requires simple parameter on left side. Eg: `x => x.Phones.Select(p => p.Number).Any(n => n > 5)`"); } string @operator = GetOperator(binaryExpression.NodeType); _builder.Append(@operator); VisitAsPredicate(binaryExpression.Right, ensurePredicate: false); return; } if (body is MethodCallExpression methodCallExpression) { if (methodCallExpression.Object.NodeType != ExpressionType.Parameter) { throw new NotSupportedException("Any/All requires simple parameter on left side. Eg: `x.Customers.Select(c => c.Name).Any(n => n.StartsWith('J'))`"); } if (!TryGetResolver(methodCallExpression.Method.DeclaringType, out var typeResolver)) { throw new NotSupportedException("Method " + methodCallExpression.Method.Name + " not available to convert to BsonExpression inside Any/All call."); } string text = typeResolver.ResolveMethod(methodCallExpression.Method); if (text == null || !text.StartsWith("#")) { throw new NotSupportedException("Method " + methodCallExpression.Method.Name + " not available to convert to BsonExpression inside Any/All call."); } ResolvePattern(text.Substring(1), methodCallExpression.Object, methodCallExpression.Arguments); return; } throw new LiteException(0, "When using Any/All method test do only simple predicate variable. Eg: `x => x.Phones.Select(p => p.Number).Any(n => n > 5)`"); } private string GetOperator(ExpressionType nodeType) { return nodeType switch { ExpressionType.Add => " + ", ExpressionType.Multiply => " * ", ExpressionType.Subtract => " - ", ExpressionType.Divide => " / ", ExpressionType.Equal => " = ", ExpressionType.NotEqual => " != ", ExpressionType.GreaterThan => " > ", ExpressionType.GreaterThanOrEqual => " >= ", ExpressionType.LessThan => " < ", ExpressionType.LessThanOrEqual => " <= ", ExpressionType.And => " AND ", ExpressionType.AndAlso => " AND ", ExpressionType.Or => " OR ", ExpressionType.OrElse => " OR ", _ => throw new NotSupportedException($"Operator not supported {nodeType}"), }; } private string ResolveMember(MemberInfo member) { string name = member.Name; bool flag = _dbRefType != null && member.DeclaringType.IsAssignableFrom(_dbRefType); MemberMapper memberMapper = _mapper.GetEntityMapper(member.DeclaringType).Members.FirstOrDefault((MemberMapper x) => x.MemberName == name); if (memberMapper == null) { throw new NotSupportedException($"Member {name} not found on BsonMapper for type {member.DeclaringType}."); } _dbRefType = (memberMapper.IsDbRef ? memberMapper.UnderlyingType : null); return "." + ((flag && memberMapper.FieldName == "_id") ? "$id" : memberMapper.FieldName); } private bool IsMethodIndexEval(MethodCallExpression node, out Expression obj, out Expression idx) { MethodInfo method = node.Method; _ = method.DeclaringType; ParameterInfo[] parameters = method.GetParameters(); if (method.Name == "get_Item" && parameters.Length == 1 && (parameters[0].ParameterType == typeof(int) || parameters[0].ParameterType == typeof(string))) { obj = node.Object; idx = node.Arguments[0]; return true; } obj = null; idx = null; return false; } private void VisitAsPredicate(Expression expr, bool ensurePredicate) { ensurePredicate = ensurePredicate && (expr.NodeType == ExpressionType.MemberAccess || expr.NodeType == ExpressionType.Call || expr.NodeType == ExpressionType.Invoke || expr.NodeType == ExpressionType.Constant); if (ensurePredicate) { _builder.Append("("); _builder.Append("("); base.Visit(expr); _builder.Append(")"); _builder.Append(" = true)"); } else { base.Visit(expr); } } private object Evaluate(Expression expr, params Type[] validTypes) { object value = null; if (expr.NodeType == ExpressionType.Constant) { ConstantExpression constantExpression = (ConstantExpression)expr; value = constantExpression.Value; } else { Delegate @delegate = Expression.Lambda(expr).Compile(); value = @delegate.DynamicInvoke(); } if (validTypes.Length != 0 && value == null) { throw new NotSupportedException($"Expression {expr} can't return null value"); } if (validTypes.Length != 0 && !validTypes.Any((Type x) => x == value.GetType())) { throw new NotSupportedException(string.Format("Expression {0} must return on of this types: {1}", expr, string.Join(", ", validTypes.Select((Type x) => "`" + x.Name + "`")))); } return value; } private bool TryGetResolver(Type declaringType, out ITypeResolver typeResolver) { bool num = Reflection.IsCollection(declaringType); bool flag = Reflection.IsEnumerable(declaringType); bool flag2 = Reflection.IsNullable(declaringType); Type key = (num ? typeof(ICollection) : (flag ? typeof(Enumerable) : (flag2 ? typeof(Nullable) : declaringType))); return _resolver.TryGetValue(key, out typeResolver); } } internal class ParameterExpressionVisitor : ExpressionVisitor { public bool IsParameter { get; private set; } protected override Expression VisitParameter(ParameterExpression node) { IsParameter = true; return base.VisitParameter(node); } public static bool Test(Expression node) { ParameterExpressionVisitor parameterExpressionVisitor = new ParameterExpressionVisitor(); parameterExpressionVisitor.Visit(node); return parameterExpressionVisitor.IsParameter; } } internal class BsonValueResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return null; } public string ResolveMember(MemberInfo member) { switch (member.Name) { case "AsInt32": case "AsInt64": case "AsArray": case "AsDateTime": case "AsDocument": case "AsObjectId": case "AsString": case "AsBinary": case "AsDouble": case "AsBoolean": case "AsDecimal": case "AsGuid": return "#"; case "IsNull": return "IS_NULL(#)"; case "IsArray": return "IS_ARRAY(#)"; case "IsDocument": return "IS_DOCUMENT(#)"; case "IsInt32": return "IS_INT32(#)"; case "IsInt64": return "IS_INT64(#)"; case "IsDouble": return "IS_DOUBLE(#)"; case "IsDecimal": return "IS_DECIMAL(#)"; case "IsNumber": return "IS_NUMBER(#)"; case "IsBinary": return "IS_BINARY(#)"; case "IsBoolean": return "IS_BOOLEAN(#)"; case "IsString": return "IS_STRING(#)"; case "IsObjectId": return "IS_OBJECTID(#)"; case "IsGuid": return "IS_GUID(#)"; case "IsDateTime": return "IS_DATETIME(#)"; case "IsMinValue": return "IS_MINVALUE(#)"; case "IsMaxValue": return "IS_MAXVALUE(#)"; default: return null; } } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class ConvertResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return method.Name switch { "ToInt32" => "INT32(@0)", "ToInt64" => "INT64(@0)", "ToDouble" => "DOUBLE(@0)", "ToDecimal" => "DECIMAL(@0)", "ToDateTime" => "DATE(@0)", "FromBase64String" => "BINARY(@0)", "ToBoolean" => "BOOL(@0)", "ToString" => "STRING(@0)", _ => null, }; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class DateTimeResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { switch (method.Name) { case "AddYears": return "DATEADD('y', @0, #)"; case "AddMonths": return "DATEADD('M', @0, #)"; case "AddDays": return "DATEADD('d', @0, #)"; case "AddHours": return "DATEADD('h', @0, #)"; case "AddMinutes": return "DATEADD('m', @0, #)"; case "AddSeconds": return "DATEADD('s', @0, #)"; case "ToString": { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 0) { return "STRING(#)"; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "FORMAT(#, @0)"; } break; } case "ToUniversalTime": return "TO_UTC(#)"; case "Parse": return "DATETIME(@0)"; case "Equals": return "# = @0"; } return null; } public string ResolveMember(MemberInfo member) { return member.Name switch { "Now" => "NOW()", "UtcNow" => "NOW_UTC()", "Today" => "TODAY()", "Year" => "YEAR(#)", "Month" => "MONTH(#)", "Day" => "DAY(#)", "Hour" => "HOUR(#)", "Minute" => "MINUTE(#)", "Second" => "SECOND(#)", "Date" => "DATETIME(YEAR(#), MONTH(#), DAY(#))", "ToLocalTime" => "TO_LOCAL(#)", "ToUniversalTime" => "TO_UTC(#)", _ => null, }; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 3 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(int)) { return "DATETIME(@0, @1, @2)"; } return null; } } internal class EnumerableResolver : ITypeResolver { public virtual string ResolveMethod(MethodInfo method) { switch (Reflection.MethodName(method, 1)) { case "AsEnumerable()": return "@0[*]"; case "get_Item(int)": return "#[@0]"; case "ElementAt(int)": return "@0[@1]"; case "Single()": case "First()": case "SingleOrDefault()": case "FirstOrDefault()": return "@0[0]"; case "Last()": case "LastOrDefault()": return "@0[-1]"; case "Single(Func<T,TResult>)": case "First(Func<T,TResult>)": case "SingleOrDefault(Func<T,TResult>)": case "FirstOrDefault(Func<T,TResult>)": return "FIRST(FILTER(@0 => @1))"; case "Last(Func<T,TResult>)": case "LastOrDefault(Func<T,TResult>)": return "LAST(FILTER(@0 => @1))"; case "Where(Func<T,TResult>)": return "FILTER(@0 => @1)"; case "Select(Func<T,TResult>)": return "MAP(@0 => @1)"; case "Count()": return "COUNT(@0)"; case "Sum()": return "SUM(@0)"; case "Average()": return "AVG(@0)"; case "Max()": return "MAX(@0)"; case "Min()": return "MIN(@0)"; case "Count(Func<T,TResult>)": return "COUNT(FILTER(@0 => @1))"; case "Sum(Func<T,TResult>)": return "SUM(MAP(@0 => @1))"; case "Average(Func<T,TResult>)": return "AVG(MAP(@0 => @1))"; case "Max(Func<T,TResult>)": return "MAX(MAP(@0 => @1))"; case "Min(Func<T,TResult>)": return "MIN(MAP(@0 => @1))"; case "ToList()": case "ToArray()": return "ARRAY(@0)"; case "Any(Func<T,TResult>)": return "@0 ANY %"; case "All(Func<T,TResult>)": return "@0 ALL %"; case "Any()": return "COUNT(@0) > 0"; default: if (method.Name == "Contains") { return "@0 ANY = @1"; } return null; } } public virtual string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Length")) { if (name == "Count") { return "COUNT(#)"; } return null; } return "LENGTH(#)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class GuidResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return method.Name switch { "ToString" => "STRING(#)", "NewGuid" => "GUID()", "Parse" => "GUID(@0)", "TryParse" => throw new NotSupportedException("There is no TryParse translate. Use Guid.Parse()"), "Equals" => "# = @0", _ => null, }; } public string ResolveMember(MemberInfo member) { if (member.Name == "Empty") { return "GUID('00000000-0000-0000-0000-000000000000')"; } return null; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "GUID(@0)"; } return null; } } internal class ICollectionResolver : EnumerableResolver { public override string ResolveMethod(MethodInfo method) { if (method.Name == "Contains") { return "# ANY = @0"; } return base.ResolveMethod(method); } } internal interface ITypeResolver { string ResolveMethod(MethodInfo method); string ResolveMember(MemberInfo member); string ResolveCtor(ConstructorInfo ctor); } internal class MathResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { int num = method.GetParameters().Length; switch (method.Name) { case "Abs": return "ABS(@0)"; case "Pow": return "POW(@0, @1)"; case "Round": if (num != 2) { throw new ArgumentOutOfRangeException("Method Round need 2 arguments when convert to BsonExpression"); } return "ROUND(@0, @1)"; default: return null; } } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class NullableResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return null; } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "HasValue")) { if (name == "Value") { return "#"; } return null; } return "(IS_NULL(#) = false)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class NumberResolver : ITypeResolver { private readonly string _parseMethod; public NumberResolver(string parseMethod) { _parseMethod = parseMethod; } public string ResolveMethod(MethodInfo method) { switch (method.Name) { case "ToString": { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 0) { return "STRING(#)"; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "FORMAT(#, @0)"; } break; } case "Parse": return _parseMethod + "(@0)"; case "Equals": return "# = @0"; } return null; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class ObjectIdResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { string name = method.Name; if (!(name == "ToString")) { if (name == "Equals") { return "# = @0"; } return null; } return "STRING(#)"; } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Empty")) { if (name == "CreationTime") { return "OID_CREATIONTIME(#)"; } return null; } return "OBJECTID('000000000000000000000000')"; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "OBJECTID(@0)"; } return null; } } internal class RegexResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { string name = method.Name; if (!(name == "Split")) { if (name == "IsMatch") { return "IS_MATCH(@0, @1)"; } return null; } return "SPLIT(@0, @1, true)"; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class StringResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { int num = method.GetParameters().Length; switch (method.Name) { case "Count": return "LENGTH(#)"; case "Trim": return "TRIM(#)"; case "TrimStart": return "LTRIM(#)"; case "TrimEnd": return "RTRIM(#)"; case "ToUpper": return "UPPER(#)"; case "ToUpperInvariant": return "UPPER(#)"; case "ToLower": return "LOWER(#)"; case "ToLowerInvariant": return "LOWER(#)"; case "Replace": return "REPLACE(#, @0, @1)"; case "PadLeft": return "LPAD(#, @0, @1)"; case "RightLeft": return "RPAD(#, @0, @1)"; case "IndexOf": if (num != 1) { return "INDEXOF(#, @0, @1)"; } return "INDEXOF(#, @0)"; case "Substring": if (num != 1) { return "SUBSTRING(#, @0, @1)"; } return "SUBSTRING(#, @0)"; case "StartsWith": return "# LIKE (@0 + '%')"; case "Contains": return "# LIKE ('%' + @0 + '%')"; case "EndsWith": return "# LIKE ('%' + @0)"; case "ToString": return "#"; case "Equals": return "# = @0"; case "IsNullOrEmpty": return "(LENGTH(@0) = 0)"; case "IsNullOrWhiteSpace": return "(LENGTH(TRIM(@0)) = 0)"; case "Format": throw new NotImplementedException(); case "Join": throw new NotImplementedException(); default: return null; } } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Length")) { if (name == "Empty") { return "''"; } return null; } return "LENGTH(#)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } public class MemberMapper { public bool AutoId { get; set; } public string MemberName { get; set; } public Type DataType { get; set; } public string FieldName { get; set; } public GenericGetter Getter { get; set; } public GenericSetter Setter { get; set; } public Func<object, BsonMapper, BsonValue> Serialize { get; set; } public Func<BsonValue, BsonMapper, object> Deserialize { get; set; } public bool IsDbRef { get; set; } public bool IsEnumerable { get; set; } public Type UnderlyingType { get; set; } public bool IsIgnore { get; set; } } public delegate object CreateObject(BsonDocument value); public delegate void GenericSetter(object target, object value); public delegate object GenericGetter(object obj); internal class Reflection { private static readonly Dictionary<Type, CreateObject> _cacheCtor = new Dictionary<Type, CreateObject>(); public static readonly Dictionary<Type, PropertyInfo> ConvertType = new Dictionary<Type, PropertyInfo> { [typeof(DateTime)] = typeof(BsonValue).GetProperty("AsDateTime"), [typeof(decimal)] = typeof(BsonValue).GetProperty("AsDecimal"), [typeof(double)] = typeof(BsonValue).GetProperty("AsDouble"), [typeof(long)] = typeof(BsonValue).GetProperty("AsInt64"), [typeof(int)] = typeof(BsonValue).GetProperty("AsInt32"), [typeof(bool)] = typeof(BsonValue).GetProperty("AsBoolean"), [typeof(byte[])] = typeof(BsonValue).GetProperty("AsBinary"), [typeof(BsonDocument)] = typeof(BsonValue).GetProperty("AsDocument"), [ty