// TODO: This is a complicated method, and the fact that it directly uses reflection to read attribute // data indicates that it is a good candidate for refactoring. In the future, we should research migrating // to either a code-generated or database-driven approach for storing this data. public SearchQuery GetQuery <T>(T dto) { var query = new SearchQuery() { RuleSets = new List <SearchRuleSet>() }; // Get the Master Index Type. var masterIndexTypeAttr = dto.GetType().GetCustomAttribute <MasterIndexTypeAttribute>(true); if (masterIndexTypeAttr != null) { query.TableName = masterIndexTypeAttr.MasterType.GetDescription(); // Evaluate object properties. foreach (PropertyInfo prop in dto.GetType().GetProperties()) { EvaluateProperty(query, prop, dto); } // Add a field for master link. SearchQueryField masterLinkField; switch (masterIndexTypeAttr.MasterType) { case MasterIndexType.Name: masterLinkField = new SearchQueryField { Name = MasterIndexSettings.MASTER_LINK_FIELD, ColumnName = MasterIndexSettings.MASTER_NAME_LINK_FIELD }; break; case MasterIndexType.Address: masterLinkField = new SearchQueryField { Name = MasterIndexSettings.MASTER_LINK_FIELD, ColumnName = MasterIndexSettings.MASTER_ADDRESS_LINK_FIELD }; break; case MasterIndexType.Vehicle: masterLinkField = new SearchQueryField { Name = MasterIndexSettings.MASTER_LINK_FIELD, ColumnName = MasterIndexSettings.MASTER_VEHICLE_LINK_FIELD }; break; case MasterIndexType.Property: masterLinkField = new SearchQueryField { Name = MasterIndexSettings.MASTER_LINK_FIELD, ColumnName = MasterIndexSettings.MASTER_PROPERTY_LINK_FIELD }; break; // TODO: Others... default: masterLinkField = new SearchQueryField(); break; } query.RuleSets.ForEach(s => s.Fields.Add(masterLinkField)); } return(query); }
/// <summary> /// Recursively generates query fields from the given property. /// </summary> private void EvaluateProperty(SearchQuery query, PropertyInfo prop, object dto) { // Find the Search attribute. var searchAttr = Attribute.GetCustomAttribute(prop, typeof(MasterIndexSearchAttribute)) as MasterIndexSearchAttribute; if (searchAttr != null && searchAttr.Searchable) { // If the property is not marked as including nested properties (or if it is a value type), // then create query fields using its value. if (!searchAttr.IncludeNestedProperties || prop.PropertyType.IsValueType) { // Only create a field if it has a non-null RMS column name. if (!String.IsNullOrWhiteSpace(searchAttr.RmsClassicColumnName)) { // Get the value of the property. string searchValue = GetPropertyValue(prop, dto); // Create the query field. var field = new SearchQueryField() { QueryValue = searchValue, Name = prop.Name, ColumnName = searchAttr.RmsClassicColumnName, Weight = searchAttr.FieldWeight, SearchType = searchAttr.SearchType, Required = searchAttr.Required }; // Create the appropriate rule sets for the field's priority. foreach (var priority in searchAttr.Priorities) { SearchRuleSet existing = query.RuleSets.FirstOrDefault(r => r.Priority == priority); if (existing == null) { existing = new SearchRuleSet() { Priority = priority, Fields = new List <SearchQueryField>() }; query.RuleSets.Add(existing); } existing.Fields.Add(field); } } } else { // If the property is marked to include nested properties (or if it is a reference type), then // recursively look through all of its properties to see if any of them should be included. // TODO: This ignores the MasterIndexType attribute of the property's containing class. Therefore, // this will assume that all column names defined by all nested properties exist on the table // defined by the MasterIndexType on the root instance. For instance, if Organization uses table // "dbo.organization", then Organization.Address.StreetAddress will use the same table even // though StreetAddress is a member of a type that is configured to look at "dbo.masterAddress". foreach (var nestedProp in prop.PropertyType.GetProperties()) { EvaluateProperty(query, nestedProp, prop.GetValue(dto)); } } } }