/// <summary>
/// Make association property metadata for the entity.
/// Also populates the ForeignKeyMap which is used for related-entity fixup in NHContext.FixupRelationships
/// </summary>
/// <param name="propertyInfo">Property info describing the property</param>
/// <param name="containingType">Type containing the property</param>
/// <param name="dataProperties">Data properties already collected for the containingType. "isPartOfKey" may be added to a property.</param>
/// <param name="isKey">Whether the property is part of the key</param>
/// <returns></returns>
private Dictionary <string, object> MakeAssociationProperty(Type containingType, PropertyInfo propertyInfo, List <Dictionary <string, object> > dataProperties, bool isKey)
{
var nmap = new Dictionary <string, object>();
var name = propertyInfo.Name;
nmap.Add("nameOnServer", name);
var propType = propertyInfo.PropertyType;
var isCollection = IsCollectionType(propType);
var relatedEntityType = isCollection ? GetElementType(propType) : propType;
relatedEntityType = _describer.Replace(relatedEntityType, _allTypes);
nmap.Add("entityTypeName", relatedEntityType.Name + ":#" + relatedEntityType.Namespace);
nmap.Add("isScalar", !isCollection);
AddAttributesToNavProperty(propertyInfo, nmap);
var entityRelationship = containingType.FullName + '.' + name;
// Add assocation. The associationName will be resolved later, when both ends are known.
nmap.Add("__association", new Association(containingType, relatedEntityType, name));
if (!isCollection)
{
// For scalar navigation properties, we need to identify the foreign key
var missingFKHandling = _describer.GetMissingFKHandling(containingType, propertyInfo);
Dictionary <string, object> dataProp = null;
// Find the matching key in the data properties for this entity
// First see if a data property was identified on an attribute of the navigation property
var fkNames = nmap.Get("foreignKeyNamesOnServer") as string[];
if (fkNames != null)
{
// find the matching data prop using its fk name
var fkName = (fkNames)[0];
dataProp = FindEntry(dataProperties, "nameOnServer", fkName);
}
if (dataProp == null)
{
// Next see if a data property was marked as a foreign key during attribute processing
dataProp = FindEntry(dataProperties, "__foreignKey", name);
}
if (dataProp == null)
{
// Use the descriptor to guess the foreign key
var dataPropertyName = _describer.GetForeignKeyName(containingType, propertyInfo);
if (dataPropertyName != null)
{
dataProp = FindEntry(dataProperties, "nameOnServer", dataPropertyName);
}
}
if (dataProp == null && missingFKHandling == MissingKeyHandling.Add)
{
// Add a new dataproperty to represent the foreign key
var dataPropertyName = _describer.GetForeignKeyName(containingType, propertyInfo);
dataProp = new Dictionary <string, object>();
dataProp.Add("nameOnServer", dataPropertyName);
dataProp.Add("custom", "fk_generated");
string dataType = null;
// find the related entity so we can get the datatype of the key
var relatedEntityProperties = relatedEntityType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var relatedKey = relatedEntityProperties.Where(p => _describer.IsKeyProperty(relatedEntityType, p)).FirstOrDefault();
if (relatedKey != null)
{
dataType = _describer.GetDataPropertyType(relatedEntityType, relatedKey).Name;
}
if (dataType == null)
{
dataType = "Guid";
}
dataProp.Add("dataType", dataType);
dataProperties.Add(dataProp);
}
if (dataProp != null)
{
fkNames = new string[] { dataProp["nameOnServer"].ToString() };
nmap["foreignKeyNamesOnServer"] = fkNames;
dataProp.Remove("__foreignKey");
dataProp.Remove("__relatedType");
// if the navigation property is defined as part of the key, set the fk data property instead
if (isKey)
{
dataProp["isPartOfKey"] = true;
}
// For many-to-one and one-to-one associations, save the relationship in ForeignKeyMap for re-establishing relationships during save
_map.ForeignKeyMap.Add(entityRelationship, string.Join(",", fkNames));
}
else
{
if (missingFKHandling == MissingKeyHandling.Error)
{
throw new Exception("Cannot find foreign key property on type " + containingType.Name + " for navigation property " + propertyInfo.Name);
}
else if (missingFKHandling == MissingKeyHandling.Log)
{
Console.Error.WriteLine("Cannot find foreign key property on type " + containingType.Name + " for navigation property " + propertyInfo.Name);
_map.ForeignKeyMap.Add(entityRelationship, "ERROR - NOT FOUND");
}
}
}
return(nmap);
}