1 //---------------------------------------------------------------------
2 // <copyright file="EntitySqlException.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
13 using System.Data.Common.EntitySql;
14 using System.Data.Entity;
15 using System.Diagnostics;
16 using System.Globalization;
17 using System.Runtime.Serialization;
18 using System.Security;
19 using System.Security.Permissions;
23 /// Represents an eSQL Query compilation exception;
24 /// The class of exceptional conditions that may cause this exception to be raised are mainly:
25 /// 1) Syntax Errors: raised during query text parsing and when a given query does not conform to eSQL formal grammar;
26 /// 2) Semantic Errors: raised when semantic rules of eSQL language are not met such as metadata or schema information
27 /// not accurate or not present, type validation errors, scoping rule violations, user of undefined variables, etc.
28 /// For more information, see eSQL Language Spec.
31 public sealed class EntitySqlException : EntityException
33 #region Private Fields
35 /// error message description.
37 private string _errorDescription;
40 /// information about the context where the error occurred
42 private string _errorContext;
50 /// error column number
55 #region Public Constructors
57 /// Initializes a new instance of <see cref="EntitySqlException"/> with the generic error message.
59 public EntitySqlException()
60 : this(System.Data.Entity.Strings.GeneralQueryError)
62 HResult = HResults.InvalidQuery;
66 /// Initializes a new instance of <see cref="EntitySqlException"/> with the given message.
68 public EntitySqlException(string message)
71 HResult = HResults.InvalidQuery;
75 /// Initializes a new instance of <see cref="EntitySqlException"/> with the given message and innerException instance.
77 public EntitySqlException(string message, Exception innerException)
78 : base(message, innerException)
80 HResult = HResults.InvalidQuery;
84 /// Initializes a new instance <see cref="EntitySqlException"/> with the given serializationInfo and streamingContext.
86 /// <param name="serializationInfo"></param>
87 /// <param name="streamingContext"></param>
88 private EntitySqlException(SerializationInfo serializationInfo, StreamingContext streamingContext)
89 : base(serializationInfo, streamingContext)
91 HResult = HResults.InvalidQuery;
92 _errorDescription = serializationInfo.GetString("ErrorDescription");
93 _errorContext = serializationInfo.GetString("ErrorContext");
94 _line = serializationInfo.GetInt32("Line");
95 _column = serializationInfo.GetInt32("Column");
99 #region Internal Constructors
101 /// Initializes a new instance EntityException with an ErrorContext instance and a given error message.
103 internal static EntitySqlException Create(ErrorContext errCtx, string errorMessage, Exception innerException)
105 return EntitySqlException.Create(errCtx.CommandText, errorMessage, errCtx.InputPosition, errCtx.ErrorContextInfo, errCtx.UseContextInfoAsResourceIdentifier, innerException);
109 /// Initializes a new instance EntityException with contextual information to allow detailed error feedback.
111 internal static EntitySqlException Create(string commandText,
112 string errorDescription,
114 string errorContextInfo,
115 bool loadErrorContextInfoFromResource,
116 Exception innerException)
120 string errorContext = FormatErrorContext(commandText, errorPosition, errorContextInfo, loadErrorContextInfoFromResource, out line, out column);
122 string errorMessage = FormatQueryError(errorDescription, errorContext);
124 return new EntitySqlException(errorMessage, errorDescription, errorContext, line, column, innerException);
130 private EntitySqlException(string message, string errorDescription, string errorContext, int line, int column, Exception innerException)
131 : base(message, innerException)
133 _errorDescription = errorDescription;
134 _errorContext = errorContext;
138 HResult = HResults.InvalidQuery;
143 #region Public Properties
145 /// Gets the error description explaining the reason why the query was not accepted or an empty String.Empty
147 public string ErrorDescription
151 return _errorDescription ?? String.Empty;
156 /// Gets the aproximate context where the error occurred if available.
158 public string ErrorContext
162 return _errorContext ?? String.Empty;
167 /// Returns the the aproximate line number where the error occurred
171 get { return _line; }
175 /// Returns the the aproximate column number where the error occurred
179 get { return _column; }
184 internal static string GetGenericErrorMessage(string commandText, int position)
188 return FormatErrorContext(commandText, position, EntityRes.GenericSyntaxError, true, out lineNumber, out colNumber);
192 /// Returns error context in the format [[errorContextInfo, ]line ddd, column ddd].
193 /// Returns empty string if errorPosition is less than 0 and errorContextInfo is not specified.
195 internal static string FormatErrorContext(
198 string errorContextInfo,
199 bool loadErrorContextInfoFromResource,
201 out int columnNumber)
203 Debug.Assert(errorPosition > -1, "position in input stream cannot be < 0");
204 Debug.Assert(errorPosition <= commandText.Length, "position in input stream cannot be greater than query text size");
206 if (loadErrorContextInfoFromResource)
208 errorContextInfo = !String.IsNullOrEmpty(errorContextInfo) ? EntityRes.GetString(errorContextInfo) : String.Empty;
212 // Replace control chars and newLines for single representation characters
214 StringBuilder sb = new StringBuilder(commandText.Length);
215 for (int i = 0; i < commandText.Length; i++)
217 Char c = commandText[i];
218 if (CqlLexer.IsNewLine(c))
222 else if ((Char.IsControl(c) || Char.IsWhiteSpace(c)) && ('\r' != c))
228 commandText = sb.ToString().TrimEnd(new char[] { '\n' });
231 // Compute line and column
233 string[] queryLines = commandText.Split(new char[] { '\n' }, StringSplitOptions.None);
234 for (lineNumber = 0, columnNumber = errorPosition;
235 lineNumber < queryLines.Length && columnNumber > queryLines[lineNumber].Length;
236 columnNumber -= (queryLines[lineNumber].Length + 1), ++lineNumber) ;
238 ++lineNumber; // switch lineNum and colNum to 1-based indexes
242 // Error context format: "[errorContextInfo,] line ddd, column ddd"
244 sb = new Text.StringBuilder();
245 if (!String.IsNullOrEmpty(errorContextInfo))
247 sb.AppendFormat(CultureInfo.CurrentCulture, "{0}, ", errorContextInfo);
250 if (errorPosition >= 0)
252 sb.AppendFormat(CultureInfo.CurrentCulture,
254 System.Data.Entity.Strings.LocalizedLine,
256 System.Data.Entity.Strings.LocalizedColumn,
260 return sb.ToString();
264 /// Returns error message in the format: "error such and such[, near errorContext]."
266 private static string FormatQueryError(string errorMessage, string errorContext)
269 // Message format: error such and such[, near errorContextInfo].
271 StringBuilder sb = new StringBuilder();
272 sb.Append(errorMessage);
273 if (!String.IsNullOrEmpty(errorContext))
275 sb.AppendFormat(CultureInfo.CurrentCulture, " {0} {1}", System.Data.Entity.Strings.LocalizedNear, errorContext);
278 return sb.Append(".").ToString();
282 #region ISerializable implementation
284 /// sets the System.Runtime.Serialization.SerializationInfo
285 /// with information about the exception.
287 /// <param name="info">The System.Runtime.Serialization.SerializationInfo that holds the serialized
288 /// object data about the exception being thrown.
290 /// <param name="context"></param>
292 [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
293 public override void GetObjectData(SerializationInfo info, StreamingContext context)
295 base.GetObjectData(info, context);
296 info.AddValue("ErrorDescription", _errorDescription);
297 info.AddValue("ErrorContext", _errorContext);
298 info.AddValue("Line", _line);
299 info.AddValue("Column", _column);