Dashing.CodeGeneration.ProxyGenerator.CreateTrackingClass C# (CSharp) Method

CreateTrackingClass() private method

private CreateTrackingClass ( IMap map, CodeGeneratorConfig codeGeneratorConfig ) : CodeTypeDeclaration
map IMap
codeGeneratorConfig CodeGeneratorConfig
return System.CodeDom.CodeTypeDeclaration
        private CodeTypeDeclaration CreateTrackingClass(
            IMap map, 
            CodeGeneratorConfig codeGeneratorConfig) {
            var trackingClass =
                new CodeTypeDeclaration(map.Type.Name + codeGeneratorConfig.TrackedClassSuffix);
            trackingClass.IsClass = true;
            trackingClass.TypeAttributes = TypeAttributes.Public;
            trackingClass.BaseTypes.Add(
                map.Type.Name + codeGeneratorConfig.ForeignKeyAccessClassSuffix);
            trackingClass.BaseTypes.Add(typeof(ITrackedEntity));

            // add in change tracking properties
            this.GenerateGetSetProperty(trackingClass, "IsTracking", typeof(bool), FinalPublic);
            this.GenerateGetSetProperty(
                trackingClass, 
                "DirtyProperties", 
                typeof(ISet<>).MakeGenericType(typeof(string)), 
                FinalPublic);
            this.GenerateGetSetProperty(
                trackingClass, 
                "OldValues", 
                typeof(IDictionary<,>).MakeGenericType(typeof(string), typeof(object)), 
                FinalPublic);
            this.GenerateGetSetProperty(
                trackingClass, 
                "NewValues", 
                typeof(IDictionary<,>).MakeGenericType(typeof(string), typeof(object)), 
                FinalPublic);
            this.GenerateGetSetProperty(
                trackingClass, 
                "AddedEntities", 
                typeof(IDictionary<,>).MakeGenericType(
                    typeof(string), 
                    typeof(IList<>).MakeGenericType(typeof(object))), 
                FinalPublic);
            this.GenerateGetSetProperty(
                trackingClass, 
                "DeletedEntities", 
                typeof(IDictionary<,>).MakeGenericType(
                    typeof(string), 
                    typeof(IList<>).MakeGenericType(typeof(object))), 
                FinalPublic);

            // add in a constructor to initialise collections
            var constructor = new CodeConstructor();
            constructor.Attributes = MemberAttributes.Public;
            constructor.Statements.Add(
                new CodeAssignStatement(
                    CodeHelpers.ThisField("DirtyProperties"), 
                    new CodeObjectCreateExpression(
                        typeof(HashSet<>).MakeGenericType(typeof(string)))));
            constructor.Statements.Add(
                new CodeAssignStatement(
                    CodeHelpers.ThisField("OldValues"), 
                    new CodeObjectCreateExpression(
                        typeof(Dictionary<,>).MakeGenericType(typeof(string), typeof(object)))));
            constructor.Statements.Add(
                new CodeAssignStatement(
                    CodeHelpers.ThisField("NewValues"), 
                    new CodeObjectCreateExpression(
                        typeof(Dictionary<,>).MakeGenericType(typeof(string), typeof(object)))));
            constructor.Statements.Add(
                new CodeAssignStatement(
                    CodeHelpers.ThisField("AddedEntities"), 
                    new CodeObjectCreateExpression(
                        typeof(Dictionary<,>).MakeGenericType(
                            typeof(string), 
                            typeof(IList<>).MakeGenericType(typeof(object))))));
            constructor.Statements.Add(
                new CodeAssignStatement(
                    CodeHelpers.ThisField("DeletedEntities"), 
                    new CodeObjectCreateExpression(
                        typeof(Dictionary<,>).MakeGenericType(
                            typeof(string), 
                            typeof(IList<>).MakeGenericType(typeof(object))))));

            // these constructor statements override the collection properties to use observable collections
            foreach (var collectionColumn in map.Columns.Where(c => c.Value.Type.IsCollection())) {
                if (
                    !collectionColumn.Value.Map.Type.GetProperty(collectionColumn.Value.Name)
                                     .GetGetMethod()
                                     .IsVirtual) {
                    // TODO: send a warning back to the programmer, did they mean to do this?
                    continue;
                }

                constructor.Statements.Add(
                    new CodeConditionStatement(
                        new CodeBinaryOperatorExpression(
                            new CodeFieldReferenceExpression(
                                new CodeThisReferenceExpression(), 
                                collectionColumn.Key), 
                            CodeBinaryOperatorType.IdentityEquality, 
                            new CodePrimitiveExpression(null)), 
                        new CodeStatement[] {
                                                new CodeAssignStatement(
                                                    new CodePropertyReferenceExpression(
                                                    new CodeThisReferenceExpression(), 
                                                    collectionColumn.Key), 
                                                    new CodeObjectCreateExpression(
                                                    "Dashing.CodeGeneration.TrackingCollection<" + trackingClass.Name
                                                    + "," + collectionColumn.Value.Type.GenericTypeArguments.First()
                                                    + ">", 
                                                    new CodeThisReferenceExpression(), 
                                                    new CodePrimitiveExpression(collectionColumn.Key)))
                                            }, 
                        new CodeStatement[] {
                                                new CodeAssignStatement(
                                                    new CodePropertyReferenceExpression(
                                                    new CodeThisReferenceExpression(), 
                                                    collectionColumn.Key), 
                                                    new CodeObjectCreateExpression(
                                                    "Dashing.CodeGeneration.TrackingCollection<" + trackingClass.Name
                                                    + "," + collectionColumn.Value.Type.GenericTypeArguments.First()
                                                    + ">", 
                                                    new CodeThisReferenceExpression(), 
                                                    new CodePrimitiveExpression(collectionColumn.Key), 
                                                    new CodePropertyReferenceExpression(
                                                    new CodeThisReferenceExpression(), 
                                                    collectionColumn.Key)))
                                            }));
            }

            // override value type properties to perform dirty checking
            foreach (
                var valueTypeColumn in
                    map.Columns.Where(c => !c.Value.Type.IsCollection() && !c.Value.IsIgnored)) {
                if (
                    !valueTypeColumn.Value.Map.Type.GetProperty(valueTypeColumn.Value.Name)
                                    .GetGetMethod()
                                    .IsVirtual) {
                    // TODO: send a warning back to the programmer, did they mean to do this?
                    continue;
                }

                var prop = this.GenerateGetSetProperty(
                    trackingClass, 
                    valueTypeColumn.Key, 
                    valueTypeColumn.Value.Type, 
                    MemberAttributes.Public | MemberAttributes.Override, 
                    true);

                // override the setter
                // if isTracking && !this.DirtyProperties.ContainsKey(prop) add to dirty props and add oldvalue
                bool propertyCanBeNull = valueTypeColumn.Value.Type.IsNullable()
                                         || !valueTypeColumn.Value.Type.IsValueType;
                var changeCheck = new CodeBinaryOperatorExpression();
                if (!propertyCanBeNull) {
                    // can't be null so just check values
                    changeCheck.Left =
                        new CodeMethodInvokeExpression(
                            CodeHelpers.BaseProperty(valueTypeColumn.Key), 
                            "Equals", 
                            new CodePropertySetValueReferenceExpression());
                    changeCheck.Operator = CodeBinaryOperatorType.IdentityEquality;
                    changeCheck.Right = new CodePrimitiveExpression(false);
                }
                else {
                    // can be null, need to be careful of null reference exceptions
                    changeCheck.Left =
                        new CodeBinaryOperatorExpression(
                            CodeHelpers.BasePropertyIsNull(valueTypeColumn.Key), 
                            CodeBinaryOperatorType.BooleanAnd, 
                            new CodeBinaryOperatorExpression(
                                new CodePropertySetValueReferenceExpression(), 
                                CodeBinaryOperatorType.IdentityInequality, 
                                new CodePrimitiveExpression(null)));
                    changeCheck.Operator = CodeBinaryOperatorType.BooleanOr;
                    changeCheck.Right =
                        new CodeBinaryOperatorExpression(
                            CodeHelpers.BasePropertyIsNotNull(valueTypeColumn.Key), 
                            CodeBinaryOperatorType.BooleanAnd, 
                            new CodeBinaryOperatorExpression(
                                new CodeMethodInvokeExpression(
                                    CodeHelpers.BaseProperty(valueTypeColumn.Key), 
                                    "Equals", 
                                    new CodePropertySetValueReferenceExpression()), 
                                CodeBinaryOperatorType.IdentityEquality, 
                                new CodePrimitiveExpression(false)));
                }

                prop.SetStatements.Insert(
                    0, 
                    new CodeConditionStatement(
                        CodeHelpers.ThisPropertyIsTrue("IsTracking"), 
                        new CodeStatement[] {
                                                new CodeConditionStatement(
                                                    new CodeBinaryOperatorExpression(
                                                    new CodeBinaryOperatorExpression(
                                                    new CodeMethodInvokeExpression(
                                                    CodeHelpers.ThisProperty("DirtyProperties"), 
                                                    "Contains", 
                                                    new CodePrimitiveExpression(prop.Name)), 
                                                    CodeBinaryOperatorType.IdentityEquality, 
                                                    new CodePrimitiveExpression(false)), 
                                                    CodeBinaryOperatorType.BooleanAnd, 
                                                    changeCheck), 
                                                    new CodeStatement[] {
                                                                            new CodeExpressionStatement(
                                                                                new CodeMethodInvokeExpression(
                                                                                CodeHelpers.ThisProperty("DirtyProperties"), 
                                                                                "Add", 
                                                                                new CodePrimitiveExpression(prop.Name))), 
                                                                            new CodeAssignStatement(
                                                                                new CodeIndexerExpression(
                                                                                CodeHelpers.ThisProperty("OldValues"), 
                                                                                new CodePrimitiveExpression(prop.Name)), 
                                                                                CodeHelpers.BaseField(prop.Name))
                                                                        }), 
                                                new CodeAssignStatement(
                                                    new CodeIndexerExpression(
                                                    CodeHelpers.ThisProperty("NewValues"), 
                                                    new CodePrimitiveExpression(prop.Name)), 
                                                    new CodePropertySetValueReferenceExpression())
                                            }));
            }

            trackingClass.Members.Add(constructor);

            return trackingClass;
        }