internal static bool CreateInstance(IndexRegistry registry, Type grainType, out GrainIndexes grainIndexes) { grainIndexes = registry.TryGetGrainIndexedInterfaces(grainType, out Type[] indexedInterfaces) ? new GrainIndexes(registry, indexedInterfaces, registry.GetNullPropertyValuesForGrain(grainType)) : null; return(grainIndexes != null); }
internal static IndexRegistry GetIndexRegistry(ApplicationPartsIndexableGrainLoader loader, IEnumerable <Type> grainClassTypes) { var registry = new IndexRegistry(); foreach (var grainClassType in grainClassTypes) { if (registry.ContainsGrainType(grainClassType)) { throw new InvalidOperationException($"Precondition violated: GetIndexRegistry should not encounter a duplicate grain class type ({IndexUtils.GetFullTypeName(grainClassType)})"); } GetIndexesForASingleGrainType(loader, registry, grainClassType); } return(registry); }
internal async static Task <IndexRegistry> GetIndexRegistry(ApplicationPartsIndexableGrainLoader loader, Type[] grainTypes) { var registry = new IndexRegistry(); foreach (var grainType in grainTypes) { if (registry.ContainsKey(grainType)) { throw new InvalidOperationException($"Precondition violated: GetGrainClassIndexes should not encounter a duplicate type ({IndexUtils.GetFullTypeName(grainType)})"); } await GetIndexesForASingleGrainType(loader, registry, grainType); } return(registry); }
private GrainIndexes(IndexRegistry registry, IEnumerable <Type> indexedInterfaceTypes, IReadOnlyDictionary <string, object> propertyNullValues) { this.indexRegistry = registry; this.PropertyNullValues = propertyNullValues; this.interfaceToIndexMap = indexedInterfaceTypes.ToDictionary(itf => itf, itf => new InterfaceIndexes(registry[itf])); }
private async static Task <bool?> CreateIndexesForASingleInterface(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type propertiesArgType, Type userDefinedIGrain, Type userDefinedGrainImpl, bool?grainIndexesAreEager) { // All the properties in TProperties are scanned for Index annotation. // If found, the index is created using the information provided in the annotation. NamedIndexMap indexesOnGrain = new NamedIndexMap(); var interfaceHasLazyIndex = false; // Use a separate value from grainIndexesAreEager in case we change to allow mixing eager and lazy on a single grain. foreach (PropertyInfo propInfo in propertiesArgType.GetProperties()) { var indexAttrs = propInfo.GetCustomAttributes <IndexAttribute>(inherit: false); foreach (var indexAttr in indexAttrs) { string indexName = "__" + propInfo.Name; if (indexesOnGrain.ContainsKey(indexName)) { throw new InvalidOperationException($"An index named {indexName} already exists for user-defined grain interface {userDefinedIGrain.Name}"); } Type indexType = (Type)indexTypeProperty.GetValue(indexAttr); if (indexType.IsGenericTypeDefinition) { indexType = indexType.MakeGenericType(propInfo.PropertyType, userDefinedIGrain); } // If it's not eager, then it's configured to be lazily updated bool isEager = (bool)isEagerProperty.GetValue(indexAttr); if (!isEager) { interfaceHasLazyIndex = true; } if (!grainIndexesAreEager.HasValue) { grainIndexesAreEager = isEager; } bool isUnique = (bool)isUniqueProperty.GetValue(indexAttr); ValidateSingleIndex(indexAttr, userDefinedIGrain, userDefinedGrainImpl, propertiesArgType, propInfo, grainIndexesAreEager, isEager, isUnique); int maxEntriesPerBucket = (int)maxEntriesPerBucketProperty.GetValue(indexAttr); if (loader != null) { await loader.CreateIndex(propertiesArgType, userDefinedIGrain, indexesOnGrain, propInfo, indexName, indexType, isEager, isUnique, maxEntriesPerBucket); } } } registry[userDefinedIGrain] = indexesOnGrain; if (interfaceHasLazyIndex && loader != null) { await loader.RegisterWorkflowQueues(userDefinedIGrain, userDefinedGrainImpl); } return(grainIndexesAreEager); }
private async static Task GetIndexesForASingleGrainType(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type grainType) { Type[] interfaces = grainType.GetInterfaces(); bool? grainIndexesAreEager = null; // If there is an interface that directly extends IIndexableGrain<TProperties>... Type iIndexableGrain = interfaces.Where(itf => itf.IsGenericType && itf.GetGenericTypeDefinition() == typeof(IIndexableGrain <>)).FirstOrDefault(); if (iIndexableGrain != null) { // ... and its generic argument is a class (TProperties)... Type propertiesArgType = iIndexableGrain.GetGenericArguments()[0]; if (propertiesArgType.GetTypeInfo().IsClass) { // ... then add the indexes for all the descendant interfaces of IIndexableGrain<TProperties>; these interfaces are defined by end-users. foreach (Type userDefinedIGrain in interfaces.Where(itf => iIndexableGrain != itf && iIndexableGrain.IsAssignableFrom(itf) && !registry.ContainsKey(itf))) { grainIndexesAreEager = await CreateIndexesForASingleInterface(loader, registry, propertiesArgType, userDefinedIGrain, grainType, grainIndexesAreEager); } } } }
private static bool?CreateIndexesForASingleInterface(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type propertiesClassType, Type grainInterfaceType, Type grainClassType, ConsistencyScheme consistencyScheme, bool?grainIndexesAreEager) { // All the properties in TProperties are scanned for Index annotation. // If found, the index is created using the information provided in the annotation. var indexesOnInterface = new NamedIndexMap(propertiesClassType); foreach (var propInfo in propertiesClassType.GetProperties()) { var indexAttrs = propInfo.GetCustomAttributes <IndexAttribute>(inherit: false); foreach (var indexAttr in indexAttrs) { var indexName = IndexUtils.PropertyNameToIndexName(propInfo.Name); if (indexesOnInterface.ContainsKey(indexName)) { throw new InvalidOperationException($"An index named {indexName} already exists for user-defined grain interface {grainInterfaceType.Name}"); } var indexType = (Type)indexTypeProperty.GetValue(indexAttr); if (consistencyScheme == ConsistencyScheme.Transactional) { var transactionalVariantAttr = indexType.GetCustomAttribute <TransactionalIndexVariantAttribute>(); if (transactionalVariantAttr != null) { indexType = transactionalVariantAttr.TransactionalIndexType; } } if (indexType.IsGenericTypeDefinition) { // For the (Active|Total) constructors that take (Active|Total)IndexType parameters, leaving the indexType's key type and interface type // generic arguments as "<,>", this fills them in. indexType = indexType.MakeGenericType(propInfo.PropertyType, grainInterfaceType); } // If it's not eager, then it's configured to be lazily updated var isEager = (bool)isEagerProperty.GetValue(indexAttr); if (!grainIndexesAreEager.HasValue) { grainIndexesAreEager = isEager; } var isUnique = (bool)isUniqueProperty.GetValue(indexAttr); ValidateSingleIndex(indexAttr, grainInterfaceType, grainClassType, propertiesClassType, propInfo, grainIndexesAreEager, consistencyScheme, isEager: isEager, isUnique: isUnique); // Multiple bools, so use param names for safety var maxEntriesPerBucket = (int)maxEntriesPerBucketProperty.GetValue(indexAttr); if (loader != null) { loader.CreateIndex(propertiesClassType, grainInterfaceType, indexesOnInterface, propInfo, indexName, indexType, isEager, isUnique, maxEntriesPerBucket); } else { IndexFactory.ValidateIndexType(indexType, propInfo, out _, out _); } } } registry[grainInterfaceType] = indexesOnInterface; return(grainIndexesAreEager); }
private static void GetIndexesForASingleGrainType(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type grainClassType) { // First see if any indexed interfaces on this grain were already encountered on another grain (unless we're // in validation mode, which doesn't create the indexes). var indexedInterfacesAndProperties = EnumerateIndexedInterfacesForAGrainClassType(grainClassType).ToList(); var indexedInterfaces = loader != null ? new HashSet <Type>(indexedInterfacesAndProperties.Select(tup => tup.interfaceType).Where(itfType => registry.ContainsKey(itfType))) : new HashSet <Type>(); var grainIndexesAreEager = indexedInterfaces.Count > 0 ? registry[indexedInterfaces.First()].HasAnyEagerIndex : default(bool?); var consistencyScheme = grainClassType.GetConsistencyScheme(); // Now find all indexed interfaces we're seeing for the first time (again, unless we're in validation mode). foreach (var(grainInterfaceType, propertiesClassType) in indexedInterfacesAndProperties) { if (!indexedInterfaces.Contains(grainInterfaceType)) { grainIndexesAreEager = CreateIndexesForASingleInterface(loader, registry, propertiesClassType, grainInterfaceType, grainClassType, consistencyScheme, grainIndexesAreEager); indexedInterfaces.Add(grainInterfaceType); } } IReadOnlyDictionary <string, object> getNullValuesDictionary() { IEnumerable <(string propName, (string itfName, object nullValue))> getNullPropertyValuesForInterface(Type interfaceType) => registry[interfaceType].PropertiesClassType.GetProperties() .Select(info => (name: info.Name, nullSpec: (itfname: interfaceType.Name, nullValue: IndexUtils.GetNullValue(info)))) .Where(p => p.nullSpec.nullValue != null); Dictionary <string, (string, object)> addToDict(Dictionary <string, (string, object)> dict, (string propName, (string itfName, object nullValue)nullSpec) current) { bool isInDict(string propName) { return(dict.TryGetValue(propName, out (string itfName, object nullValue)prevNullSpec) ? (prevNullSpec.nullValue.Equals(current.nullSpec.nullValue) ? true : throw new IndexConfigurationException($"Property {propName} has conflicting NullValues defined on interfaces {prevNullSpec.itfName} and {current.nullSpec.itfName}")) : false); } if (!isInDict(current.propName)) { dict[current.propName] = current.nullSpec; } return(dict); } return(indexedInterfaces.SelectMany(itf => getNullPropertyValuesForInterface(itf)) .Aggregate(new Dictionary <string, (string itfName, object nullValue)>(), (dict, pair) => addToDict(dict, pair)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.nullValue)); } if (indexedInterfaces.Count > 0) { registry.SetGrainIndexes(grainClassType, indexedInterfaces.ToArray(), getNullValuesDictionary()); } }
/// <summary> /// Provides the index information for a given grain interface type. /// </summary> /// <typeparam name="T">The target grain interface type</typeparam> /// <returns>the index information for the given grain type T. /// The index information is a dictionary from indexIDs defined /// on a grain interface to a triple. The triple consists of: /// 1) the index object that implements IndexInterface, /// 2) the IndexMetaData object for this index, and /// 3) the IndexUpdateGenerator instance for this index. /// This triple is untyped, because IndexInterface, IndexMetaData /// and IndexUpdateGenerator types are not visible in this project. /// /// This method returns an empty dictionary if the OrleansIndexing /// project is not available.</returns> internal static IDictionary <string, Tuple <object, object, object> > GetIndexes <T>() where T : IIndexableGrain { return(IndexRegistry.GetIndexes <T>()); }
/// <summary> /// Provides the index information for a given grain interface type. /// </summary> /// <param name="iGrainType">The target grain interface type</param> /// <returns>the index information for the given grain type T. /// The index information is a dictionary from indexIDs defined /// on a grain interface to a triple. The triple consists of: /// 1) the index object (that implements IndexInterface, /// 2) the IndexMetaData object for this index, and /// 3) the IndexUpdateGenerator instance for this index. /// This triple is untyped, because IndexInterface, IndexMetaData /// and IndexUpdateGenerator types are not visible in this project. /// /// This method returns an empty dictionary if the OrleansIndexing /// project is not available.</returns> internal static IDictionary <string, Tuple <object, object, object> > GetIndexes(Type iGrainType) { return(IndexRegistry.GetIndexes(iGrainType)); }
private async static Task GetIndexesForASingleGrainType(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type grainClassType) { if (!typeof(IIndexableGrain).IsAssignableFrom(grainClassType)) { return; } if (registry.ContainsGrainType(grainClassType)) { throw new InvalidOperationException($"Grain class type {grainClassType.Name} has already been added to the registry"); } bool?grainIndexesAreEager = null; var indexedInterfaces = new List <Type>(); var consistencyScheme = grainClassType.GetConsistencyScheme(); foreach (var(grainInterfaceType, propertiesClassType) in EnumerateIndexedInterfacesForAGrainClassType(grainClassType).Where(tup => !registry.ContainsKey(tup.interfaceType))) { grainIndexesAreEager = await CreateIndexesForASingleInterface(loader, registry, propertiesClassType, grainInterfaceType, grainClassType, consistencyScheme, grainIndexesAreEager); indexedInterfaces.Add(grainInterfaceType); } IReadOnlyDictionary <string, object> getNullValuesDictionary() { IEnumerable <(string propName, (string itfName, object nullValue))> getNullPropertyValuesForInterface(Type interfaceType) => registry[interfaceType].PropertiesClassType.GetProperties() .Select(info => (name: info.Name, nullSpec: (itfname: interfaceType.Name, nullValue: IndexUtils.GetNullValue(info)))) .Where(p => p.nullSpec.nullValue != null); Dictionary <string, (string, object)> addToDict(Dictionary <string, (string, object)> dict, (string propName, (string itfName, object nullValue)nullSpec) current) { bool isInDict(string propName) { return(dict.TryGetValue(propName, out (string itfName, object nullValue)prevNullSpec) ? (prevNullSpec.nullValue.Equals(current.nullSpec.nullValue) ? true : throw new IndexConfigurationException($"Property {propName} has conflicting NullValues defined on interfaces {prevNullSpec.itfName} and {current.nullSpec.itfName}")) : false); } if (!isInDict(current.propName)) { dict[current.propName] = current.nullSpec; } return(dict); } return(indexedInterfaces.SelectMany(itf => getNullPropertyValuesForInterface(itf)) .Aggregate(new Dictionary <string, (string itfName, object nullValue)>(), (dict, pair) => addToDict(dict, pair)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.nullValue)); } registry.SetGrainIndexes(grainClassType, indexedInterfaces.ToArray(), getNullValuesDictionary()); }