using Raven.Client.Documents; using Raven.Client.Documents.Conventions; using Raven.Client.Documents.Operations.CompareExchange; using Raven.Client.Json.Serialization.NewtonsoftJson; using System; using System.Linq; using System.Reflection; using System.Threading.Tasks; using zero.Core.Attributes; using zero.Core.Entities; using zero.Core.Options; using zero.Core.Utils; namespace zero.Core.Extensions { public static class RavenDocumentStoreExtensions { const char DOT = '.'; /// /// Setup conventions for the document store /// public static IDocumentStore Setup(this IDocumentStore store, IZeroOptions options) { Type[] polymorphTypes = new Type[2] { typeof(SpaceContent), typeof(Page) }; Type dbConventionType = typeof(IZeroDbConventions); store.Conventions.IdentityPartsSeparator = DOT; store.Conventions.RegisterAsyncIdConvention((_, entity) => { string prefix = String.Empty; string guid = IdGenerator.Create(); string collection = store.Conventions.GetCollectionName(entity); //CollectionAttribute collectionAttribute = entity.GetType().GetCustomAttribute(true); if ((entity is IMedia && ((IMedia)entity).IsCore) || (entity is IMediaFolder && ((IMediaFolder)entity).IsCore)) { prefix = Constants.Database.CoreIdPrefix; } if (entity is IBackofficeUser or IBackofficeUserRole) { guid = IdGenerator.Create(8); } string tag = store.Conventions.TransformTypeCollectionNameToDocumentIdPrefix(collection); return Task.FromResult(prefix + tag + store.Conventions.IdentityPartsSeparator + guid); }); store.Conventions.RegisterAsyncIdConvention((_, entity) => { return store.Conventions.AsyncDocumentIdGenerator(_, entity); }); (store.Conventions.Serialization as NewtonsoftJsonSerializationConventions).CustomizeJsonDeserializer = x => { x.Converters.Add(new RefJsonConverter()); }; (store.Conventions.Serialization as NewtonsoftJsonSerializationConventions).CustomizeJsonSerializer = x => { x.Converters.Add(new RefJsonConverter()); }; store.Conventions.FindCollectionName = type => { Type finalType = type; // do not alter non-internal entities if (!dbConventionType.IsAssignableFrom(type)) { return DocumentConventions.DefaultGetCollectionName(type); } // use name from attribute if available CollectionAttribute collection = type.GetCustomAttribute(true); if (collection != null) { return collection.Name; } // use base interface if available Type interfaceBaseType = type.GetInterfaces().FirstOrDefault(x => x.IsInterface && dbConventionType.IsAssignableFrom(x) && x.Name != dbConventionType.Name); if (interfaceBaseType != null) { // use name from attribute if available collection = interfaceBaseType.GetCustomAttribute(true); if (collection != null) { return options.Raven.CollectionPrefix + collection.Name; } } // use base type for polymorphism Type polymorphBaseType = polymorphTypes.FirstOrDefault(x => type.IsSubclassOf(x)); if (polymorphBaseType != null) { finalType = polymorphBaseType; } return options.Raven.CollectionPrefix + DocumentConventions.DefaultGetCollectionName(finalType); }; store.Conventions.TransformTypeCollectionNameToDocumentIdPrefix = name => { if (!options.Raven.CollectionPrefix.IsNullOrWhiteSpace()) { name = options.Raven.CollectionPrefix.EnsureEndsWith(store.Conventions.IdentityPartsSeparator) + name.TrimStart(options.Raven.CollectionPrefix); } return name.ToCamelCaseId(); }; return store; } /// /// Reserves a key cluster-wide /// public static async Task ReserveAsync(this IDocumentStore store, string key, string value = null) { if (String.IsNullOrWhiteSpace(key)) { return false; } if (value == null) { value = key; } var operation = new PutCompareExchangeValueOperation(key, value, 0); CompareExchangeResult result = await store.Operations.SendAsync(operation).ConfigureAwait(false); return result.Successful; } /// /// Removes a cluster-wide key reservation /// public static async Task RemoveReservationAsync(this IDocumentStore store, string key) { if (!String.IsNullOrWhiteSpace(key)) { return false; } CompareExchangeValue readResult = store.Operations.Send(new GetCompareExchangeValueOperation(key)); if (readResult == null) { return false; } DeleteCompareExchangeValueOperation operation = new DeleteCompareExchangeValueOperation(key, readResult.Index); CompareExchangeResult result = await store.Operations.SendAsync(operation).ConfigureAwait(false); return result.Successful; } } }