//==============================================================
//  版权所有：深圳杰文科技
//  文件名：DbContextExt.cs
//  版本：V1.0  
//  创建者：Jay  ( QQ： 85363208 )
//  创建时间：2017-11-28 16:18
//  创建描述：
//  修改者：
//  修改时间：
//  修改说明： 
//==============================================================

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
using System.Data.Entity.Validation;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Infrastructure.Log;
using Infrastructure.TaskQueue;
using Infrastructure.Utilities;

namespace System.Data.Entity
{
	//[DebuggerStepThrough]
	public static class DbContextExt
	{
		private static Dictionary<string, string> _SqlDict = new Dictionary<string, string>();

		public static bool WriteSqlLog = false;

		public static void InitDB<DB>() where DB : DbContext, new()
		{
			using (DB db = new DB())
			{
				//var objectContext = ((IObjectContextAdapter)db).ObjectContext;
				//var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
				//mappingCollection.GenerateViews(new List<EdmSchemaError>());
			}
		}

		#region - Query -

		public static TEntity GetById<TEntity>(this DbContext db, Guid id) where TEntity : class, new()
		{
			return GetById<TEntity>(db, id.ToString());
		}

		public static TEntity GetById<TEntity>(this DbContext db, long id) where TEntity : class, new()
		{
			Type entityType = typeof(TEntity);
			string keyFieldName = GetKeyFieldName(entityType);
			return GetByWhere<TEntity>(db, " and {0} = {1}".Fmt(keyFieldName, id));
		}

		public static TEntity GetById<TEntity>(this DbContext db, int id) where TEntity : class, new()
		{
			return GetById<TEntity>(db, (long)id);
		}

		public static TEntity GetById<TEntity>(this DbContext db, string id) where TEntity : class, new()
		{
			Type entityType = typeof(TEntity);
			string keyFieldName = GetKeyFieldName(entityType);
			return GetByWhere<TEntity>(db, " and {0} = '{1}'".Fmt(keyFieldName, id));
		}

		private static TEntity GetByWhere<TEntity>(this DbContext db, string where) where TEntity : class, new()
		{
			string sql = ParseSQL<TEntity>();
			sql = sql.Fmt(where);

			using (DbDataReader reader = Read(db, sql))
			{
				List<TEntity> list = DataTableHelper.Mapping<TEntity>(reader);
				reader.Close();

				TEntity entity = list.FirstOrDefault();
				return entity;
			}
		}

		public static TEntity Get<TEntity>(this DbContext db, params object[] args) where TEntity : class, new()
		{
			string sql = ParseSQL<TEntity>();
			List<TEntity> entities = SqlQuery<TEntity>(db, sql, args);
			return entities.FirstOrDefault();
		}

		public static TEntity SqlGet<TEntity>(this DbContext db, string sql, params object[] args) where TEntity : class, new()
		{
			List<TEntity> entities = SqlQuery<TEntity>(db, sql, args);
			return entities.FirstOrDefault();
		}

		public static List<TEntity> Query<TEntity>(this DbContext db, params object[] args) where TEntity : class, new()
		{
			string sql = ParseSQL<TEntity>();
			return SqlQuery<TEntity>(db, sql, args);
		}

		public static List<TEntity> PagingQuery<TEntity>(this DbContext db, string pagingOderBy, int recordNumber, int pageNumber, params object[] args) where TEntity : class, new()
		{
			if (pageNumber == 0)
			{
				pageNumber = 1;
			}
			string pagingTemplate = @"select top {1} * from ({0}
) t where rownum > {1} * ({2} - 1) and rownum <= {1} * {2} order by rownum";
			string sql = ParseSQL<TEntity>();
			sql = sql.Fmt(args).Replace("select", "select row_number()over(order by {0}) rownum,").Fmt(pagingOderBy);
			sql = pagingTemplate.Fmt(sql, recordNumber, pageNumber);
			return SqlQuery<TEntity>(db, sql);
		}

		public static int Count<TEntity>(this DbContext db, string where) where TEntity : class,new()
		{
			string sql = ParseSQL<TEntity>();
			int fromPos = sql.ToLower().IndexOf("from");
			sql = sql.Substring(fromPos);
			sql = "select count(1) cnt " + sql;

			int orderbyPos = sql.IndexOf("order by");
			if (orderbyPos > -1)
			{
				sql = sql.Substring(0, orderbyPos);
			}

			int count = ExecuteScalar<int>(db, sql, where);
			return count;
		}

		private static string ParseSQL<TEntity>() where TEntity : class, new()
		{
			Type entityType = typeof(TEntity);
			if (_SqlDict.ContainsKey(entityType.FullName))
			{
				return _SqlDict[entityType.FullName];
			}

			FieldInfo fi = entityType.GetField("SQL");
			if (fi == null)
			{
				throw new ServiceException("{0}未定义SQL", entityType.Name);
			}

			string sql = (string)fi.GetValue(new TEntity());
			_SqlDict.Add(entityType.FullName, sql);
			return sql;
		}

		public static List<TEntity> SqlQuery<TEntity>(this DbContext db, string sql, params object[] args) where TEntity : class, new()
		{
			using (DbDataReader reader = Read(db, sql, args))
			{
				List<TEntity> list = DataTableHelper.Mapping<TEntity>(reader);
				reader.Close();
				return list;
			}
		}

		//返回简单类型的列表，例如int、string、decimal等。
		public static List<T> SimpleQuery<T>(this DbContext db, string sql, params object[] args) where T : class
		{
			List<T> list = new List<T>();
			using (DbDataReader reader = Read(db, sql, args))
			{
				while (reader.Read())
				{
					T value = (T)reader[0];
					list.Add(value);
				}
				reader.Close();
			}
			return list;
		}

		//返回第一行第一个字段的值。
		public static T ExecuteScalar<T>(this DbContext db, string sql, params object[] args)
		{
			using (DbDataReader reader = Read(db, sql, args))
			{
				if (reader.Read())
				{
					if (!reader.IsDBNull(0))
					{
						return (T)reader[0];
					}
				}
			}
			return default(T);
		}

		private static DbDataReader Read(this DbContext db, string sql, params object[] args)
		{
			//db.Configuration.ProxyCreationEnabled = false; //不用生成代理，DTO传输到前台不需要代理。
			db.Database.Connection.Close();
			db.Database.Connection.Open();
			DbCommand cmd = db.Database.Connection.CreateCommand();

			if (args.Length == 0)
			{
				cmd.CommandText = string.Format(sql, "");
			}
			else
			{
				cmd.CommandText = string.Format(sql, args);
			}

			if (WriteSqlLog)
			{
				SystemLogger.Instance.WriteSql("", cmd.CommandText);
			}
			else
			{
				Debug.WriteLine(cmd.CommandText);
			}

			DbDataReader reader = null;
			try
			{
				reader = cmd.ExecuteReader();
			}
			catch (Exception ex)
			{
				throw new Exception(GetRealException(ex).Message);
			}
			return reader;
		}

		#endregion

		public static DataTable SqlQueryData(this DbContext db, string sql, params object[] args)
		{
			if (args != null && args.Length > 0)
			{
				sql = sql.Fmt(args);
			}

			db.Database.Connection.Close();
			db.Database.Connection.Open();

			SqlCommand cmd = new SqlCommand();
			cmd.Connection = (SqlConnection)db.Database.Connection;
			cmd.CommandText = sql;

			SqlDataAdapter adapter = new SqlDataAdapter(cmd);
			DataTable table = new DataTable();
			adapter.Fill(table);
			return table;
		}

		#region - Execute & Save -

		public static int Execute(this DbContext db, string sql)
		{
			SystemLogger.Instance.WriteSql("", sql);
			return db.Database.ExecuteSqlCommand(sql);
		}

		public static int Save(this DbContext db)
		{
			if (db == null)
			{
				return -1;
			}

			//数据日志功能是正常的，但db.Track()的执行时间太长，导致保存时速度很慢。
			//IDB idb = db as IDB;
			//if (idb != null)
			//{
			//	if (idb.CurrentUser != null)
			//	{
			//		SystemLogger.Instance.Write("Save AuditedLog 1 ---------> " + DateTime.Now);
			//		List<AuditedLog> logList = db.Track();
			//		SystemLogger.Instance.Write("Save AuditedLog 2 ---------> " + DateTime.Now);
			//		_Task.Append(logList); //把数据日志放到异步队列任务中保存，避免保存日志影响效率。
			//		SystemLogger.Instance.Write("Save AuditedLog 3 ---------> " + DateTime.Now);
			//	}
			//}

			try
			{
				int cnt = db.SaveChanges();
				return cnt;
			}
			catch (DbEntityValidationException ex)
			{
				string msg = "";
				List<DbValidationError> errors = ex.EntityValidationErrors.First().ValidationErrors.ToList();
				foreach (DbValidationError e in errors)
				{
					if (msg != "")
					{
						msg = msg + "\r\n";
					}
					msg = msg + e.ErrorMessage;
				}
				throw new ServiceException(msg);
			}
			catch (DbUpdateException ex)
			{
				throw new ServiceException(GetRealException(ex).Message);
			}
			catch (Exception ex)
			{
				throw new ServiceException(GetRealException(ex).Message);
			}
		}

		private static Exception GetRealException(Exception ex)
		{
			for (int i = 0; i < 5; i++)
			{
				if (ex.InnerException == null)
				{
					return ex;
				}
				ex = ex.InnerException;
			}
			return ex;
		}

		#endregion

		#region - AddRage -

		public static void AddRang<T>(this ICollection<T> collection, ICollection<T> list)
		{
			foreach (T item in list)
			{
				collection.Add(item);
			}
		}

		public static void AddRang<T>(this DbSet<T> dbset, IEnumerable<T> list) where T : class
		{
			foreach (T item in list)
			{
				dbset.Add(item);
			}
		}

		#endregion

		#region - Delete -

		//这个方法有Bug，EF会先装载表的所有记录，导致内存溢出。
		//public static void Delete<T>(this DbSet<T> dbset, Func<T, bool> where) where T : class
		//{
		//	var r = dbset.Where<T>(where);
		//	foreach (var item in r)
		//	{
		//		dbset.Remove(item);
		//	}
		//}

		public static void RemoveById<T>(this DbSet<T> dbset, Guid id) where T : class
		{
			var r = dbset.Find(id);
			if (r != null)
			{
				dbset.Remove(r);
			}
		}

		#endregion

		#region - Update -

		public static void Update<T>(this DbSet<T> dbset, T vo) where T : class, new()
		{
			dbset.AddOrUpdate(vo);
		}

		public static T Update<T>(this DbSet<T> dbset, T vo, Expression<Func<T, object>> keySelector) where T : class, new()
		{
			if (vo == null)
			{
				throw new ServiceException("{0}不能为空", typeof(T).Name);
			}

			PropertyInfo keyPI = ReflectHelper.GetProperty<T>(keySelector);
			object id = keyPI.GetValue(vo, null);
			T po = dbset.Find(id);
			if (po == null)
			{
				po = ObjectMapper.Map<T>(vo);
				dbset.Add(po);
			}
			else
			{
				ObjectMapper.Map<T>(vo, po);
			}
			return po;
		}

		public static void Update<T>(this DbSet<T> dbset, IEnumerable<T> voList, IEnumerable<T> poList, Expression<Func<T, object>> keySelector) where T : class, new()
		{
			if (voList == null)
			{
				voList = new List<T>();
			}

			if (poList == null)
			{
				poList = new List<T>();
			}

			PropertyInfo keyPI = ReflectHelper.GetProperty<T>(keySelector);

			foreach (T vo in voList)
			{
				if (vo == null)
				{
					throw new ServiceException("VO不能为空");
				}

				object idValue = keyPI.GetValue(vo, null);

				T po = ObjectMapper.Find<T>(poList, keyPI.Name, idValue);
				if (po != null) //如果数据库中已存在，那么更新（用VO覆盖PO）；
				{
					ObjectMapper.Map<T>(vo, po);
				}
				else //否则就是新增。
				{
					if (poList is IList<T>)
					{
						(poList as IList<T>).Add(vo);
					}
					else
					{
						dbset.Add(vo);
					}
				}
			}

			foreach (T po in poList.ToList())
			{
				object idValue = keyPI.GetValue(po, null);
				T vo = ObjectMapper.Find<T>(voList, keyPI.Name, idValue);
				if (vo == null) //如果数据库中存在，但VO中不存在，说明此item需要删除。
				{
					if (poList is IList<T>)
					{
						(poList as IList<T>).Remove(po); //从集合里删除。
					}
					dbset.Remove(po); //从数据库里删除。
				}
			}
		}

		#endregion

		#region - Helper -

		private static string GetKeyFieldName(Type entityType)
		{
			foreach (PropertyInfo pi in ReflectHelper.GetPropertyList(entityType))
			{
				foreach (object attribute in pi.GetCustomAttributes(true))
				{
					if (attribute is KeyAttribute)
					{
						return pi.Name;
					}
				}
			}
			return null;
		}

		#endregion
	}
}
