DmlSqlGenerator.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / SqlClient / SqlGen / DmlSqlGenerator.cs / 1 / DmlSqlGenerator.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text; 
using System.Data.Common;
using System.Data.SqlClient; 
using System.Data.Metadata.Edm; 
using System.Data.Common.CommandTrees;
using System.Data.Common.Utils; 
using System.Data.Mapping.Update.Internal;
using System.Data.Mapping;

 
namespace System.Data.SqlClient.SqlGen
{ 
    ///  
    /// Class generating SQL for a DML command tree.
    ///  
    internal static class DmlSqlGenerator
    {
        private const int s_commandTextBuilderInitialCapacity = 256;
 
        internal static string GenerateUpdateSql(DbUpdateCommandTree tree, SqlVersion sqlVersion, out List parameters)
        { 
            const string dummySetParameter = "@p"; 

            StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); 
            ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, sqlVersion);

            if (tree.SetClauses.Count == 0)
            { 
                commandText.AppendLine("declare " + dummySetParameter + " int");
            } 
 
            // update [schemaName].[tableName]
            commandText.Append("update "); 
            tree.Target.Expression.Accept(translator);
            commandText.AppendLine();

            // set c1 = ..., c2 = ..., ... 
            bool first = true;
            commandText.Append("set "); 
            foreach (DbSetClause setClause in tree.SetClauses) 
            {
                if (first) { first = false; } 
                else { commandText.Append(", "); }
                setClause.Property.Accept(translator);
                commandText.Append(" = ");
                setClause.Value.Accept(translator); 
            }
 
            if (first) 
            {
                // If first is still true, it indicates there were no set 
                // clauses. Introduce a fake set clause so that:
                // - we acquire the appropriate locks
                // - server-gen columns (e.g. timestamp) get recomputed
                // 
                // We use the following pattern:
                // 
                //  update Foo 
                //  set @p = 0
                //  where ... 
                commandText.Append(dummySetParameter + " = 0");
            }
            commandText.AppendLine();
 
            // where c1 = ..., c2 = ...
            commandText.Append("where "); 
            tree.Predicate.Accept(translator); 
            commandText.AppendLine();
 
            // generate returning sql
            GenerateReturningSql(commandText, tree, translator, tree.Returning);

            parameters = translator.Parameters; 
            return commandText.ToString();
        } 
 
        internal static string GenerateDeleteSql(DbDeleteCommandTree tree, SqlVersion sqlVersion, out List parameters)
        { 
            StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity);
            ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, false, sqlVersion);

            // delete [schemaName].[tableName] 
            commandText.Append("delete ");
            tree.Target.Expression.Accept(translator); 
            commandText.AppendLine(); 

            // where c1 = ... AND c2 = ... 
            commandText.Append("where ");
            tree.Predicate.Accept(translator);

            parameters = translator.Parameters; 
            return commandText.ToString();
        } 
 
        internal static string GenerateInsertSql(DbInsertCommandTree tree, SqlVersion sqlVersion, out List parameters)
        { 
            StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity);
            ExpressionTranslator translator = new ExpressionTranslator(commandText, tree,
                null != tree.Returning, sqlVersion);
 
            // insert [schemaName].[tableName]
            commandText.Append("insert "); 
            tree.Target.Expression.Accept(translator); 

            if (0 < tree.SetClauses.Count) 
            {
                // (c1, c2, c3, ...)
                commandText.Append("(");
                bool first = true; 
                foreach (DbSetClause setClause in tree.SetClauses)
                { 
                    if (first) { first = false; } 
                    else { commandText.Append(", "); }
                    setClause.Property.Accept(translator); 
                }
                commandText.AppendLine(")");

                // values c1, c2, ... 
                first = true;
                commandText.Append("values ("); 
                foreach (DbSetClause setClause in tree.SetClauses) 
                {
                    if (first) { first = false; } 
                    else { commandText.Append(", "); }
                    setClause.Value.Accept(translator);

                    translator.RegisterMemberValue(setClause.Property, setClause.Value); 
                }
                commandText.AppendLine(")"); 
            } 
            else
            { 
                // default values
                commandText.AppendLine().AppendLine("default values");
            }
            // generate returning sql 
            GenerateReturningSql(commandText, tree, translator, tree.Returning);
 
            parameters = translator.Parameters; 
            return commandText.ToString();
        } 

        // Generates T-SQL describing a member
        // Requires: member must belong to an entity type (a safe requirement for DML
        // SQL gen, where we only access table columns) 
        private static string GenerateMemberTSql(EdmMember member)
        { 
            EntityType entityType = (EntityType)member.DeclaringType; 
            string sql;
            if (!entityType.TryGetMemberSql(member, out sql)) 
            {
                sql = SqlGenerator.QuoteIdentifier(member.Name);
                entityType.SetMemberSql(member, sql);
            } 
            return sql;
        } 
 
        /// 
        /// Generates SQL fragment returning server-generated values. 
        /// Requires: translator knows about member values so that we can figure out
        /// how to construct the key predicate.
        /// 
        /// Sample SQL: 
        ///
        ///     select IdentityValue 
        ///     from dbo.MyTable 
        ///     where @@ROWCOUNT > 0 and IdentityValue = scope_identity()
        /// 
        /// or
        ///
        ///     select TimestamptValue
        ///     from dbo.MyTable 
        ///     where @@ROWCOUNT > 0 and Id = 1
        /// 
        /// Note that we filter on rowcount to ensure no rows are returned if no rows were modified. 
        /// 
        ///  
        /// Builder containing command text
        /// Modification command tree
        /// Translator used to produce DML SQL statement
        /// for the tree 
        /// Returning expression. If null, the method returns
        /// immediately without producing a SELECT statement. 
        private static void GenerateReturningSql(StringBuilder commandText, DbModificationCommandTree tree, 
            ExpressionTranslator translator, DbExpression returning)
        { 
            // Nothing to do if there is no Returning expression
            if (null == returning) { return; }

            // select 
            commandText.Append("select ");
            returning.Accept(translator); 
            commandText.AppendLine(); 

            // from 
            commandText.Append("from ");
            tree.Target.Expression.Accept(translator);
            commandText.AppendLine();
 
            // where
            commandText.Append("where @@ROWCOUNT > 0"); 
            EntitySetBase table = ((DbScanExpression)tree.Target.Expression).Target; 
            bool identity = false;
            foreach (EdmMember keyMember in table.ElementType.KeyMembers) 
            {
                commandText.Append(" and ");
                commandText.Append(GenerateMemberTSql(keyMember));
                commandText.Append(" = "); 

                // retrieve member value sql. the translator remembers member values 
                // as it constructs the DML statement (which precedes the "returning" 
                // SQL)
                SqlParameter value; 
                if (translator.MemberValues.TryGetValue(keyMember, out value))
                {
                    commandText.Append(value.ParameterName);
                } 
                else
                { 
                    // if no value is registered for the key member, it means it is an identity 
                    // which can be retrieved using the scope_identity() function
                    if (identity) 
                    {
                        // there can be only one server generated key
                        throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_NotSupportedServerGenKey(table.Name));
                    } 

                    if (!IsValidIdentityColumnType(keyMember.TypeUsage)) 
                    { 
                        throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_NotSupportedIdentityType(
                            keyMember.Name, keyMember.TypeUsage.ToString())); 
                    }

                    commandText.Append("scope_identity()");
                    identity = true; 
                }
            } 
        } 

        private static bool IsValidIdentityColumnType(TypeUsage typeUsage) 
        {
            // SQL Server supports the following types for identity columns:
            // tinyint, smallint, int, bigint, decimal(p,0), or numeric(p,0)
 
            // make sure it's a primitive type
            if (typeUsage.EdmType.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType) 
            { 
                return false;
            } 

            // check if this is a supported primitive type (compare by name)
            string typeName = typeUsage.EdmType.Name;
 
            // integer types
            if (typeName == "tinyint" || typeName == "smallint" || 
                typeName == "int" || typeName == "bigint") 
            {
                return true; 
            }

            // variable scale types (require scale = 0)
            if (typeName == "decimal" || typeName == "numeric") 
            {
                Facet scaleFacet; 
                return (typeUsage.Facets.TryGetValue(DbProviderManifest.ScaleFacetName, 
                    false, out scaleFacet) && Convert.ToInt32(scaleFacet.Value, CultureInfo.InvariantCulture) == 0);
            } 

            // type not in supported list
            return false;
        } 

        ///  
        /// Lightweight expression translator for DML expression trees, which have constrained 
        /// scope and support.
        ///  
        private class ExpressionTranslator : BasicExpressionVisitor
        {
            /// 
            /// Initialize a new expression translator populating the given string builder 
            /// with command text. Command text builder and command tree must not be null.
            ///  
            /// Command text with which to populate commands 
            /// Command tree generating SQL
            /// Indicates whether the translator should preserve 
            /// member values while compiling t-SQL (only needed for server generation)
            internal ExpressionTranslator(StringBuilder commandText, DbModificationCommandTree commandTree,
                bool preserveMemberValues, SqlVersion version)
            { 
                Debug.Assert(null != commandText);
                Debug.Assert(null != commandTree); 
                _commandText = commandText; 
                _commandTree = commandTree;
                _version = version; 
                _parameters = new List();
                _memberValues = preserveMemberValues ? new Dictionary() :
                    null;
            } 

            private readonly StringBuilder _commandText; 
            private readonly DbModificationCommandTree _commandTree; 
            private readonly List _parameters;
            private readonly Dictionary _memberValues; 
            private readonly static AliasGenerator s_parameterNames = new AliasGenerator("@", 1000);
            private readonly SqlVersion _version;

            internal List Parameters { get { return _parameters; } } 
            internal Dictionary MemberValues { get { return _memberValues; } }
 
            // generate parameter (name based on parameter ordinal) 
            internal SqlParameter CreateParameter(object value, TypeUsage type)
            { 
                // Suppress the MaxLength facet in the type usage because
                // SqlClient will silently truncate data when SqlParameter.Size < |SqlParameter.Value|.
                const bool ignoreMaxLengthFacet = true;
                SqlParameter parameter = SqlProviderServices.CreateSqlParameter(s_parameterNames.GetName(_parameters.Count), type, ParameterMode.In, value, ignoreMaxLengthFacet, _version); 

                _parameters.Add(parameter); 
 
                return parameter;
            } 

            public override void Visit(DbAndExpression expression)
            {
                VisitBinary(expression, " and "); 
            }
 
            public override void Visit(DbOrExpression expression) 
            {
                VisitBinary(expression, " or "); 
            }

            public override void Visit(DbComparisonExpression expression)
            { 
                Debug.Assert(expression.ExpressionKind == DbExpressionKind.Equals,
                    "only equals comparison expressions are produced in DML command trees in V1"); 
 
                VisitBinary(expression, " = ");
 
                RegisterMemberValue(expression.Left, expression.Right);
            }

            ///  
            /// Call this method to register a property value pair so the translator "remembers"
            /// the values for members of the row being modified. These values can then be used 
            /// to form a predicate for server-generation (based on the key of the row) 
            /// 
            /// DbExpression containing the column reference (property expression). 
            /// DbExpression containing the value of the column.
            internal void RegisterMemberValue(DbExpression propertyExpression, DbExpression value)
            {
                if (null != _memberValues) 
                {
                    // register the value for this property 
                    Debug.Assert(propertyExpression.ExpressionKind == DbExpressionKind.Property, 
                        "DML predicates and setters must be of the form property = value");
 
                    // get name of left property
                    EdmMember property = ((DbPropertyExpression)propertyExpression).Property;

                    // don't track null values 
                    if (value.ExpressionKind != DbExpressionKind.Null)
                    { 
                        Debug.Assert(value.ExpressionKind == DbExpressionKind.Constant, 
                            "value must either constant or null");
                        // retrieve the last parameter added (which describes the parameter) 
                        _memberValues[property] = _parameters[_parameters.Count - 1];
                    }
                }
            } 

            public override void Visit(DbIsNullExpression expression) 
            { 
                expression.Argument.Accept(this);
                _commandText.Append(" is null"); 
            }

            public override void Visit(DbNotExpression expression)
            { 
                _commandText.Append("not (");
                expression.Accept(this); 
                _commandText.Append(")"); 
            }
 
            public override void Visit(DbConstantExpression expression)
            {
                SqlParameter parameter = CreateParameter(expression.Value, expression.ResultType);
                _commandText.Append(parameter.ParameterName); 
            }
 
            public override void Visit(DbScanExpression expression) 
            {
                // we know we won't hit this code unless there is no function defined for this 
                // ModificationOperation, so if this EntitySet is using a DefiningQuery, instead
                // of a table, that is an error
                if (expression.Target.DefiningQuery != null)
                { 
                    string missingCudElement;
                    if (_commandTree.CommandTreeKind == DbCommandTreeKind.Delete) 
                    { 
                        missingCudElement = StorageMslConstructs.DeleteFunctionElement;
                    } 
                    else if (_commandTree.CommandTreeKind == DbCommandTreeKind.Insert)
                    {
                        missingCudElement = StorageMslConstructs.InsertFunctionElement;
                    } 
                    else
                    { 
                        Debug.Assert(_commandTree.CommandTreeKind == DbCommandTreeKind.Update, "did you add a new option?"); 
                        missingCudElement = StorageMslConstructs.UpdateFunctionElement;
                    } 
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_SqlEntitySetWithoutDmlFunctions(expression.Target.Name, missingCudElement, StorageMslConstructs.ModificationFunctionMappingElement), null);
                }

                _commandText.Append(SqlGenerator.GetTargetTSql(expression.Target)); 
            }
 
            public override void Visit(DbPropertyExpression expression) 
            {
                _commandText.Append(GenerateMemberTSql(expression.Property)); 
            }

            public override void Visit(DbNullExpression expression)
            { 
                _commandText.Append("null");
            } 
 
            public override void Visit(DbNewInstanceExpression expression)
            { 
                // assumes all arguments are self-describing (no need to use aliases
                // because no renames are ever used in the projection)
                bool first = true;
                foreach (DbExpression argument in expression.Arguments) 
                {
                    if (first) { first = false; } 
                    else { _commandText.Append(", "); } 
                    argument.Accept(this);
                } 
            }

            private void VisitBinary(DbBinaryExpression expression, string separator)
            { 
                _commandText.Append("(");
                expression.Left.Accept(this); 
                _commandText.Append(separator); 
                expression.Right.Accept(this);
                _commandText.Append(")"); 
            }

        }
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text; 
using System.Data.Common;
using System.Data.SqlClient; 
using System.Data.Metadata.Edm; 
using System.Data.Common.CommandTrees;
using System.Data.Common.Utils; 
using System.Data.Mapping.Update.Internal;
using System.Data.Mapping;

 
namespace System.Data.SqlClient.SqlGen
{ 
    ///  
    /// Class generating SQL for a DML command tree.
    ///  
    internal static class DmlSqlGenerator
    {
        private const int s_commandTextBuilderInitialCapacity = 256;
 
        internal static string GenerateUpdateSql(DbUpdateCommandTree tree, SqlVersion sqlVersion, out List parameters)
        { 
            const string dummySetParameter = "@p"; 

            StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); 
            ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, sqlVersion);

            if (tree.SetClauses.Count == 0)
            { 
                commandText.AppendLine("declare " + dummySetParameter + " int");
            } 
 
            // update [schemaName].[tableName]
            commandText.Append("update "); 
            tree.Target.Expression.Accept(translator);
            commandText.AppendLine();

            // set c1 = ..., c2 = ..., ... 
            bool first = true;
            commandText.Append("set "); 
            foreach (DbSetClause setClause in tree.SetClauses) 
            {
                if (first) { first = false; } 
                else { commandText.Append(", "); }
                setClause.Property.Accept(translator);
                commandText.Append(" = ");
                setClause.Value.Accept(translator); 
            }
 
            if (first) 
            {
                // If first is still true, it indicates there were no set 
                // clauses. Introduce a fake set clause so that:
                // - we acquire the appropriate locks
                // - server-gen columns (e.g. timestamp) get recomputed
                // 
                // We use the following pattern:
                // 
                //  update Foo 
                //  set @p = 0
                //  where ... 
                commandText.Append(dummySetParameter + " = 0");
            }
            commandText.AppendLine();
 
            // where c1 = ..., c2 = ...
            commandText.Append("where "); 
            tree.Predicate.Accept(translator); 
            commandText.AppendLine();
 
            // generate returning sql
            GenerateReturningSql(commandText, tree, translator, tree.Returning);

            parameters = translator.Parameters; 
            return commandText.ToString();
        } 
 
        internal static string GenerateDeleteSql(DbDeleteCommandTree tree, SqlVersion sqlVersion, out List parameters)
        { 
            StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity);
            ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, false, sqlVersion);

            // delete [schemaName].[tableName] 
            commandText.Append("delete ");
            tree.Target.Expression.Accept(translator); 
            commandText.AppendLine(); 

            // where c1 = ... AND c2 = ... 
            commandText.Append("where ");
            tree.Predicate.Accept(translator);

            parameters = translator.Parameters; 
            return commandText.ToString();
        } 
 
        internal static string GenerateInsertSql(DbInsertCommandTree tree, SqlVersion sqlVersion, out List parameters)
        { 
            StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity);
            ExpressionTranslator translator = new ExpressionTranslator(commandText, tree,
                null != tree.Returning, sqlVersion);
 
            // insert [schemaName].[tableName]
            commandText.Append("insert "); 
            tree.Target.Expression.Accept(translator); 

            if (0 < tree.SetClauses.Count) 
            {
                // (c1, c2, c3, ...)
                commandText.Append("(");
                bool first = true; 
                foreach (DbSetClause setClause in tree.SetClauses)
                { 
                    if (first) { first = false; } 
                    else { commandText.Append(", "); }
                    setClause.Property.Accept(translator); 
                }
                commandText.AppendLine(")");

                // values c1, c2, ... 
                first = true;
                commandText.Append("values ("); 
                foreach (DbSetClause setClause in tree.SetClauses) 
                {
                    if (first) { first = false; } 
                    else { commandText.Append(", "); }
                    setClause.Value.Accept(translator);

                    translator.RegisterMemberValue(setClause.Property, setClause.Value); 
                }
                commandText.AppendLine(")"); 
            } 
            else
            { 
                // default values
                commandText.AppendLine().AppendLine("default values");
            }
            // generate returning sql 
            GenerateReturningSql(commandText, tree, translator, tree.Returning);
 
            parameters = translator.Parameters; 
            return commandText.ToString();
        } 

        // Generates T-SQL describing a member
        // Requires: member must belong to an entity type (a safe requirement for DML
        // SQL gen, where we only access table columns) 
        private static string GenerateMemberTSql(EdmMember member)
        { 
            EntityType entityType = (EntityType)member.DeclaringType; 
            string sql;
            if (!entityType.TryGetMemberSql(member, out sql)) 
            {
                sql = SqlGenerator.QuoteIdentifier(member.Name);
                entityType.SetMemberSql(member, sql);
            } 
            return sql;
        } 
 
        /// 
        /// Generates SQL fragment returning server-generated values. 
        /// Requires: translator knows about member values so that we can figure out
        /// how to construct the key predicate.
        /// 
        /// Sample SQL: 
        ///
        ///     select IdentityValue 
        ///     from dbo.MyTable 
        ///     where @@ROWCOUNT > 0 and IdentityValue = scope_identity()
        /// 
        /// or
        ///
        ///     select TimestamptValue
        ///     from dbo.MyTable 
        ///     where @@ROWCOUNT > 0 and Id = 1
        /// 
        /// Note that we filter on rowcount to ensure no rows are returned if no rows were modified. 
        /// 
        ///  
        /// Builder containing command text
        /// Modification command tree
        /// Translator used to produce DML SQL statement
        /// for the tree 
        /// Returning expression. If null, the method returns
        /// immediately without producing a SELECT statement. 
        private static void GenerateReturningSql(StringBuilder commandText, DbModificationCommandTree tree, 
            ExpressionTranslator translator, DbExpression returning)
        { 
            // Nothing to do if there is no Returning expression
            if (null == returning) { return; }

            // select 
            commandText.Append("select ");
            returning.Accept(translator); 
            commandText.AppendLine(); 

            // from 
            commandText.Append("from ");
            tree.Target.Expression.Accept(translator);
            commandText.AppendLine();
 
            // where
            commandText.Append("where @@ROWCOUNT > 0"); 
            EntitySetBase table = ((DbScanExpression)tree.Target.Expression).Target; 
            bool identity = false;
            foreach (EdmMember keyMember in table.ElementType.KeyMembers) 
            {
                commandText.Append(" and ");
                commandText.Append(GenerateMemberTSql(keyMember));
                commandText.Append(" = "); 

                // retrieve member value sql. the translator remembers member values 
                // as it constructs the DML statement (which precedes the "returning" 
                // SQL)
                SqlParameter value; 
                if (translator.MemberValues.TryGetValue(keyMember, out value))
                {
                    commandText.Append(value.ParameterName);
                } 
                else
                { 
                    // if no value is registered for the key member, it means it is an identity 
                    // which can be retrieved using the scope_identity() function
                    if (identity) 
                    {
                        // there can be only one server generated key
                        throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_NotSupportedServerGenKey(table.Name));
                    } 

                    if (!IsValidIdentityColumnType(keyMember.TypeUsage)) 
                    { 
                        throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_NotSupportedIdentityType(
                            keyMember.Name, keyMember.TypeUsage.ToString())); 
                    }

                    commandText.Append("scope_identity()");
                    identity = true; 
                }
            } 
        } 

        private static bool IsValidIdentityColumnType(TypeUsage typeUsage) 
        {
            // SQL Server supports the following types for identity columns:
            // tinyint, smallint, int, bigint, decimal(p,0), or numeric(p,0)
 
            // make sure it's a primitive type
            if (typeUsage.EdmType.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType) 
            { 
                return false;
            } 

            // check if this is a supported primitive type (compare by name)
            string typeName = typeUsage.EdmType.Name;
 
            // integer types
            if (typeName == "tinyint" || typeName == "smallint" || 
                typeName == "int" || typeName == "bigint") 
            {
                return true; 
            }

            // variable scale types (require scale = 0)
            if (typeName == "decimal" || typeName == "numeric") 
            {
                Facet scaleFacet; 
                return (typeUsage.Facets.TryGetValue(DbProviderManifest.ScaleFacetName, 
                    false, out scaleFacet) && Convert.ToInt32(scaleFacet.Value, CultureInfo.InvariantCulture) == 0);
            } 

            // type not in supported list
            return false;
        } 

        ///  
        /// Lightweight expression translator for DML expression trees, which have constrained 
        /// scope and support.
        ///  
        private class ExpressionTranslator : BasicExpressionVisitor
        {
            /// 
            /// Initialize a new expression translator populating the given string builder 
            /// with command text. Command text builder and command tree must not be null.
            ///  
            /// Command text with which to populate commands 
            /// Command tree generating SQL
            /// Indicates whether the translator should preserve 
            /// member values while compiling t-SQL (only needed for server generation)
            internal ExpressionTranslator(StringBuilder commandText, DbModificationCommandTree commandTree,
                bool preserveMemberValues, SqlVersion version)
            { 
                Debug.Assert(null != commandText);
                Debug.Assert(null != commandTree); 
                _commandText = commandText; 
                _commandTree = commandTree;
                _version = version; 
                _parameters = new List();
                _memberValues = preserveMemberValues ? new Dictionary() :
                    null;
            } 

            private readonly StringBuilder _commandText; 
            private readonly DbModificationCommandTree _commandTree; 
            private readonly List _parameters;
            private readonly Dictionary _memberValues; 
            private readonly static AliasGenerator s_parameterNames = new AliasGenerator("@", 1000);
            private readonly SqlVersion _version;

            internal List Parameters { get { return _parameters; } } 
            internal Dictionary MemberValues { get { return _memberValues; } }
 
            // generate parameter (name based on parameter ordinal) 
            internal SqlParameter CreateParameter(object value, TypeUsage type)
            { 
                // Suppress the MaxLength facet in the type usage because
                // SqlClient will silently truncate data when SqlParameter.Size < |SqlParameter.Value|.
                const bool ignoreMaxLengthFacet = true;
                SqlParameter parameter = SqlProviderServices.CreateSqlParameter(s_parameterNames.GetName(_parameters.Count), type, ParameterMode.In, value, ignoreMaxLengthFacet, _version); 

                _parameters.Add(parameter); 
 
                return parameter;
            } 

            public override void Visit(DbAndExpression expression)
            {
                VisitBinary(expression, " and "); 
            }
 
            public override void Visit(DbOrExpression expression) 
            {
                VisitBinary(expression, " or "); 
            }

            public override void Visit(DbComparisonExpression expression)
            { 
                Debug.Assert(expression.ExpressionKind == DbExpressionKind.Equals,
                    "only equals comparison expressions are produced in DML command trees in V1"); 
 
                VisitBinary(expression, " = ");
 
                RegisterMemberValue(expression.Left, expression.Right);
            }

            ///  
            /// Call this method to register a property value pair so the translator "remembers"
            /// the values for members of the row being modified. These values can then be used 
            /// to form a predicate for server-generation (based on the key of the row) 
            /// 
            /// DbExpression containing the column reference (property expression). 
            /// DbExpression containing the value of the column.
            internal void RegisterMemberValue(DbExpression propertyExpression, DbExpression value)
            {
                if (null != _memberValues) 
                {
                    // register the value for this property 
                    Debug.Assert(propertyExpression.ExpressionKind == DbExpressionKind.Property, 
                        "DML predicates and setters must be of the form property = value");
 
                    // get name of left property
                    EdmMember property = ((DbPropertyExpression)propertyExpression).Property;

                    // don't track null values 
                    if (value.ExpressionKind != DbExpressionKind.Null)
                    { 
                        Debug.Assert(value.ExpressionKind == DbExpressionKind.Constant, 
                            "value must either constant or null");
                        // retrieve the last parameter added (which describes the parameter) 
                        _memberValues[property] = _parameters[_parameters.Count - 1];
                    }
                }
            } 

            public override void Visit(DbIsNullExpression expression) 
            { 
                expression.Argument.Accept(this);
                _commandText.Append(" is null"); 
            }

            public override void Visit(DbNotExpression expression)
            { 
                _commandText.Append("not (");
                expression.Accept(this); 
                _commandText.Append(")"); 
            }
 
            public override void Visit(DbConstantExpression expression)
            {
                SqlParameter parameter = CreateParameter(expression.Value, expression.ResultType);
                _commandText.Append(parameter.ParameterName); 
            }
 
            public override void Visit(DbScanExpression expression) 
            {
                // we know we won't hit this code unless there is no function defined for this 
                // ModificationOperation, so if this EntitySet is using a DefiningQuery, instead
                // of a table, that is an error
                if (expression.Target.DefiningQuery != null)
                { 
                    string missingCudElement;
                    if (_commandTree.CommandTreeKind == DbCommandTreeKind.Delete) 
                    { 
                        missingCudElement = StorageMslConstructs.DeleteFunctionElement;
                    } 
                    else if (_commandTree.CommandTreeKind == DbCommandTreeKind.Insert)
                    {
                        missingCudElement = StorageMslConstructs.InsertFunctionElement;
                    } 
                    else
                    { 
                        Debug.Assert(_commandTree.CommandTreeKind == DbCommandTreeKind.Update, "did you add a new option?"); 
                        missingCudElement = StorageMslConstructs.UpdateFunctionElement;
                    } 
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_SqlEntitySetWithoutDmlFunctions(expression.Target.Name, missingCudElement, StorageMslConstructs.ModificationFunctionMappingElement), null);
                }

                _commandText.Append(SqlGenerator.GetTargetTSql(expression.Target)); 
            }
 
            public override void Visit(DbPropertyExpression expression) 
            {
                _commandText.Append(GenerateMemberTSql(expression.Property)); 
            }

            public override void Visit(DbNullExpression expression)
            { 
                _commandText.Append("null");
            } 
 
            public override void Visit(DbNewInstanceExpression expression)
            { 
                // assumes all arguments are self-describing (no need to use aliases
                // because no renames are ever used in the projection)
                bool first = true;
                foreach (DbExpression argument in expression.Arguments) 
                {
                    if (first) { first = false; } 
                    else { _commandText.Append(", "); } 
                    argument.Accept(this);
                } 
            }

            private void VisitBinary(DbBinaryExpression expression, string separator)
            { 
                _commandText.Append("(");
                expression.Left.Accept(this); 
                _commandText.Append(separator); 
                expression.Right.Accept(this);
                _commandText.Append(")"); 
            }

        }
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK