private static int getCount(String action, IEntity target, EntityInfo entityInfo, EntityPropertyInfo info, Object obj) { if (obj == null) { return(1); } String usql; IDatabaseDialect dialect = entityInfo.Dialect; if (action.Equals("update")) { usql = String.Format("select count(Id) from {0} where Id<>{3} and {1}={2}", entityInfo.TableName, info.ColumnName, dialect.GetParameter(info.Name), target.Id); } else { usql = String.Format("select count(Id) from {0} where {1}={2}", entityInfo.TableName, info.ColumnName, dialect.GetParameter(info.Name)); } logger.Info(LoggerUtil.SqlPrefix + " validate unique sql : " + usql); IDbCommand cmd = DataFactory.GetCommand(usql, DbContext.getConnection(entityInfo)); DataFactory.SetParameter(cmd, info.ColumnName, obj); return(cvt.ToInt(cmd.ExecuteScalar())); }
/// <summary> /// Initializes a new instance of the <see cref="T:Ceen.Database.ColumnMapping"/> class. /// </summary> /// <param name="dialect">The database dialect.</param> /// <param name="property">The property to map.</param> public ColumnMapping(IDatabaseDialect dialect, PropertyInfo property) { Name = dialect.GetName(property); IsPrimaryKey = property.GetCustomAttributes <PrimaryKeyAttribute>(true).Any(); SqlType = dialect.GetSqlColumnType(property); Property = property; }
public ReflectionTableColumn(IDatabaseDialect dialect, PropertyInfo prop, Type declaredColumnType, bool isNullable) { Property = prop ?? throw new ArgumentNullException(nameof(prop)); Dialect = dialect ?? throw new ArgumentNullException(nameof(dialect)); if (declaredColumnType == null) { throw new ArgumentNullException(nameof(declaredColumnType)); } Name = dialect.GetAliasOrDefault(prop); var clrType = GetClrType(declaredColumnType); if (clrType == null) { throw new ArgumentNullException($"The declared column type does not implement IDbType<T>. Check { prop.ReflectedType.FullName }.{ prop.Name } and ensure that the column type { declaredColumnType.FullName } implements this interface.", nameof(declaredColumnType)); } var columnType = new ReflectionColumnDataType(dialect, declaredColumnType, clrType); var autoIncrAttr = dialect.GetDialectAttribute <AutoIncrementAttribute>(declaredColumnType) ?? dialect.GetDialectAttribute <AutoIncrementAttribute>(prop); if (autoIncrAttr != null) { if (!ValidAutoIncrementTypes.Contains(columnType.DataType)) { throw new ArgumentNullException($"The column { prop.ReflectedType.FullName }.{ prop.Name } is declared as being auto incrementing, which is not supported on a '{ columnType.DataType }' data type.", nameof(declaredColumnType)); } AutoIncrement = new AutoIncrement(autoIncrAttr.InitialValue, autoIncrAttr.Increment); } Type = columnType; IsNullable = isNullable; }
public string ToSql(IDatabaseDialect dialect) { if (Tokens.Empty()) { return(ExpressionText); } var builder = StringBuilderCache.Acquire(ExpressionText.Length); var tokenValues = GetTokenValues(dialect).ToList(); var tokenInfo = Tokens.Zip(tokenValues, (t, v) => { var startPos = t.Position.Absolute; var endPos = startPos + t.Span.Length; return(new { Start = startPos, End = endPos, Value = v }); }).ToList(); var firstInfo = tokenInfo[0]; if (firstInfo.Start > 0) { builder.Append(ExpressionText, 0, firstInfo.Start); } var prevEnd = -1; foreach (var info in tokenInfo) { if (prevEnd < info.Start) { prevEnd = Math.Max(0, prevEnd); var text = ExpressionText[prevEnd..info.Start];
public static void Ctor_GivenNullDialect_ThrowsArgumentNullException() { IDatabaseDialect dialect = null; const RuleLevel level = RuleLevel.Error; Assert.That(() => new ReservedKeywordNameRule(dialect, level), Throws.ArgumentNullException); }
/// <summary> /// Retrieves the resolved schema-qualified name for an object type. /// </summary> /// <param name="dialect">A dialect that the name should be qualified for.</param> /// <param name="database">The database that an object should be qualified for.</param> /// <param name="type">The type of object that the attribute is applied to.</param> /// <returns>A schema-qualified name for a database object.</returns> /// <exception cref="ArgumentNullException"><paramref name="dialect"/>, <paramref name="database"/>, or <paramref name="type"/> is <c>null</c></exception> public static Identifier GetQualifiedNameOrDefault(this IDatabaseDialect dialect, IRelationalDatabase database, Type type) { if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } if (database == null) { throw new ArgumentNullException(nameof(dialect)); } if (type == null) { throw new ArgumentNullException(nameof(type)); } var schemaName = dialect.GetSchemaOverride(type); if (schemaName.IsNullOrWhiteSpace()) { schemaName = database.IdentifierDefaults.Schema; } var localName = dialect.GetAliasOrDefault(type); return(Identifier.CreateQualifiedIdentifier(schemaName, localName)); }
private static void checkConnectionString(DbConfig result) { logger.Info("checkConnectionString..."); if (result.ConnectionStringTable == null) { return; } Dictionary <String, ConnectionString> connStringMap = new Dictionary <String, ConnectionString>(); Dictionary <String, String> newString = new Dictionary <String, String>(); foreach (KeyValuePair <String, String> kv in result.ConnectionStringTable) { String connectionString = kv.Value; DatabaseType dbtype = getDbType(kv.Key, connectionString, result); ConnectionString objConnString = new ConnectionString { Name = kv.Key, StringContent = connectionString, DbType = dbtype }; connStringMap.Add(kv.Key, objConnString); logger.Info("connectionString:" + connectionString); IDatabaseDialect dialect = DataFactory.GetDialect(dbtype); if ((dbtype == DatabaseType.Access)) { String connectionItem = dialect.GetConnectionItem(connectionString, ConnectionItemType.Database); logger.Info("database path original:" + connectionItem); if (connectionItem == null) { throw new Exception("没有设置access地址:" + connectionString); } if (IsRelativePath(connectionItem)) { connectionItem = PathHelper.Map(strUtil.Join(SystemInfo.ApplicationPath, connectionItem)); logger.Info("database path now:" + connectionItem); String newConnString = String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}", connectionItem); newString.Add(kv.Key, newConnString); } } } foreach (KeyValuePair <String, String> kv in newString) { result.ConnectionStringTable[kv.Key] = kv.Value; connStringMap[kv.Key].StringContent = kv.Value; } result.SetConnectionStringMap(connStringMap); }
public ReflectionColumnDataType(IDatabaseDialect dialect, Type columnType, Type clrType) { if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } if (columnType == null) { throw new ArgumentNullException(nameof(columnType)); } if (clrType == null) { throw new ArgumentNullException(nameof(clrType)); } var attr = dialect.GetDialectAttribute <DeclaredTypeAttribute>(columnType); if (attr == null) { throw new ArgumentException($"The column type { columnType.FullName } does not contain a definition for the dialect { dialect.GetType().FullName }"); } var typeProvider = dialect.TypeProvider; if (typeProvider == null) { throw new ArgumentException("The given dialect does not contain a valid type provider.", nameof(dialect)); } var collationAttr = dialect.GetDialectAttribute <CollationAttribute>(columnType); var typeMetadata = new ColumnTypeMetadata { ClrType = clrType, Collation = collationAttr?.CollationName != null ? Option <Identifier> .Some(collationAttr.CollationName) : Option <Identifier> .None, DataType = attr.DataType, IsFixedLength = attr.IsFixedLength, MaxLength = attr.Length, NumericPrecision = attr.Precision > 0 && attr.Scale > 0 ? Option <INumericPrecision> .Some(new NumericPrecision(attr.Precision, attr.Scale)) : Option <INumericPrecision> .None, }; var dbType = typeProvider.CreateColumnType(typeMetadata); // map dbType to properties, avoids keeping a reference TypeName = dbType.TypeName; DataType = dbType.DataType; Definition = dbType.Definition; IsFixedLength = dbType.IsFixedLength; MaxLength = dbType.MaxLength; ClrType = dbType.ClrType; NumericPrecision = dbType.NumericPrecision; Collation = dbType.Collation; }
public ReflectionSequence(IRelationalDatabase database, IDatabaseDialect dialect, Type sequenceType) { if (database == null) { throw new ArgumentNullException(nameof(database)); } if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } if (sequenceType == null) { throw new ArgumentNullException(nameof(sequenceType)); } var typeInfo = sequenceType.GetTypeInfo(); if (!typeInfo.ImplementedInterfaces.Contains(ISequenceType)) { throw new ArgumentException($"The sequence type { typeInfo.FullName } must implement the { ISequenceType.FullName } interface.", nameof(sequenceType)); } var ctor = sequenceType.GetDefaultConstructor(); if (ctor == null) { throw new ArgumentException($"The sequence type { typeInfo.FullName } does not contain a default constructor.", nameof(sequenceType)); } var instance = ctor.Invoke(Array.Empty <object>()) as ISequence; var sequenceName = dialect.GetQualifiedNameOrDefault(database, sequenceType); var minValue = instance !.MinValue.ToOption(); var maxValue = instance !.MaxValue.ToOption(); // create an inner sequence, which will perform validation var sequence = new DatabaseSequence( sequenceName, instance !.Start, instance !.Increment, minValue, maxValue, instance !.Cycle, instance !.Cache ); Cache = sequence.Cache; Cycle = sequence.Cycle; Increment = sequence.Increment; MaxValue = maxValue; MinValue = minValue; Name = sequenceName; Start = sequence.Start; }
public ReflectionTypeProvider(IDatabaseDialect dialect, Type databaseDefinitionType) { Dialect = dialect ?? throw new ArgumentNullException(nameof(dialect)); DatabaseDefinitionType = databaseDefinitionType ?? throw new ArgumentNullException(nameof(databaseDefinitionType)); _tables = new Lazy <IEnumerable <Type> >(LoadTables); _views = new Lazy <IEnumerable <Type> >(LoadViews); _sequences = new Lazy <IEnumerable <Type> >(LoadSequences); _synonyms = new Lazy <IEnumerable <Type> >(LoadSynonyms); _dbProperties = DatabaseDefinitionType.GetProperties(); }
/// <summary> /// Gets the database dialect for a connection /// </summary> /// <returns>The dialect for the connection.</returns> /// <param name="connection">The connection to get the dialect for.</param> /// <param name="defaultOverride">A dialect that is selected instead of the default.</param> public static IDatabaseDialect GetDialect(this IDbConnection connection, IDatabaseDialect defaultOverride = null) { lock (_dialectLock) { if (!_dialect.TryGetValue(connection, out var res)) { _dialect.Add(connection, res = defaultOverride ?? DefaultDialect); } return(res); } }
/// <summary> /// Initializes a new instance of the <see cref="T:Ceen.Database.ColumnMapping"/> class. /// </summary> /// <param name="dialect">The database dialect.</param> /// <param name="member">The member to map.</param> /// <param name="memberType">The member type</param> private ColumnMapping(IDatabaseDialect dialect, MemberInfo member, Type memberType) { Member = member ?? throw new ArgumentNullException(nameof(member)); MemberType = memberType; ColumnName = dialect.GetName(member); QuotedColumnName = dialect.QuoteName(ColumnName); IsPrimaryKey = member.GetCustomAttributes <PrimaryKeyAttribute>(true).Any(); var sqlType = dialect.GetSqlColumnType(member); SqlType = sqlType.Item1; AutoGenerateAction = sqlType.Item2; ValidationRules = member.GetCustomAttributes <ValidationBaseAttribute>(true).ToArray(); }
public ReflectionTableComputedColumn(IDatabaseDialect dialect, IRelationalDatabaseTable table, Identifier columnName, string definition) { if (definition.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(definition)); } Definition = definition; Dialect = dialect ?? throw new ArgumentNullException(nameof(dialect)); Name = columnName; Table = table ?? throw new ArgumentNullException(nameof(table)); IsNullable = true; }
public static IList FindBySql(String sql, Type type) { IDatabaseDialect dialect = Entity.GetInfo(type).Dialect; sql = dialect.GetLimit(sql); logger.Info(String.Format("{0}[FindBySql]{1}", LoggerUtil.SqlPrefix, sql)); ObjectInfo state = new ObjectInfo(type); // EntityFactory.New( type.FullName ).state; state.Includer.IncludeAll(); return(EntityPropertyUtil.FindList(state, sql)); }
public ReflectionView(IRelationalDatabase database, IDatabaseDialect dialect, Type viewType) { if (database == null) { throw new ArgumentNullException(nameof(database)); } if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } ViewType = viewType ?? throw new ArgumentNullException(nameof(viewType)); Name = dialect.GetQualifiedNameOrDefault(database, ViewType); }
public ReflectionRoutine(IRelationalDatabase database, IDatabaseDialect dialect, Type routineType) { if (database == null) { throw new ArgumentNullException(nameof(database)); } if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } RoutineType = routineType ?? throw new ArgumentNullException(nameof(routineType)); Name = dialect.GetQualifiedNameOrDefault(database, RoutineType); }
public ReflectionRelationalDatabase(IDatabaseDialect dialect, Type databaseDefinitionType, IIdentifierDefaults identifierDefaults) { Dialect = dialect ?? throw new ArgumentNullException(nameof(dialect)); DatabaseDefinitionType = databaseDefinitionType ?? throw new ArgumentNullException(nameof(databaseDefinitionType)); IdentifierDefaults = identifierDefaults ?? throw new ArgumentNullException(nameof(identifierDefaults)); TypeProvider = new ReflectionTypeProvider(dialect, databaseDefinitionType); EnsureUniqueTypes(DatabaseDefinitionType, TypeProvider); _tableLookup = new Lazy <IReadOnlyDictionary <Identifier, IRelationalDatabaseTable> >(LoadTables); _viewLookup = new Lazy <IReadOnlyDictionary <Identifier, IDatabaseView> >(LoadViews); _sequenceLookup = new Lazy <IReadOnlyDictionary <Identifier, IDatabaseSequence> >(LoadSequences); _synonymLookup = new Lazy <IReadOnlyDictionary <Identifier, IDatabaseSynonym> >(LoadSynonyms); _routineLookup = new Lazy <IReadOnlyDictionary <Identifier, IDatabaseRoutine> >(LoadRoutines); }
/// <summary> /// Provides an alias for a type, or the type name. /// </summary> /// <param name="dialect">A dialect that the alias applies to.</param> /// <param name="type">The type of object that the attribute is applied to.</param> /// <returns>An alias for a type if available, the type's name otherwise.</returns> /// <exception cref="ArgumentNullException"><paramref name="dialect"/> or <paramref name="type"/> is <c>null</c>.</exception> public static string GetAliasOrDefault(this IDatabaseDialect dialect, Type type) { if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } if (type == null) { throw new ArgumentNullException(nameof(type)); } var aliasAttr = dialect.GetDialectAttribute <AliasAttribute>(type); return(aliasAttr?.Alias ?? type.Name); }
/// <summary> /// Provides an alias for a property, or the property name. /// </summary> /// <param name="dialect">A dialect that the alias applies to.</param> /// <param name="property">A property that may contain an alias attribute.</param> /// <returns>A name that should be used for the property, which is an alias if one is available, or the property name otherwise.</returns> /// <exception cref="ArgumentNullException"><paramref name="dialect"/> or <paramref name="property"/> is <c>null</c>.</exception> public static string GetAliasOrDefault(this IDatabaseDialect dialect, PropertyInfo property) { if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } if (property == null) { throw new ArgumentNullException(nameof(property)); } var aliasAttr = dialect.GetDialectAttribute <AliasAttribute>(property); return(aliasAttr?.Alias ?? property.Name); }
/// <summary> /// Provides schema override for a type. /// </summary> /// <param name="dialect">A dialect that the schema override applies to.</param> /// <param name="type">The type of object that a schema override attribute may be applied to.</param> /// <returns>A schema override for a type if available, otherwise <c>null</c>.</returns> /// <exception cref="ArgumentNullException"><paramref name="dialect"/> or <paramref name="type"/> is <c>null</c></exception> public static string?GetSchemaOverride(this IDatabaseDialect dialect, Type type) { if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } if (type == null) { throw new ArgumentNullException(nameof(type)); } var nameAttr = dialect.GetDialectAttribute <SchemaAttribute>(type); return(nameAttr?.Schema); }
/// <summary> /// Constructs a new guarded connection /// </summary> /// <param name="connection">The connection to use</param> public GuardedConnection(IDbConnection connection) { m_con = connection ?? throw new ArgumentNullException(nameof(connection)); if (m_con.State != ConnectionState.Open) { m_con.Open(); } if (m_con.State != ConnectionState.Open) { throw new ArgumentException("The database connection was not open and did not open"); } m_locker = (m_dialect = m_con.GetDialect()).IsMultiThreadSafe ? new Task <AsyncLock.Releaser>(null) : null; }
public TableOrderingRenderer( IDatabaseDialect dialect, IReadOnlyCollection <IRelationalDatabaseTable> tables, DirectoryInfo exportDirectory) { if (tables == null || tables.AnyNull()) { throw new ArgumentNullException(nameof(tables)); } Tables = tables; Dialect = dialect ?? throw new ArgumentNullException(nameof(dialect)); ExportDirectory = exportDirectory ?? throw new ArgumentNullException(nameof(exportDirectory)); }
public ReflectionTable(IRelationalDatabase database, IDatabaseDialect dialect, Type tableType) { Database = database ?? throw new ArgumentNullException(nameof(database)); Dialect = dialect ?? throw new ArgumentNullException(nameof(dialect)); InstanceType = tableType ?? throw new ArgumentNullException(nameof(tableType)); Name = Dialect.GetQualifiedNameOrDefault(database, InstanceType); TypeProvider = new ReflectionTableTypeProvider(Dialect, InstanceType); _columns = new Lazy <IReadOnlyList <IDatabaseColumn> >(LoadColumnList); _checkLookup = new Lazy <IReadOnlyDictionary <Identifier, IDatabaseCheckConstraint> >(LoadChecks); _uniqueKeyLookup = new Lazy <IReadOnlyDictionary <Identifier, IDatabaseKey> >(LoadUniqueKeys); _indexLookup = new Lazy <IReadOnlyDictionary <Identifier, IDatabaseIndex> >(LoadIndexes); _parentKeyLookup = new Lazy <IReadOnlyDictionary <Identifier, IDatabaseRelationalKey> >(LoadParentKeys); _childKeys = new Lazy <IReadOnlyCollection <IDatabaseRelationalKey> >(LoadChildKeys); _primaryKey = new Lazy <Option <IDatabaseKey> >(LoadPrimaryKey); }
public void CheckDatabase() { if (strUtil.IsNullOrEmpty(_connectionString)) { throw new Exception("[sqlserver] connection String is not found"); } IDatabaseDialect dialect = DataFactory.GetDialect(DatabaseType.SqlServer); if (strUtil.IsNullOrEmpty(dialect.GetConnectionItem(_connectionString, ConnectionItemType.Server))) { throw new Exception("[sqlserver] address is empty"); } if (strUtil.IsNullOrEmpty(dialect.GetConnectionItem(_connectionString, ConnectionItemType.Database))) { throw new Exception("[sqlserver] database is empty"); } }
public void CheckDatabase() { if (strUtil.IsNullOrEmpty(this._connectionString)) { throw new Exception("[MySQL] 数据库连接字符串未设置"); } IDatabaseDialect dialect = DataFactory.GetDialect(DatabaseType.MySql); if (strUtil.IsNullOrEmpty(dialect.GetConnectionItem(this._connectionString, ConnectionItemType.Server))) { throw new Exception("[MySQL] 未指定目标数据库服务器地址"); } if (strUtil.IsNullOrEmpty(dialect.GetConnectionItem(_connectionString, ConnectionItemType.Database))) { throw new Exception("[MySQL] 未指定目标数据库名称"); } }
public void CheckDatabase() { if (strUtil.IsNullOrEmpty(this._connectionString)) { throw new Exception("connection string can not be empty"); } IDatabaseDialect dialect = DataFactory.GetDialect(DatabaseType.MySql); if (strUtil.IsNullOrEmpty(dialect.GetConnectionItem(this._connectionString, ConnectionItemType.Server))) { throw new Exception("[mysql] server address is empty"); } if (strUtil.IsNullOrEmpty(dialect.GetConnectionItem(_connectionString, ConnectionItemType.Database))) { throw new Exception("[mysql] database is empty"); } }
public ReflectionTableTypeProvider(IDatabaseDialect dialect, Type tableType) { Dialect = dialect ?? throw new ArgumentNullException(nameof(dialect)); TableType = tableType ?? throw new ArgumentNullException(nameof(tableType)); if (tableType.IsAbstract) { throw new ArgumentException($"The given table type '{ tableType.FullName }' is abstract. A non-abstract table type must be provided.", nameof(tableType)); } _columns = new Lazy <IReadOnlyCollection <IModelledColumn> >(LoadColumns); _checks = new Lazy <IReadOnlyCollection <IModelledCheckConstraint> >(LoadChecks); _primaryKey = new Lazy <IModelledKey?>(LoadPrimaryKey); _uniqueKeys = new Lazy <IReadOnlyCollection <IModelledKey> >(LoadUniqueKeys); _parentKeys = new Lazy <IReadOnlyCollection <IModelledRelationalKey> >(LoadParentKeys); _indexes = new Lazy <IReadOnlyCollection <IModelledIndex> >(LoadIndexes); TableProperties = TableType.GetProperties(); TableInstance = CreateTableInstance(); }
public ReflectionSynonym(IRelationalDatabase database, IDatabaseDialect dialect, Type synonymType) { if (database == null) { throw new ArgumentException(nameof(database)); } if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } if (synonymType == null) { throw new ArgumentNullException(nameof(synonymType)); } Name = dialect.GetQualifiedNameOrDefault(database, synonymType); var targetType = GetBaseGenericTypeArg(synonymType); Target = dialect.GetQualifiedNameOrDefault(database, targetType); }
/// <summary> /// Retrieves an attribute that applies to a specific dialect. /// </summary> /// <typeparam name="T">The attribute to retrieve.</typeparam> /// <param name="dialect">A dialect that the attribute should apply to.</param> /// <param name="property">The property that the attribute is applied to.</param> /// <returns>An attribute for the given property. This will be <c>null</c> when no attribute is present.</returns> /// <exception cref="ArgumentNullException"><paramref name="dialect"/> or <paramref name="property"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">More than one matching attribute was found.</exception> public static T GetDialectAttribute <T>(this IDatabaseDialect dialect, PropertyInfo property) where T : ModelledSchemaAttribute { if (dialect == null) { throw new ArgumentNullException(nameof(dialect)); } if (property == null) { throw new ArgumentNullException(nameof(property)); } var dialectType = dialect.GetType(); var attrs = property.GetCustomAttributes <T>(true) .Where(attr => attr.SupportsDialect(dialectType)) .ToList(); if (attrs.Count > 1) { throw new ArgumentException($"More than one matching { typeof(T).FullName } attribute was found for the property { property.Name } in { property.ReflectedType.FullName }.", nameof(property)); } return(attrs.SingleOrDefault()); }
/// <summary> /// Initializes a new instance of the <see cref="T:Ceen.Database.TableMapping"/> class. /// </summary> /// <param name="dialect">The database dialect.</param> /// <param name="type">The type to mape.</param> /// <param name="nameoverride">An optional name override for the table name</param> public TableMapping(IDatabaseDialect dialect, Type type, string nameoverride = null) { Dialect = dialect ?? throw new ArgumentNullException(nameof(dialect)); Type = type; Name = nameoverride ?? dialect.GetName(type); AllColumns = type .GetProperties() .Where(x => !x.GetCustomAttributes <IgnoreAttribute>(true).Any()) .Select(x => new ColumnMapping(dialect, x)) .ToArray(); ColumnsWithoutPrimaryKey = AllColumns.Where(x => !x.IsPrimaryKey).ToArray(); PrimaryKeys = AllColumns.Where(x => x.IsPrimaryKey).ToArray(); AllColumnsBySqlName = AllColumns.ToDictionary(x => x.Name, x => x); AllColumnsByPropertyName = AllColumns.ToDictionary(x => x.Property.Name, x => x); // Build the unique maps var uniques = AllColumns .Select(x => new Tuple <ColumnMapping, string[]>(x, x.Property.GetCustomAttributes <UniqueAttribute>().Select(y => y.Group).ToArray())) .Where(x => x.Item2 != null && x.Item2.Length > 0); var solos = uniques.Where(x => x.Item2.Contains(null)); var groups = uniques .SelectMany(x => x.Item2 .Where(y => y != null) .Select(y => new KeyValuePair <string, ColumnMapping>(y, x.Item1)) ) .GroupBy(x => x.Key); Uniques = solos .Select(x => new UniqueMapping(null, new ColumnMapping[] { x.Item1 })) .Concat( groups.Select(x => new UniqueMapping(x.Key, x.Select(y => y.Value).ToArray())) ).ToArray(); }