mirror of
https://gitee.com/dotnetchina/OpenAuth.Net.git
synced 2025-04-05 17:38:01 +08:00
640 lines
26 KiB
C#
640 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using ICSharpCode.NRefactory.CSharp;
|
|
|
|
namespace SchemaMapper
|
|
{
|
|
#region Mapping Parser
|
|
[DebuggerDisplay("Table: {TableName}, Entity: {EntityClass}, Mapping: {MappingClass}")]
|
|
public class ParsedEntity
|
|
{
|
|
public ParsedEntity()
|
|
{
|
|
Properties = new List<ParsedProperty>();
|
|
Relationships = new List<ParsedRelationship>();
|
|
}
|
|
|
|
public string EntityClass { get; set; }
|
|
public string MappingClass { get; set; }
|
|
|
|
public string TableName { get; set; }
|
|
public string TableSchema { get; set; }
|
|
|
|
public List<ParsedProperty> Properties { get; private set; }
|
|
public List<ParsedRelationship> Relationships { get; private set; }
|
|
}
|
|
|
|
[DebuggerDisplay("Column: {ColumnName}, Property: {PropertyName}")]
|
|
public class ParsedProperty
|
|
{
|
|
public string ColumnName { get; set; }
|
|
public string PropertyName { get; set; }
|
|
}
|
|
|
|
[DebuggerDisplay("This: {ThisPropertyName}, Other: {OtherPropertyName}")]
|
|
public class ParsedRelationship
|
|
{
|
|
public ParsedRelationship()
|
|
{
|
|
ThisProperties = new List<string>();
|
|
JoinThisColumn = new List<string>();
|
|
JoinOtherColumn = new List<string>();
|
|
}
|
|
|
|
public string ThisPropertyName { get; set; }
|
|
public List<string> ThisProperties { get; private set; }
|
|
|
|
public string OtherPropertyName { get; set; }
|
|
|
|
public string JoinTable { get; set; }
|
|
public string JoinSchema { get; set; }
|
|
public List<string> JoinThisColumn { get; private set; }
|
|
public List<string> JoinOtherColumn { get; private set; }
|
|
}
|
|
|
|
public class MappingVisitor : DepthFirstAstVisitor<object, object>
|
|
{
|
|
public MappingVisitor()
|
|
{
|
|
MappingBaseType = "EntityTypeConfiguration";
|
|
}
|
|
|
|
public string MappingBaseType { get; set; }
|
|
public ParsedEntity ParsedEntity { get; set; }
|
|
|
|
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
|
|
{
|
|
var baseType = typeDeclaration.BaseTypes.OfType<MemberType>().FirstOrDefault();
|
|
if (baseType == null || baseType.MemberName != MappingBaseType)
|
|
return base.VisitTypeDeclaration(typeDeclaration, data);
|
|
|
|
var entity = baseType.TypeArguments.OfType<MemberType>().FirstOrDefault();
|
|
if (entity == null)
|
|
return base.VisitTypeDeclaration(typeDeclaration, data);
|
|
|
|
if (ParsedEntity == null)
|
|
ParsedEntity = new ParsedEntity();
|
|
|
|
ParsedEntity.EntityClass = entity.MemberName;
|
|
ParsedEntity.MappingClass = typeDeclaration.Name;
|
|
|
|
return base.VisitTypeDeclaration(typeDeclaration, ParsedEntity);
|
|
}
|
|
|
|
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
|
|
{
|
|
if (data == null)
|
|
return base.VisitInvocationExpression(invocationExpression, null);
|
|
|
|
// visit all the methods
|
|
var identifier = invocationExpression.Target.Children.OfType<Identifier>().FirstOrDefault();
|
|
string methodName = identifier == null ? string.Empty : identifier.Name;
|
|
|
|
// the different types of incoming data, helps us know what we're parsing
|
|
var parsedEntity = data as ParsedEntity;
|
|
var parsedProperty = data as ParsedProperty;
|
|
var parsedRelationship = data as ParsedRelationship;
|
|
|
|
switch (methodName)
|
|
{
|
|
case "ToTable":
|
|
var tableNameExpression = invocationExpression.Arguments
|
|
.OfType<PrimitiveExpression>()
|
|
.ToArray();
|
|
|
|
string tableName = null;
|
|
string tableSchema = null;
|
|
|
|
if (tableNameExpression.Length >= 1)
|
|
tableName = tableNameExpression[0].Value.ToString();
|
|
if (tableNameExpression.Length >= 2)
|
|
tableSchema = tableNameExpression[1].Value.ToString();
|
|
|
|
// ToTable is either Entity -> Table map or Many to Many map
|
|
if (parsedEntity != null)
|
|
{
|
|
// when data is ParsedEntity, entity map
|
|
parsedEntity.TableName = tableName;
|
|
parsedEntity.TableSchema = tableSchema;
|
|
}
|
|
else if (parsedRelationship != null)
|
|
{
|
|
// when data is ParsedRelationship, many to many map
|
|
parsedRelationship.JoinTable = tableName;
|
|
parsedRelationship.JoinSchema = tableSchema;
|
|
}
|
|
break;
|
|
case "HasColumnName":
|
|
var columnNameExpression = invocationExpression.Arguments
|
|
.OfType<PrimitiveExpression>()
|
|
.FirstOrDefault();
|
|
|
|
if (columnNameExpression == null)
|
|
break;
|
|
|
|
// property to column map start.
|
|
string columnName = columnNameExpression.Value.ToString();
|
|
var property = new ParsedProperty { ColumnName = columnName };
|
|
ParsedEntity.Properties.Add(property);
|
|
|
|
//only have column info at this point. pass data to get property name.
|
|
return base.VisitInvocationExpression(invocationExpression, property);
|
|
case "Property":
|
|
var propertyExpression = invocationExpression.Arguments
|
|
.OfType<LambdaExpression>()
|
|
.FirstOrDefault();
|
|
|
|
if (parsedProperty == null || propertyExpression == null)
|
|
break;
|
|
|
|
// ParsedProperty is passed in as data from HasColumnName, add property name
|
|
var propertyBodyExpression = propertyExpression.Body as MemberReferenceExpression;
|
|
if (propertyBodyExpression != null)
|
|
parsedProperty.PropertyName = propertyBodyExpression.MemberName;
|
|
|
|
break;
|
|
case "Map":
|
|
// start a new Many to Many relationship
|
|
var mapRelation = new ParsedRelationship();
|
|
ParsedEntity.Relationships.Add(mapRelation);
|
|
// pass to child nodes to fill in data
|
|
return base.VisitInvocationExpression(invocationExpression, mapRelation);
|
|
case "HasForeignKey":
|
|
var foreignExpression = invocationExpression.Arguments
|
|
.OfType<LambdaExpression>()
|
|
.FirstOrDefault();
|
|
|
|
if (foreignExpression == null)
|
|
break;
|
|
|
|
// when only 1 fkey, body will be member ref
|
|
if (foreignExpression.Body is MemberReferenceExpression)
|
|
{
|
|
var foreignBodyExpression = foreignExpression.Body as MemberReferenceExpression;
|
|
// start a new relationship
|
|
var foreignRelation = new ParsedRelationship();
|
|
ParsedEntity.Relationships.Add(foreignRelation);
|
|
|
|
foreignRelation.ThisProperties.Add(foreignBodyExpression.MemberName);
|
|
// pass as data for child nodes to fill in data
|
|
return base.VisitInvocationExpression(invocationExpression, foreignRelation);
|
|
}
|
|
// when more then 1 fkey, body will be an anonymous type
|
|
if (foreignExpression.Body is AnonymousTypeCreateExpression)
|
|
{
|
|
var foreignBodyExpression = foreignExpression.Body as AnonymousTypeCreateExpression;
|
|
var foreignRelation = new ParsedRelationship();
|
|
ParsedEntity.Relationships.Add(foreignRelation);
|
|
|
|
var properties = foreignBodyExpression.Children
|
|
.OfType<MemberReferenceExpression>()
|
|
.Select(m => m.MemberName);
|
|
|
|
foreignRelation.ThisProperties.AddRange(properties);
|
|
|
|
return base.VisitInvocationExpression(invocationExpression, foreignRelation);
|
|
}
|
|
break;
|
|
case "HasMany":
|
|
var hasManyExpression = invocationExpression.Arguments
|
|
.OfType<LambdaExpression>()
|
|
.FirstOrDefault();
|
|
|
|
if (parsedRelationship == null || hasManyExpression == null)
|
|
break;
|
|
|
|
// filling existing relationship data
|
|
var hasManyBodyExpression = hasManyExpression.Body as MemberReferenceExpression;
|
|
if (hasManyBodyExpression != null)
|
|
parsedRelationship.ThisPropertyName = hasManyBodyExpression.MemberName;
|
|
|
|
break;
|
|
case "WithMany":
|
|
var withManyExpression = invocationExpression.Arguments
|
|
.OfType<LambdaExpression>()
|
|
.FirstOrDefault();
|
|
|
|
if (parsedRelationship == null || withManyExpression == null)
|
|
break;
|
|
|
|
// filling existing relationship data
|
|
var withManyBodyExpression = withManyExpression.Body as MemberReferenceExpression;
|
|
if (withManyBodyExpression != null)
|
|
parsedRelationship.OtherPropertyName = withManyBodyExpression.MemberName;
|
|
|
|
break;
|
|
case "HasRequired":
|
|
case "HasOptional":
|
|
var hasExpression = invocationExpression.Arguments
|
|
.OfType<LambdaExpression>()
|
|
.FirstOrDefault();
|
|
|
|
if (parsedRelationship == null || hasExpression == null)
|
|
break;
|
|
|
|
// filling existing relationship data
|
|
var hasBodyExpression = hasExpression.Body as MemberReferenceExpression;
|
|
if (hasBodyExpression != null)
|
|
parsedRelationship.ThisPropertyName = hasBodyExpression.MemberName;
|
|
|
|
break;
|
|
case "MapLeftKey":
|
|
if (parsedRelationship == null)
|
|
break;
|
|
|
|
var leftKeyExpression = invocationExpression.Arguments
|
|
.OfType<PrimitiveExpression>()
|
|
.Select(e => e.Value.ToString());
|
|
|
|
parsedRelationship.JoinThisColumn.AddRange(leftKeyExpression);
|
|
break;
|
|
case "MapRightKey":
|
|
if (parsedRelationship == null)
|
|
break;
|
|
|
|
var rightKeyExpression = invocationExpression.Arguments
|
|
.OfType<PrimitiveExpression>()
|
|
.Select(e => e.Value.ToString());
|
|
|
|
parsedRelationship.JoinOtherColumn.AddRange(rightKeyExpression);
|
|
break;
|
|
}
|
|
|
|
return base.VisitInvocationExpression(invocationExpression, data);
|
|
}
|
|
}
|
|
|
|
public static class MappingParser
|
|
{
|
|
public static ParsedEntity Parse(string mappingFile)
|
|
{
|
|
if (string.IsNullOrEmpty(mappingFile) || !File.Exists(mappingFile))
|
|
return null;
|
|
|
|
var parser = new CSharpParser();
|
|
CompilationUnit compilationUnit;
|
|
|
|
using (var stream = File.OpenText(mappingFile))
|
|
compilationUnit = parser.Parse(stream, mappingFile);
|
|
|
|
var visitor = new MappingVisitor();
|
|
|
|
visitor.VisitCompilationUnit(compilationUnit, null);
|
|
var parsedEntity = visitor.ParsedEntity;
|
|
|
|
if (parsedEntity != null)
|
|
Debug.WriteLine("Parsed Mapping File: '{0}'; Properties: {1}; Relationships: {2}",
|
|
Path.GetFileName(mappingFile),
|
|
parsedEntity.Properties.Count,
|
|
parsedEntity.Relationships.Count);
|
|
|
|
return parsedEntity;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Context Parser
|
|
[DebuggerDisplay("Context: {ContextClass}")]
|
|
public class ParsedContext
|
|
{
|
|
public ParsedContext()
|
|
{
|
|
Properties = new List<ParsedEntitySet>();
|
|
}
|
|
|
|
public string ContextClass { get; set; }
|
|
|
|
public List<ParsedEntitySet> Properties { get; private set; }
|
|
}
|
|
|
|
[DebuggerDisplay("Entity: {EntityClass}, Property: {ContextProperty}")]
|
|
public class ParsedEntitySet
|
|
{
|
|
public string EntityClass { get; set; }
|
|
public string ContextProperty { get; set; }
|
|
}
|
|
|
|
public class ContextVisitor : DepthFirstAstVisitor<object, object>
|
|
{
|
|
public ContextVisitor()
|
|
{
|
|
ContextBaseType = "DbContext";
|
|
DataSetType = "DbSet";
|
|
}
|
|
|
|
public string ContextBaseType { get; set; }
|
|
public string DataSetType { get; set; }
|
|
|
|
public ParsedContext ParsedContext { get; set; }
|
|
|
|
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
|
|
{
|
|
var baseType = typeDeclaration.BaseTypes
|
|
.OfType<MemberType>()
|
|
.FirstOrDefault();
|
|
|
|
// warning: if inherited from custom base type, this will break
|
|
// anyway to improve this?
|
|
if (baseType == null || baseType.MemberName != ContextBaseType)
|
|
return base.VisitTypeDeclaration(typeDeclaration, data);
|
|
|
|
if (ParsedContext == null)
|
|
ParsedContext = new ParsedContext();
|
|
|
|
ParsedContext.ContextClass = typeDeclaration.Name;
|
|
|
|
return base.VisitTypeDeclaration(typeDeclaration, ParsedContext);
|
|
}
|
|
|
|
public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data)
|
|
{
|
|
if (data == null)
|
|
return base.VisitPropertyDeclaration(propertyDeclaration, null);
|
|
|
|
// look for property to return generic DbSet type
|
|
var memberType = propertyDeclaration.ReturnType as MemberType;
|
|
if (memberType == null || memberType.MemberName != DataSetType)
|
|
return base.VisitPropertyDeclaration(propertyDeclaration, data);
|
|
|
|
// get the first generic type
|
|
var entityType = memberType.TypeArguments
|
|
.OfType<MemberType>()
|
|
.FirstOrDefault();
|
|
|
|
if (entityType == null)
|
|
return base.VisitPropertyDeclaration(propertyDeclaration, data);
|
|
|
|
var entitySet = new ParsedEntitySet
|
|
{
|
|
EntityClass = entityType.MemberName,
|
|
ContextProperty = propertyDeclaration.Name
|
|
};
|
|
|
|
ParsedContext.Properties.Add(entitySet);
|
|
|
|
return base.VisitPropertyDeclaration(propertyDeclaration, data);
|
|
}
|
|
}
|
|
|
|
public static class ContextParser
|
|
{
|
|
public static ParsedContext Parse(string contextFile)
|
|
{
|
|
if (string.IsNullOrEmpty(contextFile) || !File.Exists(contextFile))
|
|
return null;
|
|
|
|
var parser = new CSharpParser();
|
|
CompilationUnit compilationUnit;
|
|
|
|
using (var stream = File.OpenText(contextFile))
|
|
compilationUnit = parser.Parse(stream, contextFile);
|
|
|
|
var visitor = new ContextVisitor();
|
|
|
|
visitor.VisitCompilationUnit(compilationUnit, null);
|
|
var parsedContext = visitor.ParsedContext;
|
|
|
|
if (parsedContext != null)
|
|
Debug.WriteLine("Parsed Context File: '{0}'; Entities: {1}",
|
|
Path.GetFileName(contextFile),
|
|
parsedContext.Properties.Count);
|
|
|
|
return parsedContext;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public static class Synchronizer
|
|
{
|
|
public static bool UpdateFromSource(EntityContext generatedContext, string contextDirectory, string mappingDirectory)
|
|
{
|
|
if (generatedContext == null)
|
|
return false;
|
|
|
|
// make sure to update the entities before the context
|
|
UpdateFromMapping(generatedContext, mappingDirectory);
|
|
UpdateFromContext(generatedContext, contextDirectory);
|
|
return true;
|
|
}
|
|
|
|
private static void UpdateFromContext(EntityContext generatedContext, string contextDirectory)
|
|
{
|
|
if (generatedContext == null
|
|
|| contextDirectory == null
|
|
|| !Directory.Exists(contextDirectory))
|
|
return;
|
|
|
|
// parse context
|
|
ParsedContext parsedContext = null;
|
|
var files = Directory.EnumerateFiles(contextDirectory, "*.Generated.cs").GetEnumerator();
|
|
while (files.MoveNext() && parsedContext == null)
|
|
parsedContext = ContextParser.Parse(files.Current);
|
|
|
|
if (parsedContext == null)
|
|
return;
|
|
|
|
if (generatedContext.ClassName != parsedContext.ContextClass)
|
|
{
|
|
Debug.WriteLine("Rename Context Class'{0}' to '{1}'.",
|
|
generatedContext.ClassName,
|
|
parsedContext.ContextClass);
|
|
|
|
generatedContext.ClassName = parsedContext.ContextClass;
|
|
}
|
|
|
|
foreach (var parsedProperty in parsedContext.Properties)
|
|
{
|
|
var entity = generatedContext.Entities.ByClass(parsedProperty.EntityClass);
|
|
if (entity == null)
|
|
continue;
|
|
|
|
|
|
if (entity.ContextName == parsedProperty.ContextProperty)
|
|
continue;
|
|
|
|
Debug.WriteLine("Rename Context Property'{0}' to '{1}'.",
|
|
entity.ContextName,
|
|
parsedProperty.ContextProperty);
|
|
|
|
entity.ContextName = parsedProperty.ContextProperty;
|
|
}
|
|
}
|
|
|
|
private static void UpdateFromMapping(EntityContext generatedContext, string mappingDirectory)
|
|
{
|
|
if (generatedContext == null
|
|
|| mappingDirectory == null
|
|
|| !Directory.Exists(mappingDirectory))
|
|
return;
|
|
|
|
// parse all mapping files
|
|
var mappingFiles = Directory.EnumerateFiles(mappingDirectory, "*.Generated.cs");
|
|
var parsedEntities = mappingFiles
|
|
.Select(MappingParser.Parse)
|
|
.Where(parsedEntity => parsedEntity != null)
|
|
.ToList();
|
|
|
|
var relationshipQueue = new List<Tuple<Entity, ParsedEntity>>();
|
|
|
|
// update all entity and property names first because relationships are linked by property names
|
|
foreach (var parsedEntity in parsedEntities)
|
|
{
|
|
// find entity by table name to support renaming entity
|
|
var entity = generatedContext.Entities
|
|
.ByTable(parsedEntity.TableName, parsedEntity.TableSchema);
|
|
|
|
if (entity == null)
|
|
continue;
|
|
|
|
// sync names
|
|
if (entity.MappingName != parsedEntity.MappingClass)
|
|
{
|
|
Debug.WriteLine("Rename Mapping Class'{0}' to '{1}'.",
|
|
entity.MappingName,
|
|
parsedEntity.MappingClass);
|
|
|
|
entity.MappingName = parsedEntity.MappingClass;
|
|
}
|
|
|
|
// use rename api to make sure all instances are renamed
|
|
generatedContext.RenameEntity(entity.ClassName, parsedEntity.EntityClass);
|
|
|
|
// sync properties
|
|
foreach (var parsedProperty in parsedEntity.Properties)
|
|
{
|
|
// find property by column name to support property name rename
|
|
var property = entity.Properties.ByColumn(parsedProperty.ColumnName);
|
|
if (property == null)
|
|
continue;
|
|
|
|
// use rename api to make sure all instances are renamed
|
|
generatedContext.RenameProperty(
|
|
entity.ClassName,
|
|
property.PropertyName,
|
|
parsedProperty.PropertyName);
|
|
}
|
|
|
|
// save relationship for later processing
|
|
var item = new Tuple<Entity, ParsedEntity>(entity, parsedEntity);
|
|
relationshipQueue.Add(item);
|
|
}
|
|
|
|
// update relationships last
|
|
foreach (var tuple in relationshipQueue)
|
|
UpdateRelationships(generatedContext, tuple.Item1, tuple.Item2);
|
|
}
|
|
|
|
private static void UpdateRelationships(EntityContext generatedContext, Entity entity, ParsedEntity parsedEntity)
|
|
{
|
|
// sync relationships
|
|
foreach (var parsedRelationship in parsedEntity.Relationships.Where(r => r.JoinTable == null))
|
|
{
|
|
var parsedProperties = parsedRelationship.ThisProperties;
|
|
var relationship = entity.Relationships
|
|
.Where(r => !r.IsManyToMany)
|
|
.FirstOrDefault(r => r.ThisProperties.Except(parsedProperties).Count() == 0);
|
|
|
|
if (relationship == null)
|
|
continue;
|
|
|
|
bool isThisSame = relationship.ThisPropertyName == parsedRelationship.ThisPropertyName;
|
|
bool isOtherSame = relationship.OtherPropertyName == parsedRelationship.OtherPropertyName;
|
|
|
|
if (isThisSame && isOtherSame)
|
|
continue;
|
|
|
|
if (!isThisSame)
|
|
{
|
|
Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.",
|
|
relationship.ThisEntity,
|
|
relationship.ThisPropertyName,
|
|
parsedRelationship.ThisPropertyName);
|
|
|
|
relationship.ThisPropertyName = parsedRelationship.ThisPropertyName;
|
|
}
|
|
if (!isOtherSame)
|
|
{
|
|
Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.",
|
|
relationship.OtherEntity,
|
|
relationship.OtherPropertyName,
|
|
parsedRelationship.OtherPropertyName);
|
|
|
|
relationship.OtherPropertyName = parsedRelationship.OtherPropertyName;
|
|
}
|
|
|
|
// sync other relationship
|
|
var otherEntity = generatedContext.Entities.ByClass(relationship.OtherEntity);
|
|
if (otherEntity == null)
|
|
continue;
|
|
|
|
var otherRelationship = otherEntity.Relationships.ByName(relationship.RelationshipName);
|
|
if (otherRelationship == null)
|
|
continue;
|
|
|
|
otherRelationship.ThisPropertyName = relationship.OtherPropertyName;
|
|
otherRelationship.OtherPropertyName = relationship.ThisPropertyName;
|
|
}
|
|
|
|
// sync many to many
|
|
foreach (var parsedRelationship in parsedEntity.Relationships.Where(r => r.JoinTable != null))
|
|
{
|
|
var joinThisColumn = parsedRelationship.JoinThisColumn;
|
|
var joinOtherColumn = parsedRelationship.JoinOtherColumn;
|
|
|
|
var relationship = entity.Relationships
|
|
.Where(r => r.IsManyToMany)
|
|
.FirstOrDefault(r =>
|
|
r.JoinThisColumn.Except(joinThisColumn).Count() == 0 &&
|
|
r.JoinOtherColumn.Except(joinOtherColumn).Count() == 0 &&
|
|
r.JoinTable == parsedRelationship.JoinTable &&
|
|
r.JoinSchema == parsedRelationship.JoinSchema);
|
|
|
|
if (relationship == null)
|
|
continue;
|
|
|
|
|
|
bool isThisSame = relationship.ThisPropertyName == parsedRelationship.ThisPropertyName;
|
|
bool isOtherSame = relationship.OtherPropertyName == parsedRelationship.OtherPropertyName;
|
|
|
|
if (isThisSame && isOtherSame)
|
|
continue;
|
|
|
|
if (!isThisSame)
|
|
{
|
|
Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.",
|
|
relationship.ThisEntity,
|
|
relationship.ThisPropertyName,
|
|
parsedRelationship.ThisPropertyName);
|
|
|
|
relationship.ThisPropertyName = parsedRelationship.ThisPropertyName;
|
|
}
|
|
if (!isOtherSame)
|
|
{
|
|
Debug.WriteLine("Rename Relationship Property '{0}.{1}' to '{0}.{2}'.",
|
|
relationship.OtherEntity,
|
|
relationship.OtherPropertyName,
|
|
parsedRelationship.OtherPropertyName);
|
|
|
|
relationship.OtherPropertyName = parsedRelationship.OtherPropertyName;
|
|
}
|
|
|
|
// sync other relationship
|
|
var otherEntity = generatedContext.Entities.ByClass(relationship.OtherEntity);
|
|
if (otherEntity == null)
|
|
continue;
|
|
|
|
var otherRelationship = otherEntity.Relationships.ByName(relationship.RelationshipName);
|
|
if (otherRelationship == null)
|
|
continue;
|
|
|
|
otherRelationship.ThisPropertyName = relationship.OtherPropertyName;
|
|
otherRelationship.OtherPropertyName = relationship.ThisPropertyName;
|
|
}
|
|
}
|
|
}
|
|
}
|