new document conventions builder
This commit is contained in:
@@ -28,11 +28,14 @@ namespace zero.Core.Api
|
||||
|
||||
protected IPasswordHasher<BackofficeUser> PasswordHasher { get; private set; }
|
||||
|
||||
protected IZeroDocumentConventionsBuilder ConventionsBuilder { get; private set; }
|
||||
|
||||
public SetupApi(IZeroOptions options, IPasswordHasher<BackofficeUser> passwordHasher)
|
||||
|
||||
public SetupApi(IZeroOptions options, IPasswordHasher<BackofficeUser> passwordHasher, IZeroDocumentConventionsBuilder conventionsBuilder)
|
||||
{
|
||||
Options = options;
|
||||
PasswordHasher = passwordHasher;
|
||||
ConventionsBuilder = conventionsBuilder;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +62,8 @@ namespace zero.Core.Api
|
||||
Database = model.Database.Name
|
||||
};
|
||||
|
||||
raven.Setup(Options).Initialize();
|
||||
ConventionsBuilder.Run(raven.Conventions);
|
||||
raven.Initialize();
|
||||
|
||||
// create application
|
||||
Application app = Prepare(new Application()
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
using Raven.Client.Documents.Conventions;
|
||||
using Raven.Client.Json.Serialization;
|
||||
using Raven.Client.Json.Serialization.NewtonsoftJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Attributes;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Extensions;
|
||||
using zero.Core.Options;
|
||||
using zero.Core.Utils;
|
||||
|
||||
namespace zero.Core.Database
|
||||
{
|
||||
public class ZeroDocumentConventionsBuilder : IZeroDocumentConventionsBuilder
|
||||
{
|
||||
protected HashSet<Type> PolymorphTypes { get; private set; } = new();
|
||||
|
||||
protected Type AcceptsZeroConventionsType { get; set; } = typeof(IZeroDbConventions);
|
||||
|
||||
protected char IdentityPartsSeparator { get; set; } = '.';
|
||||
|
||||
protected IZeroOptions Options { get; private set; }
|
||||
|
||||
protected static Dictionary<Type, string> CachedTypeCollectionNameMap = new();
|
||||
|
||||
|
||||
public ZeroDocumentConventionsBuilder(IZeroOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Run(DocumentConventions conventions)
|
||||
{
|
||||
conventions.MaxNumberOfRequestsPerSession = 100;
|
||||
conventions.IdentityPartsSeparator = IdentityPartsSeparator;
|
||||
conventions.TransformTypeCollectionNameToDocumentIdPrefix = TransformTypeCollectionNameToDocumentIdPrefix;
|
||||
conventions.FindCollectionName = FindCollectionName;
|
||||
conventions.RegisterAsyncIdConvention<IZeroEntity>((_, entity) => GetDocumentId(conventions, entity));
|
||||
|
||||
ConfigureJsonSerializer(conventions.Serialization);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a document ID from an entity
|
||||
/// </summary>
|
||||
protected virtual Task<string> GetDocumentId<T>(DocumentConventions conventions, T entity)
|
||||
{
|
||||
string collection = conventions.GetCollectionName(entity);
|
||||
|
||||
StringBuilder documentId = new();
|
||||
documentId.Append(conventions.TransformTypeCollectionNameToDocumentIdPrefix(collection));
|
||||
documentId.Append(IdentityPartsSeparator);
|
||||
documentId.Append(IdGenerator.Create());
|
||||
|
||||
return Task.FromResult(documentId.ToString());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds the collection name for a certain type based on internal rules
|
||||
/// </summary>
|
||||
protected virtual string FindCollectionName(Type type)
|
||||
{
|
||||
string collection = null;
|
||||
|
||||
Func<string, string> cache = name =>
|
||||
{
|
||||
CachedTypeCollectionNameMap.Add(type, name);
|
||||
return name;
|
||||
};
|
||||
|
||||
if (CachedTypeCollectionNameMap.TryGetValue(type, out collection))
|
||||
{
|
||||
return collection;
|
||||
}
|
||||
|
||||
// do not alter non-internal entities
|
||||
if (!AcceptsZeroConventionsType.IsAssignableFrom(type))
|
||||
{
|
||||
return cache(DocumentConventions.DefaultGetCollectionName(type));
|
||||
}
|
||||
|
||||
// use name from attribute if available
|
||||
CollectionAttribute collectionAttribute = type.GetCustomAttribute<CollectionAttribute>(true);
|
||||
|
||||
if (collectionAttribute != null)
|
||||
{
|
||||
return cache(collectionAttribute.Name);
|
||||
}
|
||||
|
||||
// use base interface if available
|
||||
Type interfaceBaseType = type.GetInterfaces().FirstOrDefault(x => x.IsInterface && AcceptsZeroConventionsType.IsAssignableFrom(x) && x.Name != AcceptsZeroConventionsType.Name);
|
||||
|
||||
if (interfaceBaseType != null)
|
||||
{
|
||||
// use name from attribute if available
|
||||
collectionAttribute = interfaceBaseType.GetCustomAttribute<CollectionAttribute>(true);
|
||||
if (collectionAttribute != null)
|
||||
{
|
||||
return cache(collectionAttribute.Name);
|
||||
}
|
||||
}
|
||||
|
||||
// use base type for polymorphism
|
||||
Type polymorphBaseType = PolymorphTypes.FirstOrDefault(x => type.IsSubclassOf(x));
|
||||
|
||||
if (polymorphBaseType != null)
|
||||
{
|
||||
return cache(DocumentConventions.DefaultGetCollectionName(polymorphBaseType));
|
||||
}
|
||||
|
||||
return cache(DocumentConventions.DefaultGetCollectionName(type));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Configures the JSON serializer + deserializer for document retrieval from RavenDB
|
||||
/// </summary>
|
||||
protected virtual void ConfigureJsonSerializer(ISerializationConventions conventions)
|
||||
{
|
||||
NewtonsoftJsonSerializationConventions typedConventions = conventions as NewtonsoftJsonSerializationConventions;
|
||||
typedConventions.CustomizeJsonDeserializer = x => x.Converters.Add(new RefJsonConverter());
|
||||
typedConventions.CustomizeJsonSerializer = x => x.Converters.Add(new RefJsonConverter());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Translates the types collection name to the document id prefix
|
||||
/// </summary>
|
||||
protected virtual string TransformTypeCollectionNameToDocumentIdPrefix(string name)
|
||||
{
|
||||
if (!Options.Raven.CollectionPrefix.IsNullOrWhiteSpace())
|
||||
{
|
||||
name = Options.Raven.CollectionPrefix.EnsureEndsWith(IdentityPartsSeparator) + name.TrimStart(Options.Raven.CollectionPrefix);
|
||||
}
|
||||
|
||||
return name.ToCamelCaseId();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface IZeroDocumentConventionsBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies internal rules to the RavenDB document conventions
|
||||
/// </summary>
|
||||
void Run(DocumentConventions conventions);
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
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 = '.';
|
||||
|
||||
/// <summary>
|
||||
/// Setup conventions for the document store
|
||||
/// </summary>
|
||||
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<IZeroEntity>((_, entity) =>
|
||||
{
|
||||
string guid = IdGenerator.Create((entity is IBackofficeUser or IBackofficeUserRole) ? 8 : -1);
|
||||
string collection = store.Conventions.GetCollectionName(entity);
|
||||
|
||||
string tag = store.Conventions.TransformTypeCollectionNameToDocumentIdPrefix(collection);
|
||||
return Task.FromResult(tag + store.Conventions.IdentityPartsSeparator + guid);
|
||||
});
|
||||
|
||||
store.Conventions.RegisterAsyncIdConvention<IPage>((_, 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<CollectionAttribute>(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<CollectionAttribute>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="media-item-preview" :class="{'media-pattern': value.image }">
|
||||
<span class="media-item-check"><ui-icon symbol="fth-check" :size="14" /></span>
|
||||
<img class="media-item-image" v-if="value.image" :src="value.image" />
|
||||
<span class="media-item-icon" v-if="!value.image"><ui-icon :symbol="shared ? 'fth-globe' : (value.isFolder ? 'fth-folder' : 'fth-file')" :size="28" /></span>
|
||||
<span class="media-item-icon" v-if="!value.image"><ui-icon :symbol="shared ? 'fth-globe' : (value.isFolder ? 'fth-folder' : 'fth-file')" :size="36" :stroke-width="1.5" /></span>
|
||||
</div>
|
||||
<p class="media-item-text" v-if="!shared">
|
||||
<span :title="value.name">{{value.name}}</span>
|
||||
|
||||
@@ -143,19 +143,21 @@ namespace zero.Web
|
||||
/// </summary>
|
||||
void ConfigureDatabase()
|
||||
{
|
||||
// add raven
|
||||
Services.AddSingleton<IZeroDocumentConventionsBuilder, ZeroDocumentConventionsBuilder>();
|
||||
|
||||
Services.AddSingleton<IZeroStore, ZeroStore>(context =>
|
||||
{
|
||||
IZeroOptions options = context.GetService<IZeroOptions>();
|
||||
IZeroDocumentConventionsBuilder conventionsBuilder = context.GetService<IZeroDocumentConventionsBuilder>();
|
||||
|
||||
IDocumentStore store = new ZeroStore(options)
|
||||
{
|
||||
Urls = new string[1] { options.Raven.Url }
|
||||
};
|
||||
|
||||
store.Conventions.MaxNumberOfRequestsPerSession = 100;
|
||||
conventionsBuilder.Run(store.Conventions);
|
||||
|
||||
IDocumentStore raven = store.Setup(options).Initialize();
|
||||
IDocumentStore raven = store.Initialize();
|
||||
|
||||
// create all indexes
|
||||
var assemblies = AssemblyDiscovery.Current.GetAssemblies().ToList();
|
||||
|
||||
Reference in New Issue
Block a user