private static TableDescriptor GetTableDescriptor( Type type, string propertyName, ISerializer tableConfigSerializer, ILogger logger) { Guard.NotNull(type, nameof(type)); TableDescriptor tableDescriptor = null; IEnumerable <RequiresCookerAttribute> cookerAttributes = null; var tableDescriptorPropertyInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static); if (tableDescriptorPropertyInfo != null) { var getMethodInfo = tableDescriptorPropertyInfo.GetMethod; if (!getMethodInfo.IsPublic) { logger?.Warn( $"Type {type} appears to be a Table, but the " + $"{nameof(TableAttribute.TableDescriptorPropertyName)}.get method is not found as public."); return(null); } if (getMethodInfo.ReturnType != typeof(TableDescriptor)) { logger?.Warn( $"Type {type} appears to be a Table, but the " + $"{nameof(TableAttribute.TableDescriptorPropertyName)}.get method must return type " + $"{nameof(TableDescriptor)}."); return(null); } // Allow [RequiresCooker] attribute on the property that returns the TableDescriptor cookerAttributes = tableDescriptorPropertyInfo.GetCustomAttributes <RequiresCookerAttribute>(); tableDescriptor = (TableDescriptor)getMethodInfo.Invoke(null, null); } else { // The table descriptor property didn't exist, check for a field instead. var tableDescriptorFieldInfo = type.GetField(propertyName, BindingFlags.Public | BindingFlags.Static); if (tableDescriptorFieldInfo == null) { logger?.Warn( $"Type {type} appears to be a class, but the " + $"{nameof(TableAttribute.TableDescriptorPropertyName)} property is not found as public and " + "static."); return(null); } // Allow [RequiresCooker] attribute on the property that returns the TableDescriptor cookerAttributes = tableDescriptorFieldInfo.GetCustomAttributes <RequiresCookerAttribute>(); tableDescriptor = tableDescriptorFieldInfo.GetValue(null) as TableDescriptor; } if (tableDescriptor == null) { return(null); } tableDescriptor.Type = type; tableDescriptor.PrebuiltTableConfigurations = TableConfigurations.GetPrebuiltTableConfigurations( type, tableDescriptor.Guid, tableConfigSerializer, logger); foreach (var cooker in cookerAttributes) { tableDescriptor.AddRequiredDataCooker(cooker.RequiredDataCookerPath); } return(tableDescriptor); }
/// <summary> /// If possible, creates a <see cref="TableDescriptor"/> from the /// given <see cref="Type"/>. A <see cref="Type"/> is considered to /// represent a table if the following are true: /// <para /> /// <list type="bullet"> /// <item> /// The <see cref="Type"/> is concrete. That is, the /// <see cref="Type"/> is not abstract, is not static, and /// is not an interface. /// </item> /// <item> /// The <see cref="Type"/> is decorated with the /// <see cref="TableAttribute"/> attribute. /// </item> /// </list> /// <para/> /// </summary> /// <param name="type"> /// The <see cref="Type"/> from which to extract a /// <see cref="TableDescriptor"/>. /// </param> /// <param name="tableConfigSerializer"> /// The serializer object that is used to deserialize the /// pre-built table configuration JSON files. /// </param> /// <param name="logger"> /// Used to log any relevant messages. /// </param> /// <param name="isInternalTable"> /// This table is private to the custom data source. /// </param> /// <param name="tableDescriptor"> /// If <paramref name="type"/> is a valid table type, then this is /// populated with the created <see cref="TableDescriptor"/>. /// Otherwise, this parameter will be set to <c>null</c>. /// </param> /// <param name="buildTableAction"> /// If the type contains a build table action, it is returned here. /// </param> /// <param name="isDataAvailableFunc"> /// If the type contains a has data func, it is returned here. /// </param> /// <returns> /// <c>true</c> if <paramref name="type"/> is a valid table, /// <c>false</c> otherwise. /// </returns> public static bool TryCreate( Type type, ISerializer tableConfigSerializer, ILogger logger, out bool isInternalTable, out TableDescriptor tableDescriptor, out Action <ITableBuilder, IDataExtensionRetrieval> buildTableAction, out Func <IDataExtensionRetrieval, bool> isDataAvailableFunc) { tableDescriptor = null; buildTableAction = null; isDataAvailableFunc = null; isInternalTable = false; if (type is null) { return(false); } var tableAttribute = type.GetCustomAttribute <TableAttribute>(); if (tableAttribute == null) { return(false); } if (!type.IsStatic() && !type.IsConcrete()) { return(false); } isInternalTable = tableAttribute.InternalTable; buildTableAction = GetTableBuildAction(type, tableAttribute.BuildTableActionMethodName); isDataAvailableFunc = GetIsDataAvailableFunc(type, tableAttribute.IsDataAvailableMethodName); tableDescriptor = GetTableDescriptor( type, tableAttribute.TableDescriptorPropertyName, tableConfigSerializer, logger); if (tableDescriptor == null) { return(false); } // I'm looking at this for the first time in months, and the certain tables aren't showing up because they're not specifically marked as internal // and they don't have build actions on them either. So for now, I'm going to try marking any table that doesn't have a build action as internal // to see if that will fix the issue. // todo:revisit this if (buildTableAction == null) { isInternalTable = true; } if (tableDescriptor.IsMetadataTable) { isInternalTable = true; } var cookerAttributes = type.GetCustomAttributes <RequiresCookerAttribute>(); foreach (var cooker in cookerAttributes) { tableDescriptor.AddRequiredDataCooker(cooker.RequiredDataCookerPath); } // todo:should we make this optional, or does it makes sense to always include this? // we could add an "IncludeType" bool or something on the table attribute if we don't want // this to always be set. tableDescriptor.ExtendedData["Type"] = type; return(true); }