예제 #1
0
        private CodeMemberProperty GenerateGetSetProperty(
            CodeTypeDeclaration owningClass,
            string name,
            Type type,
            MemberAttributes attributes,
            bool useBaseProperty = false)
        {
            // generate the property
            var prop = new CodeMemberProperty();

            prop.Name       = name;
            prop.Type       = new CodeTypeReference(type);
            prop.Attributes = attributes;

            if (useBaseProperty)
            {
                prop.GetStatements.Add(
                    new CodeMethodReturnStatement(CodeHelpers.BaseProperty(name)));
                prop.SetStatements.Add(
                    new CodeAssignStatement(
                        CodeHelpers.BaseProperty(name),
                        new CodePropertySetValueReferenceExpression()));
            }
            else
            {
                // generate the backing field for this property
                var backingField = new CodeMemberField();
                backingField.Name = "backing" + name;
                backingField.Type = new CodeTypeReference(type);
                owningClass.Members.Add(backingField);

                prop.GetStatements.Add(
                    new CodeMethodReturnStatement(CodeHelpers.ThisField(backingField.Name)));
                prop.SetStatements.Add(
                    new CodeAssignStatement(
                        CodeHelpers.ThisField(backingField.Name),
                        new CodePropertySetValueReferenceExpression()));
            }

            owningClass.Members.Add(prop);

            return(prop);
        }
예제 #2
0
        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);
        }