/** * Given a PropertyInfo we will work out if it is a Many to One * relationship. For it to be a Many to One relationship, the current * TModel must have a property that is a List of a foreign type. */ protected virtual Relation?IsManyToOne(PropertyInfo localProp) { // The relationship desciptor we will return // if we actually find a valid relationship. var relation = new Relation { Type = Relation.RelationType.MtoO }; // Grab the local and foreign types. relation.LocalType = localProp.DeclaringType; relation.ForeignType = localProp.PropertyType.GenericTypeArguments[0]; // Grab the sql table names for both model types. relation = this.SetTableNames(relation); // Many to One relationships will always store // the foreign key in the foreign table. relation.ForeignKeyTableName = relation.ForeignTableName; // Get all local properties that are lists of the foreignType. var localProps = Model.Dynamic(relation.LocalType).MappedProps .Where(lp => TypeMapper.IsListOfEntities(lp, relation.ForeignType)).ToList(); // Get all foreign properties that are of the localType. var foreignProps = Model.Dynamic(relation.ForeignType).MappedProps .Where(fp => fp.PropertyType == relation.LocalType).ToList(); // If we can't find any matching properties on the foreign // side well we don't have a Many to One relationship. if (foreignProps.Count == 0) { return(null); } // If there is 1 local and 1 foreign property that reference each // other, we have a simple, everyday, Many to One relationship. if (localProps.Count == 1 && foreignProps.Count == 1) { relation.LocalProperty = localProp; relation.ForeignProperty = foreignProps[0]; relation.ForeignKeyColumnName = relation.LocalTableNameSingular + "Id"; } // If we have a single local property but no foreign property. // We have a "Lazy" or "One Way" Many to One relationship. // // > NOTE: I think I have decided that lazy relationships are // > a pain in the arse and should not be allowed... /*else if (localProps.Count == 1 && foreignProps.Count == 0) * { * relation.LocalProperty = localProp; * relation.ForeignProperty = null; * relation.ForeignKeyColumnName = relation.LocalTableNameSingular + "Id"; * }*/ // If there are multiple properties that reference each other, // we need to create the foreign key based on the property name // instead. So that the relationships remains unique. else if (localProps.Count > 1 || foreignProps.Count > 1) { PropertyInfo foreignProp; // First lets check for any explictly set InversePropertyAttributes. var foreignInverseProp = localProp.GetCustomAttribute <InversePropertyAttribute>(false); if (foreignInverseProp != null) { // Sweet the local property is telling is // exactly which foreign property to use. foreignProp = foreignProps.Single ( fp => fp.Name == foreignInverseProp.Value ); } else { // Lets see if any of the foreign properties have an // InversePropertyAttribute that points to our // local property. foreignProp = foreignProps .Where(fp => fp.GetCustomAttribute <InversePropertyAttribute>(false) != null) .SingleOrDefault(fp => fp.GetCustomAttribute <InversePropertyAttribute>(false).Value == localProp.Name); } // Now lets attempt to find our inverse property by looking at the property names. if (foreignProp == null) { // Determin the relationship link id. relation.LinkIdentifier = localProp.Name.Replace ( relation.ForeignTableName, String.Empty ); // We should have at least one foreign // property that contains the LinkIdentifier. try { foreignProp = foreignProps.Single ( fp => fp.Name.Contains ( relation.LinkIdentifier ) ); } catch { // Okay so we couldn't find a matching property. // We still have a ManyToOne relationship, its a Lazy One, // that requires a unique foreign key column name. // // > NOTE: I think I have decided that lazy relationships // > are a pain in the arse and should not be allowed... //foreignProp = null; // We couldn't find a matching property // so we don't have a relationship. return(null); } } else { // If the forgeign poroperty was found by means of a // InversePropertyAttribute then we still need a // LinkIdentifier so we need to make one. relation.LinkIdentifier = localProp.Name + foreignProp.Name; } // Save both navigational properties. relation.LocalProperty = localProp; relation.ForeignProperty = foreignProp; // Set the foreign key column name relation.ForeignKeyColumnName = relation.LocalTableNameSingular + relation.LinkIdentifier + "Id"; } else { return(null); } // Finally return the relationship descriptor. return(relation); }
/** * Given a PropertyInfo we will work out if it is a One to Many * relationship. For it to be a One to Many relationship, the current * "TModel" must have a property that refers to a single foreign type * and the foreign type must have a property that is a List of "TModel". */ public Relation?IsOneToMany(PropertyInfo localProp) { // The relationship desciptor we will return // if we actually find a valid relationship. var relation = new Relation { Type = Relation.RelationType.OtoM }; // Grab the local and foreign types. relation.LocalType = localProp.DeclaringType; relation.ForeignType = localProp.PropertyType; // Grab the sql table names for both model types. relation = this.SetTableNames(relation); // One to Many relationships will always store // the foreign key in the local table. relation.ForeignKeyTableName = relation.LocalTableName; // Get all local properties that are of the foreign type. var localProps = Model.Dynamic(relation.LocalType).MappedProps .Where(lp => lp.PropertyType == relation.ForeignType).ToList(); // Get all foreign properties that are lists of the local type. var foreignProps = Model.Dynamic(relation.ForeignType).MappedProps .Where(fp => TypeMapper.IsListOfEntities(fp, relation.LocalType)).ToList(); // If we can't find any matching properties on the foreign // side well we don't have a One to Many relationship. if (foreignProps.Count == 0) { return(null); } // If there is only 1 local and 1 foreign property that refrence // each other then we create the foreign key based on the model // names. This is a simple every day One to Many relationship. else if (localProps.Count == 1 && foreignProps.Count == 1) { // Save both navigational properties. relation.LocalProperty = localProp; relation.ForeignProperty = foreignProps[0]; // Set the foreign key column name relation.ForeignKeyColumnName = relation.ForeignTableNameSingular + "Id"; } // If there are multiple properties that reference each other, // we need to create the foreign key based on the property name // instead. So that the relationships remains unique. else if (localProps.Count > 1 || foreignProps.Count > 1) { PropertyInfo foreignProp; // First lets check for any explictly set InversePropertyAttributes. var foreignInverseProp = localProp.GetCustomAttribute <InversePropertyAttribute>(false); if (foreignInverseProp != null) { // Sweet the local property is telling is // exactly which foreign property to use. foreignProp = foreignProps.Single ( fp => fp.Name == foreignInverseProp.Value ); } else { // Lets see if any of the foreign properties have an // InversePropertyAttribute that points to our // local property. foreignProp = foreignProps .Where(fp => fp.GetCustomAttribute <InversePropertyAttribute>(false) != null) .SingleOrDefault(fp => fp.GetCustomAttribute <InversePropertyAttribute>(false).Value == localProp.Name); } // Now lets attempt to find our inverse property by looking at the property names. if (foreignProp == null) { // Determin the relationship link id. relation.LinkIdentifier = localProp.Name.Replace ( relation.ForeignTableNameSingular, String.Empty ); // We should have at least one foreign // property that contains the LinkIdentifier. try { foreignProp = foreignProps.Single ( fp => fp.Name.Contains ( relation.LinkIdentifier ) ); } catch { // We couldn't find a matching property // so we don't have a relationship. return(null); } } else { // If the forgeign poroperty was found by means of a // InversePropertyAttribute then we still need a // LinkIdentifier so we need to make one. relation.LinkIdentifier = localProp.Name + foreignProp.Name; } // Save both navigational properties. relation.LocalProperty = localProp; relation.ForeignProperty = foreignProp; // Set the foreign key column name relation.ForeignKeyColumnName = relation.ForeignTableNameSingular + relation.LinkIdentifier + "Id"; } else { return(null); } // Finally return the relationship descriptor. return(relation); }
/** * Given a PropertyInfo we will work out if it is a Many to Many * relationship. For it to be a Many to Many relationship, both the * current TModel and the related TModel must have a List of each * others type. */ protected virtual Relation?IsManyToMany(PropertyInfo localProp) { // The relationship desciptor we will return // if we actually find a valid relationship. var relation = new Relation { Type = Relation.RelationType.MtoM }; // Grab the local and foreign types. relation.LocalType = localProp.DeclaringType; relation.ForeignType = localProp.PropertyType.GenericTypeArguments[0]; // Grab the sql table names for both model types. relation = this.SetTableNames(relation); // Get all local properties that are lists of the foreignType. var localProps = Model.Dynamic(relation.LocalType).MappedProps .Where(lp => TypeMapper.IsListOfEntities(lp, relation.ForeignType)).ToList(); // Get all foreign properties that are lists of the localType. var foreignProps = Model.Dynamic(relation.ForeignType).MappedProps .Where(fp => TypeMapper.IsListOfEntities(fp, relation.LocalType)).ToList(); // The next part of the logic will determin these values. string pivotTableName = null; string pivotTableNameAlt = null; // If we can't find any matching properties on the foreign // side well we don't have a Many to Many relationship. if (foreignProps.Count == 0) { return(null); } // If there is only 1 local and 1 foreign property that reference // each other then we create the pivot table based on the model // names. This is a simple every day Many to Many relationship. else if (localProps.Count == 1 && foreignProps.Count == 1) { // Save both navigational properties. relation.LocalProperty = localProp; relation.ForeignProperty = foreignProps[0]; // Create the pivot table name candiates. pivotTableName = relation.LocalTableName + "To" + relation.ForeignTableName; pivotTableNameAlt = relation.ForeignTableName + "To" + relation.LocalTableName; } // This supports the ability to define multiple relationships to the // same type. If there are multiple properties that reference each // other, we use the property names to create a unique pivot table. else if (localProps.Count > 1 || foreignProps.Count > 1) { PropertyInfo foreignProp; // First lets check for any explictly set InversePropertyAttributes. var foreignInverseProp = localProp.GetCustomAttribute <InversePropertyAttribute>(false); if (foreignInverseProp != null) { // Sweet the local property is telling is // exactly which foreign property to use. foreignProp = foreignProps.Single ( fp => fp.Name == foreignInverseProp.Value ); } else { // Lets see if any of the foreign properties have an // InversePropertyAttribute that points to our // local property. foreignProp = foreignProps .Where(fp => fp.GetCustomAttribute <InversePropertyAttribute>(false) != null) .SingleOrDefault(fp => fp.GetCustomAttribute <InversePropertyAttribute>(false).Value == localProp.Name); } // Now lets attempt to find our inverse property by looking at the property names. if (foreignProp == null) { // Determin the relation link id. relation.LinkIdentifier = localProp.Name.Replace ( relation.ForeignTableName, String.Empty ); // We should have at least one foreign // property that contains the LinkIdentifier. try { foreignProp = foreignProps.Single ( fp => fp.Name.Contains ( relation.LinkIdentifier ) ); } catch { // We couldn't find a matching property // so we don't have a relationship. return(null); } } else { // If the forgeign poroperty was found by means of a // InversePropertyAttribute then we still need a // LinkIdentifier so we need to make one. relation.LinkIdentifier = localProp.Name + foreignProp.Name; } // Save both navigational properties. relation.LocalProperty = localProp; relation.ForeignProperty = foreignProp; // Create the pivot table name candiates. pivotTableName = relation.LocalTableName + relation.LinkIdentifier + relation.ForeignTableName; pivotTableNameAlt = relation.ForeignTableName + relation.LinkIdentifier + relation.LocalTableName; } else { return(null); } // Now there are 2 possible pivot table names. // It's a first in, first served situation. if (this.PivotTableTaken(pivotTableName)) { relation.PivotTableName = pivotTableName; relation.PivotTableFirstColumnName = relation.LocalTableNameSingular + "Id"; relation.PivotTableSecondColumnName = relation.ForeignTableNameSingular + "Id"; } else if (this.PivotTableTaken(pivotTableNameAlt)) { relation.PivotTableName = pivotTableNameAlt; relation.PivotTableFirstColumnName = relation.ForeignTableNameSingular + "Id"; relation.PivotTableSecondColumnName = relation.LocalTableNameSingular + "Id"; } else { // No table actually exists yet, // so just go with the first option. // The Migrator will soon create it :) relation.PivotTableName = pivotTableName; relation.PivotTableFirstColumnName = relation.LocalTableNameSingular + "Id"; relation.PivotTableSecondColumnName = relation.ForeignTableNameSingular + "Id"; } // Finally return the relationship discriptor. return(relation); }