diff Source/ServiceModel/LinqServiceSerializer.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/ServiceModel/LinqServiceSerializer.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,1699 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Data;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+
+namespace BLToolkit.ServiceModel
+{
+	using Data.Sql;
+	using Data.Sql.SqlProvider;
+	using Mapping;
+	using Reflection;
+
+	static class LinqServiceSerializer
+	{
+		#region Public Members
+
+		public static string Serialize(SqlQuery query, SqlParameter[] parameters)
+		{
+			return new QuerySerializer().Serialize(query, parameters);
+		}
+
+		public static LinqServiceQuery Deserialize(string str)
+		{
+			return new QueryDeserializer().Deserialize(str);
+		}
+
+		public static string Serialize(LinqServiceResult result)
+		{
+			return new ResultSerializer().Serialize(result);
+		}
+
+		public static LinqServiceResult DeserializeResult(string str)
+		{
+			return new ResultDeserializer().DeserializeResult(str);
+		}
+
+		public static string Serialize(string[] data)
+		{
+			return new StringArraySerializer().Serialize(data);
+		}
+
+		public static string[] DeserializeStringArray(string str)
+		{
+			return new StringArrayDeserializer().Deserialize(str);
+		}
+
+		#endregion
+
+		#region SerializerBase
+
+		const int _paramIndex     = -1;
+		const int _typeIndex      = -2;
+		const int _typeArrayIndex = -3;
+
+		class SerializerBase
+		{
+			protected readonly StringBuilder          Builder = new StringBuilder();
+			protected readonly Dictionary<object,int> Dic     = new Dictionary<object,int>();
+			protected int                             Index;
+
+			string ConvertToString(Type type, object value)
+			{
+				switch (Type.GetTypeCode(type))
+				{
+					case TypeCode.Decimal  : return ((decimal) value).ToString(CultureInfo.InvariantCulture);
+					case TypeCode.Double   : return ((double)  value).ToString(CultureInfo.InvariantCulture);
+					case TypeCode.Single   : return ((float)   value).ToString(CultureInfo.InvariantCulture);
+					case TypeCode.DateTime : return ((DateTime)value).ToString("o");
+				}
+
+				if (type == typeof(DateTimeOffset))
+					return ((DateTimeOffset)value).ToString("o");
+
+				return Common.Convert.ToString(value);
+			}
+
+			protected void Append(Type type, object value)
+			{
+				Append(type);
+
+				if (value == null)
+					Append((string)null);
+				else if (!type.IsArray)
+				{
+					Append(ConvertToString(type, value));
+				}
+				else
+				{
+					var elementType = type.GetElementType();
+
+					Builder.Append(' ');
+
+					var len = Builder.Length;
+					var cnt = 0;
+
+					if (elementType.IsArray)
+						foreach (var val in (IEnumerable)value)
+						{
+							Append(elementType, val);
+							cnt++;
+						}
+					else
+						foreach (var val in (IEnumerable)value)
+						{
+							if (val == null)
+								Append((string)null);
+							else
+								Append(ConvertToString(val.GetType(), val));
+							cnt++;
+						}
+
+					Builder.Insert(len, cnt.ToString(CultureInfo.CurrentCulture));
+				}
+			}
+
+			protected void Append(int value)
+			{
+				Builder.Append(' ').Append(value);
+			}
+
+			protected void Append(Type value)
+			{
+				Builder.Append(' ').Append(value == null ? 0 : GetType(value));
+			}
+
+			protected void Append(bool value)
+			{
+				Builder.Append(' ').Append(value ? '1' : '0');
+			}
+
+			protected void Append(IQueryElement element)
+			{
+				Builder.Append(' ').Append(element == null ? 0 : Dic[element]);
+			}
+
+			protected void Append(string str)
+			{
+				Builder.Append(' ');
+
+				if (str == null)
+				{
+					Builder.Append('-');
+				}
+				else if (str.Length == 0)
+				{
+					Builder.Append('0');
+				}
+				else
+				{
+					Builder
+						.Append(str.Length)
+						.Append(':')
+						.Append(str);
+				}
+			}
+
+			protected int GetType(Type type)
+			{
+				if (type == null)
+					return 0;
+
+				int idx;
+
+				if (!Dic.TryGetValue(type, out idx))
+				{
+					if (type.IsArray)
+					{
+						var elementType = GetType(type.GetElementType());
+
+						Dic.Add(type, idx = ++Index);
+
+						Builder
+							.Append(idx)
+							.Append(' ')
+							.Append(_typeArrayIndex)
+							.Append(' ')
+							.Append(elementType);
+					}
+					else
+					{
+						Dic.Add(type, idx = ++Index);
+
+						Builder
+							.Append(idx)
+							.Append(' ')
+							.Append(_typeIndex);
+
+						Append(type.FullName);
+					}
+
+					Builder.AppendLine();
+				}
+
+				return idx;
+			}
+		}
+
+		#endregion
+
+		#region DeserializerBase
+
+		public class DeserializerBase
+		{
+			protected readonly Dictionary<int,object> Dic = new Dictionary<int,object>();
+
+			protected string Str;
+			protected int    Pos;
+
+			protected char Peek()
+			{
+				return Str[Pos];
+			}
+
+			char Next()
+			{
+				return Str[++Pos];
+			}
+
+			protected bool Get(char c)
+			{
+				if (Peek() == c)
+				{
+					Pos++;
+					return true;
+				}
+
+				return false;
+			}
+
+			protected int ReadInt()
+			{
+				Get(' ');
+
+				var minus = Get('-');
+				var value = 0;
+
+				for (var c = Peek(); char.IsDigit(c); c = Next())
+					value = value * 10 + (c - '0');
+
+				return minus ? -value : value;
+			}
+
+			protected int? ReadCount()
+			{
+				Get(' ');
+
+				if (Get('-'))
+					return null;
+
+				var value = 0;
+
+				for (var c = Peek(); char.IsDigit(c); c = Next())
+					value = value * 10 + (c - '0');
+
+				return value;
+			}
+
+			protected string ReadString()
+			{
+				Get(' ');
+
+				var c = Peek();
+
+				if (c == '-')
+				{
+					Pos++;
+					return null;
+				}
+
+				if (c == '0')
+				{
+					Pos++;
+					return string.Empty;
+				}
+
+				var len   = ReadInt();
+				var value = Str.Substring(++Pos, len);
+
+				Pos += len;
+
+				return value;
+			}
+
+			protected bool ReadBool()
+			{
+				Get(' ');
+
+				var value = Peek() == '1';
+
+				Pos++;
+
+				return value;
+			}
+
+			protected T Read<T>()
+				where T : class
+			{
+				var idx = ReadInt();
+				return idx == 0 ? null : (T)Dic[idx];
+			}
+
+			protected T[] ReadArray<T>()
+				where T : class
+			{
+				var count = ReadCount();
+
+				if (count == null)
+					return null;
+
+				var items = new T[count.Value];
+
+				for (var i = 0; i < count; i++)
+					items[i] = Read<T>();
+
+				return items;
+			}
+
+			protected List<T> ReadList<T>()
+				where T : class
+			{
+				var count = ReadCount();
+
+				if (count == null)
+					return null;
+
+				var items = new List<T>(count.Value);
+
+				for (var i = 0; i < count; i++)
+					items.Add(Read<T>());
+
+				return items;
+			}
+
+			protected void NextLine()
+			{
+				while (Pos < Str.Length && (Peek() == '\n' || Peek() == '\r'))
+					Pos++;
+			}
+
+			interface IDeserializerHelper
+			{
+				object GetArray(DeserializerBase deserializer);
+			}
+
+			class DeserializerHelper<T> : IDeserializerHelper
+			{
+				public object GetArray(DeserializerBase deserializer)
+				{
+					var count = deserializer.ReadCount();
+
+					if (count == null)
+						return null;
+
+					var arr   = new T[count.Value];
+					var type  = typeof(T);
+
+					for (var i = 0; i < count.Value; i++)
+						arr[i] = (T)deserializer.ReadValue(type);
+
+					return arr;
+				}
+			}
+
+			static readonly Dictionary<Type,Func<DeserializerBase,object>> _arrayDeserializers =
+				new Dictionary<Type,Func<DeserializerBase,object>>();
+
+			protected object ReadValue(Type type)
+			{
+				if (type == null)
+					return ReadString();
+
+				if (type.IsArray)
+				{
+					var elem = type.GetElementType();
+
+					Func<DeserializerBase,object> deserializer;
+
+					lock (_arrayDeserializers)
+					{
+						if (!_arrayDeserializers.TryGetValue(elem, out deserializer))
+						{
+							var helper = (IDeserializerHelper)Activator.CreateInstance(typeof(DeserializerHelper<>).MakeGenericType(elem));
+							_arrayDeserializers.Add(elem, deserializer = helper.GetArray);
+						}
+					}
+
+					return deserializer(this);
+				}
+
+				var str = ReadString();
+
+				switch (Type.GetTypeCode(type))
+				{
+					case TypeCode.Decimal  : return decimal. Parse(str, CultureInfo.InvariantCulture);
+					case TypeCode.Double   : return double.  Parse(str, CultureInfo.InvariantCulture);
+					case TypeCode.Single   : return float.   Parse(str, CultureInfo.InvariantCulture);
+					case TypeCode.DateTime : return DateTime.ParseExact(str, "o", CultureInfo.InvariantCulture);
+				}
+
+				if (type == typeof(DateTimeOffset))
+					return DateTimeOffset.ParseExact(str, "o", CultureInfo.InvariantCulture);
+
+				return Common.Convert.ChangeTypeFromString(str, type);
+			}
+
+			protected readonly List<string> UnresolvedTypes = new List<string>();
+
+			protected Type ResolveType(string str)
+			{
+				if (str == null)
+					return null;
+
+				var type = Type.GetType(str, false);
+
+				if (type == null)
+				{
+					if (str == "System.Data.Linq.Binary")
+						return typeof(System.Data.Linq.Binary);
+
+#if !SILVERLIGHT
+
+					type = LinqService.TypeResolver(str);
+
+#endif
+
+					if (type == null)
+					{
+						UnresolvedTypes.Add(str);
+
+						Debug.WriteLine(
+							string.Format("Type '{0}' cannot be resolved. Use LinqService.TypeResolver to resolve unknown types.", str),
+							"LinqServiceSerializer");
+					}
+				}
+
+				return type;
+			}
+		}
+
+		#endregion
+
+		#region QuerySerializer
+
+		class QuerySerializer : SerializerBase
+		{
+			public string Serialize(SqlQuery query, SqlParameter[] parameters)
+			{
+				var visitor = new QueryVisitor();
+
+				visitor.Visit(query, Visit);
+
+				foreach (var parameter in parameters)
+					if (!Dic.ContainsKey(parameter))
+						Visit(parameter);
+
+				Builder
+					.Append(++Index)
+					.Append(' ')
+					.Append(_paramIndex);
+
+				Append(parameters.Length);
+
+				foreach (var parameter in parameters)
+					Append(parameter);
+
+				Builder.AppendLine();
+
+				return Builder.ToString();
+			}
+
+			void Visit(IQueryElement e)
+			{
+				switch (e.ElementType)
+				{
+					case QueryElementType.SqlField :
+						{
+							var fld = (SqlField)e;
+
+							if (fld != fld.Table.All)
+							{
+								GetType(fld.SystemType);
+
+								if (fld.MemberMapper != null)
+									GetType(fld.MemberMapper.MemberAccessor.TypeAccessor.OriginalType);
+							}
+
+							break;
+						}
+
+					case QueryElementType.SqlParameter :
+						{
+							var p = (SqlParameter)e;
+							var t = p.Value == null ? p.SystemType : p.Value.GetType();
+
+							if (p.Value == null || t.IsArray || t == typeof(string) || !(p.Value is IEnumerable))
+							{
+								GetType(t);
+							}
+							else
+							{
+								var elemType = TypeHelper.GetElementType(t);
+								GetType(GetArrayType(elemType));
+							}
+
+							//if (p.EnumTypes != null)
+							//	foreach (var type in p.EnumTypes)
+							//		GetType(type);
+
+							break;
+						}
+
+					case QueryElementType.SqlFunction         : GetType(((SqlFunction)        e).SystemType); break;
+					case QueryElementType.SqlExpression       : GetType(((SqlExpression)      e).SystemType); break;
+					case QueryElementType.SqlBinaryExpression : GetType(((SqlBinaryExpression)e).SystemType); break;
+					case QueryElementType.SqlDataType         : GetType(((SqlDataType)        e).Type);       break;
+					case QueryElementType.SqlValue            : GetType(((SqlValue)           e).SystemType); break;
+					case QueryElementType.SqlTable            : GetType(((SqlTable)           e).ObjectType); break;
+				}
+
+				Dic.Add(e, ++Index);
+
+				Builder
+					.Append(Index)
+					.Append(' ')
+					.Append((int)e.ElementType);
+
+				switch (e.ElementType)
+				{
+					case QueryElementType.SqlField :
+						{
+							var elem = (SqlField)e;
+
+							Append(elem.SystemType);
+							Append(elem.Name);
+							Append(elem.PhysicalName);
+							Append(elem.Nullable);
+							Append(elem.PrimaryKeyOrder);
+							Append(elem.IsIdentity);
+							Append(elem.IsUpdatable);
+							Append(elem.IsInsertable);
+							Append(elem.MemberMapper == null ? null : elem.MemberMapper.MemberAccessor.TypeAccessor.OriginalType);
+							Append(elem.MemberMapper == null ? null : elem.MemberMapper.Name);
+
+							break;
+						}
+
+					case QueryElementType.SqlFunction :
+						{
+							var elem = (SqlFunction)e;
+
+							Append(elem.SystemType);
+							Append(elem.Name);
+							Append(elem.Precedence);
+							Append(elem.Parameters);
+
+							break;
+						}
+
+					case QueryElementType.SqlParameter :
+						{
+							var elem = (SqlParameter)e;
+
+							Append(elem.Name);
+							Append(elem.IsQueryParameter);
+							Append((int)elem.DbType);
+							Append(elem.DbSize);
+
+							var type = elem.Value == null ? elem.SystemType : elem.Value.GetType();
+
+							if (elem.Value == null || type.IsArray || type == typeof(string) || !(elem.Value is IEnumerable))
+							{
+								Append(type, elem.Value);
+							}
+							else
+							{
+								var elemType = TypeHelper.GetElementType(type);
+								var value    = ConvertIEnumerableToArray(elem.Value, elemType);
+
+								Append(GetArrayType(elemType), value);
+							}
+
+							break;
+						}
+
+					case QueryElementType.SqlExpression :
+						{
+							var elem = (SqlExpression)e;
+
+							Append(elem.SystemType);
+							Append(elem.Expr);
+							Append(elem.Precedence);
+							Append(elem.Parameters);
+
+							break;
+						}
+
+					case QueryElementType.SqlBinaryExpression :
+						{
+							var elem = (SqlBinaryExpression)e;
+
+							Append(elem.SystemType);
+							Append(elem.Expr1);
+							Append(elem.Operation);
+							Append(elem.Expr2);
+							Append(elem.Precedence);
+
+							break;
+						}
+
+					case QueryElementType.SqlValue :
+						{
+							var elem = (SqlValue)e;
+							Append(elem.SystemType, elem.Value);
+							break;
+						}
+
+					case QueryElementType.SqlDataType :
+						{
+							var elem = (SqlDataType)e;
+
+							Append((int)elem.SqlDbType);
+							Append(elem.Type);
+							Append(elem.Length);
+							Append(elem.Precision);
+							Append(elem.Scale);
+
+							break;
+						}
+
+					case QueryElementType.SqlTable :
+						{
+							var elem = (SqlTable)e;
+
+							Append(elem.SourceID);
+							Append(elem.Name);
+							Append(elem.Alias);
+							Append(elem.Database);
+							Append(elem.Owner);
+							Append(elem.PhysicalName);
+							Append(elem.ObjectType);
+
+							if (elem.SequenceAttributes == null)
+								Builder.Append(" -");
+							else
+							{
+								Append(elem.SequenceAttributes.Length);
+
+								foreach (var a in elem.SequenceAttributes)
+								{
+									Append(a.ProviderName);
+									Append(a.SequenceName);
+								}
+							}
+
+							Append(Dic[elem.All]);
+							Append(elem.Fields.Count);
+
+							foreach (var field in elem.Fields)
+								Append(Dic[field.Value]);
+
+							Append((int)elem.SqlTableType);
+
+							if (elem.SqlTableType != SqlTableType.Table)
+							{
+								if (elem.TableArguments == null)
+									Append(0);
+								else
+								{
+									Append(elem.TableArguments.Length);
+
+									foreach (var expr in elem.TableArguments)
+										Append(Dic[expr]);
+								}
+							}
+
+							break;
+						}
+
+					case QueryElementType.ExprPredicate :
+						{
+							var elem = (SqlQuery.Predicate.Expr)e;
+
+							Append(elem.Expr1);
+							Append(elem.Precedence);
+
+							break;
+						}
+
+					case QueryElementType.NotExprPredicate :
+						{
+							var elem = (SqlQuery.Predicate.NotExpr)e;
+
+							Append(elem.Expr1);
+							Append(elem.IsNot);
+							Append(elem.Precedence);
+
+							break;
+						}
+
+					case QueryElementType.ExprExprPredicate :
+						{
+							var elem = (SqlQuery.Predicate.ExprExpr)e;
+
+							Append(elem.Expr1);
+							Append((int)elem.Operator);
+							Append(elem.Expr2);
+
+							break;
+						}
+
+					case QueryElementType.LikePredicate :
+						{
+							var elem = (SqlQuery.Predicate.Like)e;
+
+							Append(elem.Expr1);
+							Append(elem.IsNot);
+							Append(elem.Expr2);
+							Append(elem.Escape);
+
+							break;
+						}
+
+					case QueryElementType.BetweenPredicate :
+						{
+							var elem = (SqlQuery.Predicate.Between)e;
+
+							Append(elem.Expr1);
+							Append(elem.IsNot);
+							Append(elem.Expr2);
+							Append(elem.Expr3);
+
+							break;
+						}
+
+					case QueryElementType.IsNullPredicate :
+						{
+							var elem = (SqlQuery.Predicate.IsNull)e;
+
+							Append(elem.Expr1);
+							Append(elem.IsNot);
+
+							break;
+						}
+
+					case QueryElementType.InSubQueryPredicate :
+						{
+							var elem = (SqlQuery.Predicate.InSubQuery)e;
+
+							Append(elem.Expr1);
+							Append(elem.IsNot);
+							Append(elem.SubQuery);
+
+							break;
+						}
+
+					case QueryElementType.InListPredicate :
+						{
+							var elem = (SqlQuery.Predicate.InList)e;
+
+							Append(elem.Expr1);
+							Append(elem.IsNot);
+							Append(elem.Values);
+
+							break;
+						}
+
+					case QueryElementType.FuncLikePredicate :
+						{
+							var elem = (SqlQuery.Predicate.FuncLike)e;
+							Append(elem.Function);
+							break;
+						}
+
+					case QueryElementType.SqlQuery :
+						{
+							var elem = (SqlQuery)e;
+
+							Append(elem.SourceID);
+							Append((int)elem.QueryType);
+							Append(elem.From);
+
+							var appendInsert = false;
+							var appendUpdate = false;
+							var appendDelete = false;
+							var appendSelect = false;
+
+							switch (elem.QueryType)
+							{
+								case QueryType.InsertOrUpdate :
+									appendUpdate = true;
+									appendInsert = true;
+									break;
+
+								case QueryType.Update         :
+									appendUpdate = true;
+									break;
+
+								case QueryType.Delete         :
+									appendDelete = true;
+									appendSelect = true;
+									break;
+
+								case QueryType.Insert         :
+									appendInsert = true;
+									if (elem.From.Tables.Count != 0)
+										appendSelect = true;
+									break;
+
+								default                       :
+									appendSelect = true;
+									break;
+							}
+
+							Append(appendInsert); if (appendInsert) Append(elem.Insert);
+							Append(appendUpdate); if (appendUpdate) Append(elem.Update);
+							Append(appendDelete); if (appendDelete) Append(elem.Delete);
+							Append(appendSelect); if (appendSelect) Append(elem.Select);
+
+							Append(elem.Where);
+							Append(elem.GroupBy);
+							Append(elem.Having);
+							Append(elem.OrderBy);
+							Append(elem.ParentSql == null ? 0 : elem.ParentSql.SourceID);
+							Append(elem.IsParameterDependent);
+
+							if (!elem.HasUnion)
+								Builder.Append(" -");
+							else
+								Append(elem.Unions);
+
+							Append(elem.Parameters);
+
+							if (Dic.ContainsKey(elem.All))
+								Append(Dic[elem.All]);
+							else
+								Builder.Append(" -");
+
+							break;
+						}
+
+					case QueryElementType.Column :
+						{
+							var elem = (SqlQuery.Column) e;
+
+							Append(elem.Parent.SourceID);
+							Append(elem.Expression);
+							Append(elem._alias);
+
+							break;
+						}
+
+					case QueryElementType.SearchCondition :
+							Append(((SqlQuery.SearchCondition)e).Conditions);
+							break;
+
+					case QueryElementType.Condition :
+						{
+							var elem = (SqlQuery.Condition)e;
+
+							Append(elem.IsNot);
+							Append(elem.Predicate);
+							Append(elem.IsOr);
+
+							break;
+						}
+
+					case QueryElementType.TableSource :
+						{
+							var elem = (SqlQuery.TableSource)e;
+
+							Append(elem.Source);
+							Append(elem._alias);
+							Append(elem.Joins);
+
+							break;
+						}
+
+					case QueryElementType.JoinedTable :
+						{
+							var elem = (SqlQuery.JoinedTable)e;
+
+							Append((int)elem.JoinType);
+							Append(elem.Table);
+							Append(elem.IsWeak);
+							Append(elem.Condition);
+
+							break;
+						}
+
+					case QueryElementType.SelectClause :
+						{
+							var elem = (SqlQuery.SelectClause)e;
+
+							Append(elem.IsDistinct);
+							Append(elem.SkipValue);
+							Append(elem.TakeValue);
+							Append(elem.Columns);
+
+							break;
+						}
+
+					case QueryElementType.InsertClause :
+						{
+							var elem = (SqlQuery.InsertClause)e;
+
+							Append(elem.Items);
+							Append(elem.Into);
+							Append(elem.WithIdentity);
+
+							break;
+						}
+
+					case QueryElementType.UpdateClause :
+						{
+							var elem = (SqlQuery.UpdateClause)e;
+
+							Append(elem.Items);
+							Append(elem.Keys);
+							Append(elem.Table);
+
+							break;
+						}
+
+					case QueryElementType.DeleteClause :
+						{
+							var elem = (SqlQuery.DeleteClause)e;
+							Append(elem.Table);
+							break;
+						}
+
+					case QueryElementType.SetExpression :
+						{
+							var elem = (SqlQuery.SetExpression)e;
+
+							Append(elem.Column);
+							Append(elem.Expression);
+
+							break;
+						}
+
+					case QueryElementType.FromClause    : Append(((SqlQuery.FromClause)   e).Tables);          break;
+					case QueryElementType.WhereClause   : Append(((SqlQuery.WhereClause)  e).SearchCondition); break;
+					case QueryElementType.GroupByClause : Append(((SqlQuery.GroupByClause)e).Items);           break;
+					case QueryElementType.OrderByClause : Append(((SqlQuery.OrderByClause)e).Items);           break;
+
+					case QueryElementType.OrderByItem :
+						{
+							var elem = (SqlQuery.OrderByItem)e;
+
+							Append(elem.Expression);
+							Append(elem.IsDescending);
+
+							break;
+						}
+
+					case QueryElementType.Union :
+						{
+							var elem = (SqlQuery.Union)e;
+
+							Append(elem.SqlQuery);
+							Append(elem.IsAll);
+
+							break;
+						}
+				}
+
+				Builder.AppendLine();
+			}
+
+			void Append<T>(ICollection<T> exprs)
+				where T : IQueryElement
+			{
+				if (exprs == null)
+					Builder.Append(" -");
+				else
+				{
+					Append(exprs.Count);
+
+					foreach (var e in exprs)
+						Append(Dic[e]);
+				}
+			}
+		}
+
+		#endregion
+
+		#region QueryDeserializer
+
+		public class QueryDeserializer : DeserializerBase
+		{
+			SqlQuery       _query;
+			SqlParameter[] _parameters;
+
+			readonly Dictionary<int,SqlQuery> _queries = new Dictionary<int,SqlQuery>();
+			readonly List<Action>             _actions = new List<Action>();
+
+			public LinqServiceQuery Deserialize(string str)
+			{
+				Str = str;
+
+				while (Parse()) {}
+
+				foreach (var action in _actions)
+					action();
+
+				return new LinqServiceQuery { Query = _query, Parameters = _parameters };
+			}
+
+			bool Parse()
+			{
+				NextLine();
+
+				if (Pos >= Str.Length)
+					return false;
+
+				var obj  = null as object;
+				var idx  = ReadInt(); Pos++;
+				var type = ReadInt(); Pos++;
+
+				switch ((QueryElementType)type)
+				{
+					case (QueryElementType)_paramIndex     : obj = _parameters = ReadArray<SqlParameter>(); break;
+					case (QueryElementType)_typeIndex      : obj = ResolveType(ReadString());               break;
+					case (QueryElementType)_typeArrayIndex : obj = GetArrayType(Read<Type>());              break;
+
+					case QueryElementType.SqlField :
+						{
+							var systemType       = Read<Type>();
+							var name             = ReadString();
+							var physicalName     = ReadString();
+							var nullable         = ReadBool();
+							var primaryKeyOrder  = ReadInt();
+							var isIdentity       = ReadBool();
+							var isUpdatable      = ReadBool();
+							var isInsertable     = ReadBool();
+							var memberMapperType = Read<Type>();
+							var memberMapperName = ReadString();
+							var memberMapper     = memberMapperType == null ? null : Map.GetObjectMapper(memberMapperType)[memberMapperName];
+
+							obj = new SqlField(
+								systemType,
+								name,
+								physicalName,
+								nullable,
+								primaryKeyOrder,
+								isIdentity
+									? new DataAccess.IdentityAttribute()
+									: isInsertable || isUpdatable
+										? new DataAccess.NonUpdatableAttribute(isInsertable, isUpdatable, false)
+										: null,
+								memberMapper);
+
+							break;
+						}
+
+					case QueryElementType.SqlFunction :
+						{
+							var systemType = Read<Type>();
+							var name       = ReadString();
+							var precedence = ReadInt();
+							var parameters = ReadArray<ISqlExpression>();
+
+							obj = new SqlFunction(systemType, name, precedence, parameters);
+
+							break;
+						}
+
+					case QueryElementType.SqlParameter :
+						{
+							var name             = ReadString();
+							var isQueryParameter = ReadBool();
+							var dbType           = (DbType)ReadInt();
+							var dbSize           = ReadInt();
+							var systemType       = Read<Type>();
+							var value            = ReadValue(systemType);
+							//var enumTypes        = ReadList<Type>();
+							//var takeValues       = null as List<int>;
+
+							/*
+							var count = ReadCount();
+
+							if (count != null)
+							{
+								takeValues = new List<int>(count.Value);
+
+								for (var i = 0; i < count; i++)
+									takeValues.Add(ReadInt());
+							}
+
+							var likeStart = ReadString();
+							var likeEnd   = ReadString();
+							*/
+
+							obj = new SqlParameter(systemType, name, value, (MappingSchema)null)
+							{
+								IsQueryParameter = isQueryParameter,
+								DbType           = dbType,
+								DbSize           = dbSize,
+								//EnumTypes        = enumTypes,
+								//TakeValues       = takeValues,
+								//LikeStart        = likeStart,
+								//LikeEnd          = likeEnd,
+							};
+
+							/*
+							if (enumTypes != null && UnresolvedTypes.Count > 0)
+								foreach (var et in enumTypes)
+									if (et == null)
+										throw new LinqException(
+											"Query cannot be deserialized. The possible reason is that the deserializer could not resolve the following types: {0}. Use LinqService.TypeResolver to resolve types.",
+											string.Join(", ", UnresolvedTypes.Select(_ => "'" + _ + "'").ToArray()));
+							*/
+
+							break;
+						}
+
+					case QueryElementType.SqlExpression :
+						{
+							var systemType = Read<Type>();
+							var expr       = ReadString();
+							var precedence = ReadInt();
+							var parameters = ReadArray<ISqlExpression>();
+
+							obj = new SqlExpression(systemType, expr, precedence, parameters);
+
+							break;
+						}
+
+					case QueryElementType.SqlBinaryExpression :
+						{
+							var systemType = Read<Type>();
+							var expr1      = Read<ISqlExpression>();
+							var operation  = ReadString();
+							var expr2      = Read<ISqlExpression>();
+							var precedence = ReadInt();
+
+							obj = new SqlBinaryExpression(systemType, expr1, operation, expr2, precedence);
+
+							break;
+						}
+
+					case QueryElementType.SqlValue :
+						{
+							var systemType = Read<Type>();
+							var value      = ReadValue(systemType);
+
+							obj = new SqlValue(systemType, value);
+
+							break;
+						}
+
+					case QueryElementType.SqlDataType :
+						{
+							var dbType     = (SqlDbType)ReadInt();
+							var systemType = Read<Type>();
+							var length     = ReadInt();
+							var precision  = ReadInt();
+							var scale      = ReadInt();
+
+							obj = new SqlDataType(dbType, systemType, length, precision, scale);
+
+							break;
+						}
+
+					case QueryElementType.SqlTable :
+						{
+							var sourceID           = ReadInt();
+							var name               = ReadString();
+							var alias              = ReadString();
+							var database           = ReadString();
+							var owner              = ReadString();
+							var physicalName       = ReadString();
+							var objectType         = Read<Type>();
+							var sequenceAttributes = null as SequenceNameAttribute[];
+
+							var count = ReadCount();
+
+							if (count != null)
+							{
+								sequenceAttributes = new SequenceNameAttribute[count.Value];
+
+								for (var i = 0; i < count.Value; i++)
+									sequenceAttributes[i] = new SequenceNameAttribute(ReadString(), ReadString());
+							}
+
+							var all    = Read<SqlField>();
+							var fields = ReadArray<SqlField>();
+							var flds   = new SqlField[fields.Length + 1];
+
+							flds[0] = all;
+							Array.Copy(fields, 0, flds, 1, fields.Length);
+
+							var sqlTableType = (SqlTableType)ReadInt();
+							var tableArgs    = sqlTableType == SqlTableType.Table ? null : ReadArray<ISqlExpression>();
+
+							obj = new SqlTable(
+								sourceID, name, alias, database, owner, physicalName, objectType, sequenceAttributes, flds,
+								sqlTableType, tableArgs);
+
+							break;
+						}
+
+					case QueryElementType.ExprPredicate :
+						{
+							var expr1      = Read<ISqlExpression>();
+							var precedence = ReadInt();
+
+							obj = new SqlQuery.Predicate.Expr(expr1, precedence);
+
+							break;
+						}
+
+					case QueryElementType.NotExprPredicate :
+						{
+							var expr1      = Read<ISqlExpression>();
+							var isNot      = ReadBool();
+							var precedence = ReadInt();
+
+							obj = new SqlQuery.Predicate.NotExpr(expr1, isNot, precedence);
+
+							break;
+						}
+
+					case QueryElementType.ExprExprPredicate :
+						{
+							var expr1     = Read<ISqlExpression>();
+							var @operator = (SqlQuery.Predicate.Operator)ReadInt();
+							var expr2     = Read<ISqlExpression>();
+
+							obj = new SqlQuery.Predicate.ExprExpr(expr1, @operator, expr2);
+
+							break;
+						}
+
+					case QueryElementType.LikePredicate :
+						{
+							var expr1  = Read<ISqlExpression>();
+							var isNot  = ReadBool();
+							var expr2  = Read<ISqlExpression>();
+							var escape = Read<ISqlExpression>();
+
+							obj = new SqlQuery.Predicate.Like(expr1, isNot, expr2, escape);
+
+							break;
+						}
+
+					case QueryElementType.BetweenPredicate :
+						{
+							var expr1 = Read<ISqlExpression>();
+							var isNot = ReadBool();
+							var expr2 = Read<ISqlExpression>();
+							var expr3 = Read<ISqlExpression>();
+
+							obj = new SqlQuery.Predicate.Between(expr1, isNot, expr2, expr3);
+
+							break;
+						}
+
+					case QueryElementType.IsNullPredicate :
+						{
+							var expr1 = Read<ISqlExpression>();
+							var isNot = ReadBool();
+
+							obj = new SqlQuery.Predicate.IsNull(expr1, isNot);
+
+							break;
+						}
+
+					case QueryElementType.InSubQueryPredicate :
+						{
+							var expr1    = Read<ISqlExpression>();
+							var isNot    = ReadBool();
+							var subQuery = Read<SqlQuery>();
+
+							obj = new SqlQuery.Predicate.InSubQuery(expr1, isNot, subQuery);
+
+							break;
+						}
+
+					case QueryElementType.InListPredicate :
+						{
+							var expr1  = Read<ISqlExpression>();
+							var isNot  = ReadBool();
+							var values = ReadList<ISqlExpression>();
+
+							obj = new SqlQuery.Predicate.InList(expr1, isNot, values);
+
+							break;
+						}
+
+					case QueryElementType.FuncLikePredicate :
+						{
+							var func = Read<SqlFunction>();
+							obj = new SqlQuery.Predicate.FuncLike(func);
+							break;
+						}
+
+					case QueryElementType.SqlQuery :
+						{
+							var sid                = ReadInt();
+							var queryType          = (QueryType)ReadInt();
+							var from               = Read<SqlQuery.FromClause>();
+							var readInsert         = ReadBool();
+							var insert             = readInsert ? Read<SqlQuery.InsertClause>() : null;
+							var readUpdate         = ReadBool();
+							var update             = readUpdate ? Read<SqlQuery.UpdateClause>() : null;
+							var readDelete         = ReadBool();
+							var delete             = readDelete ? Read<SqlQuery.DeleteClause>() : null;
+							var readSelect         = ReadBool();
+							var select             = readSelect ? Read<SqlQuery.SelectClause>() : new SqlQuery.SelectClause(null);
+							var where              = Read<SqlQuery.WhereClause>();
+							var groupBy            = Read<SqlQuery.GroupByClause>();
+							var having             = Read<SqlQuery.WhereClause>();
+							var orderBy            = Read<SqlQuery.OrderByClause>();
+							var parentSql          = ReadInt();
+							var parameterDependent = ReadBool();
+							var unions             = ReadArray<SqlQuery.Union>();
+							var parameters         = ReadArray<SqlParameter>();
+
+							var query = _query = new SqlQuery(sid) { QueryType = queryType };
+
+							query.Init(
+								insert,
+								update,
+								delete,
+								select,
+								from,
+								where,
+								groupBy,
+								having,
+								orderBy,
+								unions == null ? null : unions.ToList(),
+								null,
+								parameterDependent,
+								parameters.ToList());
+
+							_queries.Add(sid, _query);
+
+							if (parentSql != 0)
+								_actions.Add(() =>
+								{
+									SqlQuery sql;
+									if (_queries.TryGetValue(parentSql, out sql))
+										query.ParentSql = sql;
+								});
+
+							query.All = Read<SqlField>();
+
+							obj = query;
+
+							break;
+						}
+
+					case QueryElementType.Column :
+						{
+							var sid        = ReadInt();
+							var expression = Read<ISqlExpression>();
+							var alias      = ReadString();
+
+							var col = new SqlQuery.Column(null, expression, alias);
+
+							_actions.Add(() => col.Parent = _queries[sid]);
+
+							obj = col;
+
+							break;
+						}
+
+					case QueryElementType.SearchCondition :
+						obj = new SqlQuery.SearchCondition(ReadArray<SqlQuery.Condition>());
+						break;
+
+					case QueryElementType.Condition :
+						obj = new SqlQuery.Condition(ReadBool(), Read<ISqlPredicate>(), ReadBool());
+						break;
+
+					case QueryElementType.TableSource :
+						{
+							var source = Read<ISqlTableSource>();
+							var alias  = ReadString();
+							var joins  = ReadArray<SqlQuery.JoinedTable>();
+
+							obj = new SqlQuery.TableSource(source, alias, joins);
+
+							break;
+						}
+
+					case QueryElementType.JoinedTable :
+						{
+							var joinType  = (SqlQuery.JoinType)ReadInt();
+							var table     = Read<SqlQuery.TableSource>();
+							var isWeak    = ReadBool();
+							var condition = Read<SqlQuery.SearchCondition>();
+
+							obj = new SqlQuery.JoinedTable(joinType, table, isWeak, condition);
+
+							break;
+						}
+
+					case QueryElementType.SelectClause :
+						{
+							var isDistinct = ReadBool();
+							var skipValue  = Read<ISqlExpression>();
+							var takeValue  = Read<ISqlExpression>();
+							var columns    = ReadArray<SqlQuery.Column>();
+
+							obj = new SqlQuery.SelectClause(isDistinct, takeValue, skipValue, columns);
+
+							break;
+						}
+
+					case QueryElementType.InsertClause :
+						{
+							var items = ReadArray<SqlQuery.SetExpression>();
+							var into  = Read<SqlTable>();
+							var wid   = ReadBool();
+
+							var c = new SqlQuery.InsertClause { Into = into, WithIdentity = wid };
+
+							c.Items.AddRange(items);
+							obj = c;
+
+							break;
+						}
+
+					case QueryElementType.UpdateClause :
+						{
+							var items = ReadArray<SqlQuery.SetExpression>();
+							var keys  = ReadArray<SqlQuery.SetExpression>();
+							var table = Read<SqlTable>();
+							//var wid   = ReadBool();
+
+							var c = new SqlQuery.UpdateClause { Table = table };
+
+							c.Items.AddRange(items);
+							c.Keys. AddRange(keys);
+							obj = c;
+
+							break;
+						}
+
+					case QueryElementType.DeleteClause :
+						{
+							var table = Read<SqlTable>();
+							obj = new SqlQuery.DeleteClause { Table = table };
+							break;
+						}
+
+					case QueryElementType.SetExpression : obj = new SqlQuery.SetExpression(Read<ISqlExpression>(), Read<ISqlExpression>()); break;
+					case QueryElementType.FromClause    : obj = new SqlQuery.FromClause(ReadArray<SqlQuery.TableSource>());                 break;
+					case QueryElementType.WhereClause   : obj = new SqlQuery.WhereClause(Read<SqlQuery.SearchCondition>());                 break;
+					case QueryElementType.GroupByClause : obj = new SqlQuery.GroupByClause(ReadArray<ISqlExpression>());                    break;
+					case QueryElementType.OrderByClause : obj = new SqlQuery.OrderByClause(ReadArray<SqlQuery.OrderByItem>());              break;
+
+					case QueryElementType.OrderByItem :
+						{
+							var expression   = Read<ISqlExpression>();
+							var isDescending = ReadBool();
+
+							obj = new SqlQuery.OrderByItem(expression, isDescending);
+
+							break;
+						}
+
+					case QueryElementType.Union :
+						{
+							var sqlQuery = Read<SqlQuery>();
+							var isAll    = ReadBool();
+
+							obj = new SqlQuery.Union(sqlQuery, isAll);
+
+							break;
+						}
+				}
+
+				Dic.Add(idx, obj);
+
+				return true;
+			}
+		}
+
+		#endregion
+
+		#region ResultSerializer
+
+		class ResultSerializer : SerializerBase
+		{
+			public string Serialize(LinqServiceResult result)
+			{
+				Append(result.FieldCount);
+				Append(result.VaryingTypes.Length);
+				Append(result.RowCount);
+				Append(result.QueryID.ToString());
+
+				Builder.AppendLine();
+
+				foreach (var name in result.FieldNames)
+				{
+					Append(name);
+					Builder.AppendLine();
+				}
+
+				foreach (var type in result.FieldTypes)
+				{
+					Append(type.FullName);
+					Builder.AppendLine();
+				}
+
+				foreach (var type in result.VaryingTypes)
+				{
+					Append(type.FullName);
+					Builder.AppendLine();
+				}
+
+				foreach (var data in result.Data)
+				{
+					foreach (var str in data)
+					{
+						if (result.VaryingTypes.Length > 0 && !string.IsNullOrEmpty(str) && str[0] == '\0')
+						{
+							Builder.Append('*');
+							Append((int)str[1]);
+							Append(str.Substring(2));
+						}
+						else
+							Append(str);
+					}
+
+					Builder.AppendLine();
+				}
+
+				return Builder.ToString();
+			}
+		}
+
+		#endregion
+
+		#region ResultDeserializer
+
+		class ResultDeserializer : DeserializerBase
+		{
+			public LinqServiceResult DeserializeResult(string str)
+			{
+				Str = str;
+
+				var fieldCount  = ReadInt();
+				var varTypesLen = ReadInt();
+
+				var result = new LinqServiceResult
+				{
+					FieldCount   = fieldCount,
+					RowCount     = ReadInt(),
+					VaryingTypes = new Type[varTypesLen],
+					QueryID      = new Guid(ReadString()),
+					FieldNames   = new string[fieldCount],
+					FieldTypes   = new Type  [fieldCount],
+					Data         = new List<string[]>(),
+				};
+
+				NextLine();
+
+				for (var i = 0; i < fieldCount;  i++) { result.FieldNames  [i] = ReadString();              NextLine(); }
+				for (var i = 0; i < fieldCount;  i++) { result.FieldTypes  [i] = ResolveType(ReadString()); NextLine(); }
+				for (var i = 0; i < varTypesLen; i++) { result.VaryingTypes[i] = ResolveType(ReadString()); NextLine(); }
+
+				for (var n = 0; n < result.RowCount; n++)
+				{
+					var data = new string[fieldCount];
+
+					for (var i = 0; i < fieldCount; i++)
+					{
+						if (varTypesLen > 0)
+						{
+							Get(' ');
+
+							if (Get('*'))
+							{
+								var idx = ReadInt();
+								data[i] = "\0" + (char)idx + ReadString();
+							}
+							else
+								data[i] = ReadString();
+						}
+						else
+							data[i] = ReadString();
+					}
+
+					result.Data.Add(data);
+
+					NextLine();
+				}
+
+				return result;
+			}
+		}
+
+		#endregion
+
+		#region StringArraySerializer
+
+		class StringArraySerializer : SerializerBase
+		{
+			public string Serialize(string[] data)
+			{
+				Append(data.Length);
+
+				foreach (var str in data)
+					Append(str);
+
+				Builder.AppendLine();
+
+				return Builder.ToString();
+			}
+		}
+
+		#endregion
+
+		#region StringArrayDeserializer
+
+		class StringArrayDeserializer : DeserializerBase
+		{
+			public string[] Deserialize(string str)
+			{
+				Str = str;
+
+				var data = new string[ReadInt()];
+
+				for (var i = 0; i < data.Length; i++)
+					data[i] = ReadString();
+
+				return data;
+			}
+		}
+
+		#endregion
+
+		#region Helpers
+
+		interface IArrayHelper
+		{
+			Type   GetArrayType();
+			object ConvertToArray(object list);
+		}
+
+		class ArrayHelper<T> : IArrayHelper
+		{
+			public Type GetArrayType()
+			{
+				return typeof(T[]);
+			}
+
+			public object ConvertToArray(object list)
+			{
+				return ((IEnumerable<T>)list).ToArray();
+			}
+		}
+
+		static readonly Dictionary<Type,Type>                _arrayTypes      = new Dictionary<Type,Type>();
+		static readonly Dictionary<Type,Func<object,object>> _arrayConverters = new Dictionary<Type,Func<object,object>>();
+
+		static Type GetArrayType(Type elementType)
+		{
+			Type arrayType;
+
+			lock (_arrayTypes)
+			{
+				if (!_arrayTypes.TryGetValue(elementType, out arrayType))
+				{
+					var helper = (IArrayHelper)Activator.CreateInstance(typeof(ArrayHelper<>).MakeGenericType(elementType));
+					_arrayTypes.Add(elementType, arrayType = helper.GetArrayType());
+				}
+			}
+
+			return arrayType;
+		}
+
+		static object ConvertIEnumerableToArray(object list, Type elementType)
+		{
+			Func<object,object> converter;
+
+			lock (_arrayConverters)
+			{
+				if (!_arrayConverters.TryGetValue(elementType, out converter))
+				{
+					var helper = (IArrayHelper)Activator.CreateInstance(typeof(ArrayHelper<>).MakeGenericType(elementType));
+					_arrayConverters.Add(elementType, converter = helper.ConvertToArray);
+				}
+			}
+
+			return converter(list);
+		}
+
+		#endregion
+	}
+}