/// <summary> /// Writes attach/detach method /// </summary> /// <param name="writer"></param> /// <param name="table"></param> /// <param name="schema"></param> /// <param name="context"></param> protected virtual void WriteClassChildrenAttachment(CodeWriter writer, Table table, Database schema, GenerationContext context) { var children = GetClassChildren(table).ToList(); if (children.Count > 0) { using (writer.WriteRegion("Attachement handlers")) { foreach (var child in children) { // the reverse child is the association seen from the child // we're going to use it... var reverseChild = schema.GetReverseAssociation(child); // ... to get the parent name var memberName = reverseChild.Member; var entityParameter = new ParameterDefinition { Name = "entity", LiteralType = child.Type }; // the Attach event handler sets the child entity parent to "this" using (writer.WriteMethod(SpecificationDefinition.Private, GetChildAttachMethodName(child), null, entityParameter)) { writer.WriteLine( writer.GetStatement( writer.GetAssignmentExpression( writer.GetMemberExpression(entityParameter.Name, memberName), writer.GetThisExpression()))); } writer.WriteLine(); // the Detach event handler sets the child entity parent to null using (writer.WriteMethod(SpecificationDefinition.Private, GetChildDetachMethodName(child), null, entityParameter)) { writer.WriteLine( writer.GetStatement( writer.GetAssignmentExpression( writer.GetMemberExpression(entityParameter.Name, memberName), writer.GetNullExpression()))); } writer.WriteLine(); } } } }
void GenerateEntityParents(CodeTypeDeclaration entity, Table table, Database schema) { var parents = table.Type.Associations.Where(a => a.IsForeignKey); if (!parents.Any()) return; var parentMembers = new List<CodeTypeMember>(); foreach (var parent in parents) { bool hasDuplicates = (from p in parents where p.Member == parent.Member select p).Count() > 1; // WriteClassParent(writer, parent, hasDuplicates, schema, context); // the following is apparently useless DbLinq.Schema.Dbml.Table targetTable = schema.Tables.FirstOrDefault(t => t.Type.Name == parent.Type); if (targetTable == null) { //Logger.Write(Level.Error, "ERROR L191 target table type not found: " + parent.Type + " (processing " + parent.Name + ")"); continue; } string member = parent.Member; string storageField = GetStorageFieldName(parent); // TODO: remove this if (member == parent.ThisKey) { member = parent.ThisKey + targetTable.Type.Name; //repeat name to prevent collision (same as Linq) storageField = "_x_" + parent.Member; } var parentType = new CodeTypeReference(targetTable.Type.Name); entity.Members.Add(new CodeMemberField(new CodeTypeReference("EntityRef", parentType), storageField) { InitExpression = new CodeObjectCreateExpression(new CodeTypeReference("EntityRef", parentType)), }); var parentName = hasDuplicates ? member + "_" + string.Join("", parent.TheseKeys.ToArray()) : member; var property = new CodeMemberProperty() { Name = parentName, Type = parentType, Attributes = ToMemberAttributes(parent), CustomAttributes = { new CodeAttributeDeclaration("Association", new CodeAttributeArgument("Storage", new CodePrimitiveExpression(storageField)), new CodeAttributeArgument("OtherKey", new CodePrimitiveExpression(parent.OtherKey)), new CodeAttributeArgument("ThisKey", new CodePrimitiveExpression(parent.ThisKey)), new CodeAttributeArgument("Name", new CodePrimitiveExpression(parent.Name)), new CodeAttributeArgument("IsForeignKey", new CodePrimitiveExpression(parent.IsForeignKey))), new CodeAttributeDeclaration("DebuggerNonUserCode"), }, }; parentMembers.Add(property); property.GetStatements.Add(new CodeMethodReturnStatement( new CodePropertyReferenceExpression( new CodeFieldReferenceExpression(thisReference, storageField), "Entity"))); // algorithm is: // 1.1. must be different than previous value // 1.2. or HasLoadedOrAssignedValue is false (but why?) // 2. implementations before change // 3. if previous value not null // 3.1. place parent in temp variable // 3.2. set [Storage].Entity to null // 3.3. remove it from parent list // 4. assign value to [Storage].Entity // 5. if value is not null // 5.1. add it to parent list // 5.2. set FK members with entity keys // 6. else // 6.1. set FK members to defaults (null or 0) // 7. implementationas after change var otherAssociation = schema.GetReverseAssociation(parent); var parentEntity = new CodePropertyReferenceExpression( new CodeFieldReferenceExpression(thisReference, storageField), "Entity"); var parentTable = schema.Tables.Single(t => t.Type.Associations.Contains(parent)); var childKeys = parent.TheseKeys.ToArray(); var childColumns = (from ck in childKeys select table.Type.Columns.Single(c => c.Member == ck)) .ToArray(); var parentKeys = parent.OtherKeys.ToArray(); property.SetStatements.Add(new CodeConditionStatement( // 1.1 ValuesAreNotEqual_Ref(parentEntity, new CodePropertySetValueReferenceExpression()), // 2. TODO: code before the change // 3. new CodeConditionStatement( ValueIsNotNull(parentEntity), // 3.1 new CodeVariableDeclarationStatement(parentType, "previous" + parent.Type, parentEntity), // 3.2 new CodeAssignStatement(parentEntity, new CodePrimitiveExpression(null)), // 3.3 new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodePropertyReferenceExpression( new CodeVariableReferenceExpression("previous" + parent.Type), otherAssociation.Member), "Remove"), thisReference))), // 4. new CodeAssignStatement(parentEntity, new CodePropertySetValueReferenceExpression()), // 5. if value is null or not... new CodeConditionStatement( ValueIsNotNull(new CodePropertySetValueReferenceExpression()), // 5.1 new CodeStatement[]{ new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodePropertyReferenceExpression( new CodePropertySetValueReferenceExpression(), otherAssociation.Member), "Add"), thisReference)) // 5.2 }.Concat(Enumerable.Range(0, parentKeys.Length).Select(i => (CodeStatement) new CodeAssignStatement( new CodeVariableReferenceExpression(GetStorageFieldName(childColumns[i])), new CodePropertyReferenceExpression( new CodePropertySetValueReferenceExpression(), parentKeys[i])) )).ToArray(), // 6. Enumerable.Range(0, parentKeys.Length).Select(i => { var column = parentTable.Type.Columns.Single(c => c.Member == childKeys[i]); return (CodeStatement) new CodeAssignStatement( new CodeVariableReferenceExpression(GetStorageFieldName(childColumns[i])), column.CanBeNull ? (CodeExpression) new CodePrimitiveExpression(null) : (CodeExpression) new CodeDefaultValueExpression(new CodeTypeReference(column.Type))); }).ToArray()) // 7: TODO )); } if (parentMembers.Count == 0) return; parentMembers.First().StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Parents")); parentMembers.Last().EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, null)); entity.Members.AddRange(parentMembers.ToArray()); }
protected virtual void WriteClassParent(CodeWriter writer, Association parent, bool hasDuplicates, Database schema, GenerationContext context) { // the following is apparently useless DbLinq.Schema.Dbml.Table targetTable = schema.Tables.FirstOrDefault(t => t.Type.Name == parent.Type); if (targetTable == null) { //Logger.Write(Level.Error, "ERROR L191 target table type not found: " + parent.Type + " (processing " + parent.Name + ")"); return; } string member = parent.Member; string storageField = parent.Storage; // TODO: remove this if (member == parent.ThisKey) { member = parent.ThisKey + targetTable.Type.Name; //repeat name to prevent collision (same as Linq) storageField = "_x_" + parent.Member; } writer.WriteField(SpecificationDefinition.Private, storageField, writer.GetGenericName(TypeExtensions.GetShortName(typeof(EntityRef<>)), targetTable.Type.Name)); var storageAttribute = NewAttributeDefinition<AssociationAttribute>(); storageAttribute["Storage"] = storageField; storageAttribute["OtherKey"] = parent.OtherKey; storageAttribute["ThisKey"] = parent.ThisKey; storageAttribute["Name"] = parent.Name; storageAttribute["IsForeignKey"] = parent.IsForeignKey; SpecificationDefinition specifications; if (parent.AccessModifierSpecified) specifications = GetSpecificationDefinition(parent.AccessModifier); else specifications = SpecificationDefinition.Public; if (parent.ModifierSpecified) specifications |= GetSpecificationDefinition(parent.Modifier); var propertyName = hasDuplicates ? member + "_" + string.Join("", parent.TheseKeys.ToArray()) : member; using (writer.WriteAttribute(storageAttribute)) using (writer.WriteAttribute(NewAttributeDefinition<DebuggerNonUserCodeAttribute>())) using (writer.WriteProperty(specifications, propertyName, targetTable.Type.Name)) { string storage = writer.GetMemberExpression(storageField, "Entity"); using (writer.WritePropertyGet()) { writer.WriteLine(writer.GetReturnStatement(storage)); } using (writer.WritePropertySet()) { // algorithm is: // 1.1. must be different than previous value // 1.2. or HasLoadedOrAssignedValue is false (but why?) // 2. implementations before change // 3. if previous value not null // 3.1. place parent in temp variable // 3.2. set [Storage].Entity to null // 3.3. remove it from parent list // 4. assign value to [Storage].Entity // 5. if value is not null // 5.1. add it to parent list // 5.2. set FK members with entity keys // 6. else // 6.1. set FK members to defaults (null or 0) // 7. implementationas after change //writer.WriteLine(writer.GetStatement(writer.GetAssignmentExpression(storage, writer.GetPropertySetValueExpression()))); var entityMember = writer.GetMemberExpression(parent.Storage, "Entity"); // 1.1 using (writer.WriteIf(writer.GetDifferentExpression(writer.GetPropertySetValueExpression(), entityMember))) { var otherAssociation = schema.GetReverseAssociation(parent); // 2. code before the change // TODO change interface to require a member instead of a column //foreach (IImplementation implementation in context.Implementations()) // implementation.WritePropertyBeforeSet(writer, ???, context); // 3. using (writer.WriteIf(writer.GetDifferentExpression(entityMember, writer.GetNullExpression()))) { var previousEntityRefName = "previous" + parent.Type; // 3.1. writer.WriteLine(writer.GetStatement( writer.GetVariableDeclarationInitialization(parent.Type, previousEntityRefName, entityMember) )); // 3.2. writer.WriteLine(writer.GetStatement( writer.GetAssignmentExpression(entityMember, writer.GetNullExpression()) )); // 3.3. writer.WriteLine(writer.GetStatement( writer.GetMethodCallExpression( writer.GetMemberExpression(writer.GetMemberExpression(previousEntityRefName, otherAssociation.Member), "Remove"), writer.GetThisExpression()) )); } // 4. writer.WriteLine(writer.GetStatement( writer.GetAssignmentExpression(entityMember, writer.GetPropertySetValueExpression()) )); // 5. if value is null or not writer.WriteRawIf(writer.GetDifferentExpression(writer.GetPropertySetValueExpression(), writer.GetNullExpression())); // 5.1. writer.WriteLine(writer.GetStatement( writer.GetMethodCallExpression( writer.GetMemberExpression(writer.GetMemberExpression(writer.GetPropertySetValueExpression(), otherAssociation.Member), "Add"), writer.GetThisExpression()) )); // 5.2 var table = schema.Tables.Single(t => t.Type.Associations.Contains(parent)); var childKeys = parent.TheseKeys.ToArray(); var childColumns = (from ck in childKeys select table.Type.Columns.Single(c => c.Member == ck)) .ToArray(); var parentKeys = parent.OtherKeys.ToArray(); for (int keyIndex = 0; keyIndex < parentKeys.Length; keyIndex++) { writer.WriteLine(writer.GetStatement(writer.GetAssignmentExpression( childColumns[keyIndex].Storage ?? childColumns[keyIndex].Member, writer.GetMemberExpression(writer.GetPropertySetValueExpression(), parentKeys[keyIndex]) ))); } // 6. writer.WriteRawElse(); // 6.1. for (int keyIndex = 0; keyIndex < parentKeys.Length; keyIndex++) { var column = table.Type.Columns.Single(c => c.Member == childKeys[keyIndex]); var columnType = System.Type.GetType(column.Type); var columnLiteralType = columnType != null ? writer.GetLiteralType(columnType) : column.Type; writer.WriteLine(writer.GetStatement(writer.GetAssignmentExpression( childColumns[keyIndex].Storage ?? childColumns[keyIndex].Member, column.CanBeNull ? writer.GetNullExpression() : writer.GetNullValueExpression(columnLiteralType) ))); } writer.WriteRawEndif(); // 7. code after change // TODO change interface to require a member instead of a column //foreach (IImplementation implementation in context.Implementations()) // implementation.WritePropertyAfterSet(writer, ???, context); } } } writer.WriteLine(); }
void GenerateEntityChildrenAttachment(CodeTypeDeclaration entity, Table table, Database schema) { var children = GetClassChildren(table).ToList(); if (!children.Any()) return; var havePrimaryKeys = table.Type.Columns.Any(c => c.IsPrimaryKey); var handlers = new List<CodeTypeMember>(); foreach (var child in children) { // the reverse child is the association seen from the child // we're going to use it... var reverseChild = schema.GetReverseAssociation(child); // ... to get the parent name var memberName = reverseChild.Member; var sendPropertyChanging = new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(thisReference, "SendPropertyChanging"))); var attach = new CodeMemberMethod() { Name = child.Member + "_Attach", Parameters = { new CodeParameterDeclarationExpression(child.Type, "entity"), }, }; handlers.Add(attach); if (havePrimaryKeys) attach.Statements.Add(sendPropertyChanging); attach.Statements.Add( new CodeAssignStatement( new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("entity"), memberName), thisReference)); var detach = new CodeMemberMethod() { Name = child.Member + "_Detach", Parameters = { new CodeParameterDeclarationExpression(child.Type, "entity"), }, }; handlers.Add(detach); if (havePrimaryKeys) detach.Statements.Add(sendPropertyChanging); detach.Statements.Add( new CodeAssignStatement( new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("entity"), memberName), new CodePrimitiveExpression(null))); } if (handlers.Count == 0) return; handlers.First().StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Attachment handlers")); handlers.Last().EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, null)); entity.Members.AddRange(handlers.ToArray()); }