diff --git a/ams/Djy.Common/DapperDBBase.cs b/ams/Djy.Common/DapperDBBase.cs index 6e5b029..9fc1df6 100644 --- a/ams/Djy.Common/DapperDBBase.cs +++ b/ams/Djy.Common/DapperDBBase.cs @@ -6,35 +6,32 @@ using Microsoft.Extensions.Configuration; using System.Collections.Generic; using System.Data; using System.Threading.Tasks; +using Common; +using Microsoft.EntityFrameworkCore; namespace Shared.Repository { /// /// Dapper帮助类 /// - public sealed class DapperDBBase : IDapperDBBase + public class DapperDBBase : DbContext, IDapperDBBase { private string sqlConnection; - private readonly IConfiguration _configuration; - /// - /// - /// - /// - public DapperDBBase(IConfiguration configuration) + + public DapperDBBase() { - _configuration = configuration; - sqlConnection = configuration.GetConnectionString("OpenAuthDBContext"); + sqlConnection = sysOptionConfig.YsWebconfig.DapperDbString; } - /// /// /// /// - public IDbConnection GetConn() + public IDbConnection GetConn(string type = null) { if (string.IsNullOrEmpty(sqlConnection)) - sqlConnection = _configuration.GetConnectionString("OpenAuthDBContext"); + + sqlConnection = sysOptionConfig.YsWebconfig.DapperDbString; IDbConnection conn = new SqlConnection(sqlConnection); conn.Open(); return conn; @@ -470,29 +467,78 @@ namespace Shared.Repository } return iResult; } - //以下为使用方法 - //public async Task GetUserDetailAsync(int Id) - //{ - // string detailSql = @"SELECT ID, UserName, Password, Gender, Birthday, CreateDate, IsDelete FROM [dbo].[Users] WHERE Id=@Id"; - // return await base.Detail(Id, detailSql); - //} - - //public async Task> GetUsersAsync() - //{ - // string selectSql = @"SELECT userName,Password,Gender ,Birthday ,CreateDate ,IsDelete FROM [dbo].[Users]"; - // return await base.Select(selectSql); - //} - - //public async Task PostUserAsync(Users entity) - //{ - // string insertSql = @"INSERT INTO [dbo].[Users](Id, UserName, Password, Gender, Birthday, CreateDate, IsDelete) VALUES(@Id, @UserName, @Password, @Gender, @Birthday, @CreateDate, @IsDelete)"; - // await base.Insert(entity, insertSql); - //} - - //public async Task PutUserAsync(Users entity) - //{ - // string updateSql = "UPDATE [dbo].[Users] SET UserName=@UserName, Password=@Password, Gender=@Gender, Birthday=@Birthday, CreateDate=@CreateDate, IsDelete=@IsDelete WHERE Id=@Id"; - // await base.Update(entity, updateSql); - //} + /// + /// 开启事务 + /// + /// + /// + public IDbTransaction BeginTransaction(IDbConnection conn) + { + IDbTransaction tran = conn.BeginTransaction(); + return tran; + } + + /// + /// 提交事务 + /// + /// + /// + public void Commit(IDbTransaction tran, IDbConnection conn) + { + tran.Commit(); + } + + /// + /// 回滚事务 + /// + /// + /// + public void Rollback(IDbTransaction tran, IDbConnection conn) + { + tran.Rollback(); + } + + + + + /// 数据库连接名 + private static string _connection = string.Empty; + + /// 获取连接名 + private static string Connection + { + get { return _connection; } + set { _connection = value; } + } + + + + /// 定义一个标识确保线程同步 + private static readonly object locker = new object(); + + private static DapperDBBase uniqueInstance; + /// + /// 获取实例,这里为单例模式,保证只存在一个实例 + /// + /// + public static DapperDBBase GetInstance() + { + // 双重锁定实现单例模式,在外层加个判空条件主要是为了减少加锁、释放锁的不必要的损耗 + if (uniqueInstance == null) + { + lock (locker) + { + if (uniqueInstance == null) + { + uniqueInstance = new DapperDBBase(); + } + } + } + return uniqueInstance; + } + + + + } } diff --git a/ams/Djy.Common/Entity/WebConfig.cs b/ams/Djy.Common/Entity/WebConfig.cs index 24b3eda..8667b7e 100644 --- a/ams/Djy.Common/Entity/WebConfig.cs +++ b/ams/Djy.Common/Entity/WebConfig.cs @@ -112,6 +112,9 @@ namespace Common /// public int ShopPower { get; set; } = 2; + + public string DapperDbString { get; set; } + #region jwt认证配置 /// /// jwt发行者 diff --git a/ams/Djy.Common/Interface/DynamicQueryable.cs b/ams/Djy.Common/Interface/DynamicQueryable.cs new file mode 100644 index 0000000..906de94 --- /dev/null +++ b/ams/Djy.Common/Interface/DynamicQueryable.cs @@ -0,0 +1,2342 @@ +//Copyright (C) Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Threading; + +namespace Infrastructure +{ + public static class DynamicQueryable + { + public static IQueryable Where(this IQueryable source, string predicate, params object[] values) + { + return (IQueryable)Where((IQueryable)source, predicate, values); + } + + public static IQueryable Where(this IQueryable source, string predicate, params object[] values) + { + if (source == null) throw new ArgumentNullException("source"); + if (predicate == null) throw new ArgumentNullException("predicate"); + LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Where", + new Type[] { source.ElementType }, + source.Expression, Expression.Quote(lambda))); + } + + public static IQueryable Select(this IQueryable source, string selector, params object[] values) + { + if (source == null) throw new ArgumentNullException("source"); + if (selector == null) throw new ArgumentNullException("selector"); + LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Select", + new Type[] { source.ElementType, lambda.Body.Type }, + source.Expression, Expression.Quote(lambda))); + } + + public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) + { + return (IQueryable)OrderBy((IQueryable)source, ordering, values); + } + + public static IQueryable ThenBy(this IQueryable source, string ordering, params object[] values) + { + return (IQueryable)ThenBy((IQueryable)source, ordering, values); + } + + public static IQueryable ThenBy(this IQueryable source, string ordering, params object[] values) + { + if (source == null) throw new ArgumentNullException("source"); + if (ordering == null) throw new ArgumentNullException("ordering"); + ParameterExpression[] parameters = new ParameterExpression[] { + Expression.Parameter(source.ElementType, "") }; + ExpressionParser parser = new ExpressionParser(parameters, ordering, values); + IEnumerable orderings = parser.ParseOrdering(); + Expression queryExpr = source.Expression; + string methodAsc = "ThenBy"; + string methodDesc = "ThenByDescending"; + foreach (DynamicOrdering o in orderings) + { + queryExpr = Expression.Call( + typeof(Queryable), o.Ascending ? methodAsc : methodDesc, + new Type[] { source.ElementType, o.Selector.Type }, + queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); + + } + return source.Provider.CreateQuery(queryExpr); + } + + public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) + { + if (source == null) throw new ArgumentNullException("source"); + if (ordering == null) throw new ArgumentNullException("ordering"); + ParameterExpression[] parameters = new ParameterExpression[] { + Expression.Parameter(source.ElementType, "") }; + ExpressionParser parser = new ExpressionParser(parameters, ordering, values); + IEnumerable orderings = parser.ParseOrdering(); + Expression queryExpr = source.Expression; + string methodAsc = "OrderBy"; + string methodDesc = "OrderByDescending"; + foreach (DynamicOrdering o in orderings) + { + queryExpr = Expression.Call( + typeof(Queryable), o.Ascending ? methodAsc : methodDesc, + new Type[] { source.ElementType, o.Selector.Type }, + queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); + methodAsc = "ThenBy"; + methodDesc = "ThenByDescending"; + } + return source.Provider.CreateQuery(queryExpr); + } + + public static IQueryable OrderBy(this IQueryable source, string propertyName, bool ascending) + where T : class + { + Type type = typeof(T); + + PropertyInfo property = type.GetProperty(propertyName); + if (property == null) + throw new ArgumentException("propertyName", "Not Exist"); + + ParameterExpression param = Expression.Parameter(type, "p"); + Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property); + LambdaExpression orderByExpression = Expression.Lambda(propertyAccessExpression, param); + + string methodName = ascending ? "OrderBy" : "OrderByDescending"; + + MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, + new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression)); + + return source.Provider.CreateQuery(resultExp); + } + + public static IQueryable Take(this IQueryable source, int count) + { + if (source == null) throw new ArgumentNullException("source"); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Take", + new Type[] { source.ElementType }, + source.Expression, Expression.Constant(count))); + } + + public static IQueryable Skip(this IQueryable source, int count) + { + if (source == null) throw new ArgumentNullException("source"); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "Skip", + new Type[] { source.ElementType }, + source.Expression, Expression.Constant(count))); + } + + public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + if (elementSelector == null) throw new ArgumentNullException("elementSelector"); + LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values); + LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values); + return source.Provider.CreateQuery( + Expression.Call( + typeof(Queryable), "GroupBy", + new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type }, + source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda))); + } + + public static bool Any(this IQueryable source) + { + if (source == null) throw new ArgumentNullException("source"); + return (bool)source.Provider.Execute( + Expression.Call( + typeof(Queryable), "Any", + new Type[] { source.ElementType }, source.Expression)); + } + + public static int Count(this IQueryable source) + { + if (source == null) throw new ArgumentNullException("source"); + return (int)source.Provider.Execute( + Expression.Call( + typeof(Queryable), "Count", + new Type[] { source.ElementType }, source.Expression)); + } + } + + public abstract class DynamicClass + { + public override string ToString() + { + PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); + StringBuilder sb = new StringBuilder(); + sb.Append("{"); + for (int i = 0; i < props.Length; i++) + { + if (i > 0) sb.Append(", "); + sb.Append(props[i].Name); + sb.Append("="); + sb.Append(props[i].GetValue(this, null)); + } + sb.Append("}"); + return sb.ToString(); + } + } + + public class DynamicProperty + { + string name; + Type type; + + public DynamicProperty(string name, Type type) + { + if (name == null) throw new ArgumentNullException("name"); + if (type == null) throw new ArgumentNullException("type"); + this.name = name; + this.type = type; + } + + public string Name + { + get { return name; } + } + + public Type Type + { + get { return type; } + } + } + + public static class DynamicExpression + { + public static Expression Parse(Type resultType, string expression, params object[] values) + { + ExpressionParser parser = new ExpressionParser(null, expression, values); + return parser.Parse(resultType); + } + + public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values) + { + return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, values); + } + + public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) + { + ExpressionParser parser = new ExpressionParser(parameters, expression, values); + return Expression.Lambda(parser.Parse(resultType), parameters); + } + + public static Expression> ParseLambda(string expression, params object[] values) + { + return (Expression>)ParseLambda(typeof(T), typeof(S), expression, values); + } + + public static Type CreateClass(params DynamicProperty[] properties) + { + return ClassFactory.Instance.GetDynamicClass(properties); + } + + public static Type CreateClass(IEnumerable properties) + { + return ClassFactory.Instance.GetDynamicClass(properties); + } + } + + internal class DynamicOrdering + { + public Expression Selector; + public bool Ascending; + } + + internal class Signature : IEquatable + { + public DynamicProperty[] properties; + public int hashCode; + + public Signature(IEnumerable properties) + { + this.properties = properties.ToArray(); + hashCode = 0; + foreach (DynamicProperty p in properties) + { + hashCode ^= p.Name.GetHashCode() ^ p.Type.GetHashCode(); + } + } + + public override int GetHashCode() + { + return hashCode; + } + + public override bool Equals(object obj) + { + return obj is Signature ? Equals((Signature)obj) : false; + } + + public bool Equals(Signature other) + { + if (properties.Length != other.properties.Length) return false; + for (int i = 0; i < properties.Length; i++) + { + if (properties[i].Name != other.properties[i].Name || + properties[i].Type != other.properties[i].Type) return false; + } + return true; + } + } + + internal class ClassFactory + { + public static readonly ClassFactory Instance = new ClassFactory(); + + static ClassFactory() { } // Trigger lazy initialization of static fields + + ModuleBuilder module; + Dictionary classes; + int classCount; + ReaderWriterLock rwLock; + + private ClassFactory() + { + AssemblyName name = new AssemblyName("DynamicClasses"); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); + // AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); +#if ENABLE_LINQ_PARTIAL_TRUST + new ReflectionPermission(PermissionState.Unrestricted).Assert(); +#endif + try + { + module = assembly.DefineDynamicModule("Module"); + } + finally + { +#if ENABLE_LINQ_PARTIAL_TRUST + PermissionSet.RevertAssert(); +#endif + } + classes = new Dictionary(); + rwLock = new ReaderWriterLock(); + } + + public Type GetDynamicClass(IEnumerable properties) + { + rwLock.AcquireReaderLock(Timeout.Infinite); + try + { + Signature signature = new Signature(properties); + Type type; + if (!classes.TryGetValue(signature, out type)) + { + type = CreateDynamicClass(signature.properties); + //fixed by https://gitee.com/DUWENINK + if (!classes.ContainsKey(signature)) + { + classes.Add(signature, type); + } + } + return type; + } + finally + { + rwLock.ReleaseReaderLock(); + } + } + + Type CreateDynamicClass(DynamicProperty[] properties) + { + LockCookie cookie = rwLock.UpgradeToWriterLock(Timeout.Infinite); + try + { + string typeName = "DynamicClass" + (classCount + 1); +#if ENABLE_LINQ_PARTIAL_TRUST + new ReflectionPermission(PermissionState.Unrestricted).Assert(); +#endif + try + { + TypeBuilder tb = this.module.DefineType(typeName, TypeAttributes.Class | + TypeAttributes.Public, typeof(DynamicClass)); + FieldInfo[] fields = GenerateProperties(tb, properties); + GenerateEquals(tb, fields); + GenerateGetHashCode(tb, fields); + Type result = tb.CreateType(); + classCount++; + return result; + } + finally + { +#if ENABLE_LINQ_PARTIAL_TRUST + PermissionSet.RevertAssert(); +#endif + } + } + finally + { + rwLock.DowngradeFromWriterLock(ref cookie); + } + } + + FieldInfo[] GenerateProperties(TypeBuilder tb, DynamicProperty[] properties) + { + FieldInfo[] fields = new FieldBuilder[properties.Length]; + for (int i = 0; i < properties.Length; i++) + { + DynamicProperty dp = properties[i]; + FieldBuilder fb = tb.DefineField("_" + dp.Name, dp.Type, FieldAttributes.Private); + PropertyBuilder pb = tb.DefineProperty(dp.Name, PropertyAttributes.HasDefault, dp.Type, null); + MethodBuilder mbGet = tb.DefineMethod("get_" + dp.Name, + MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, + dp.Type, Type.EmptyTypes); + ILGenerator genGet = mbGet.GetILGenerator(); + genGet.Emit(OpCodes.Ldarg_0); + genGet.Emit(OpCodes.Ldfld, fb); + genGet.Emit(OpCodes.Ret); + MethodBuilder mbSet = tb.DefineMethod("set_" + dp.Name, + MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, + null, new Type[] { dp.Type }); + ILGenerator genSet = mbSet.GetILGenerator(); + genSet.Emit(OpCodes.Ldarg_0); + genSet.Emit(OpCodes.Ldarg_1); + genSet.Emit(OpCodes.Stfld, fb); + genSet.Emit(OpCodes.Ret); + pb.SetGetMethod(mbGet); + pb.SetSetMethod(mbSet); + fields[i] = fb; + } + return fields; + } + + void GenerateEquals(TypeBuilder tb, FieldInfo[] fields) + { + MethodBuilder mb = tb.DefineMethod("Equals", + MethodAttributes.Public | MethodAttributes.ReuseSlot | + MethodAttributes.Virtual | MethodAttributes.HideBySig, + typeof(bool), new Type[] { typeof(object) }); + ILGenerator gen = mb.GetILGenerator(); + LocalBuilder other = gen.DeclareLocal(tb); + Label next = gen.DefineLabel(); + gen.Emit(OpCodes.Ldarg_1); + gen.Emit(OpCodes.Isinst, tb); + gen.Emit(OpCodes.Stloc, other); + gen.Emit(OpCodes.Ldloc, other); + gen.Emit(OpCodes.Brtrue_S, next); + gen.Emit(OpCodes.Ldc_I4_0); + gen.Emit(OpCodes.Ret); + gen.MarkLabel(next); + foreach (FieldInfo field in fields) + { + Type ft = field.FieldType; + Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); + next = gen.DefineLabel(); + gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); + gen.Emit(OpCodes.Ldarg_0); + gen.Emit(OpCodes.Ldfld, field); + gen.Emit(OpCodes.Ldloc, other); + gen.Emit(OpCodes.Ldfld, field); + gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("Equals", new Type[] { ft, ft }), null); + gen.Emit(OpCodes.Brtrue_S, next); + gen.Emit(OpCodes.Ldc_I4_0); + gen.Emit(OpCodes.Ret); + gen.MarkLabel(next); + } + gen.Emit(OpCodes.Ldc_I4_1); + gen.Emit(OpCodes.Ret); + } + + void GenerateGetHashCode(TypeBuilder tb, FieldInfo[] fields) + { + MethodBuilder mb = tb.DefineMethod("GetHashCode", + MethodAttributes.Public | MethodAttributes.ReuseSlot | + MethodAttributes.Virtual | MethodAttributes.HideBySig, + typeof(int), Type.EmptyTypes); + ILGenerator gen = mb.GetILGenerator(); + gen.Emit(OpCodes.Ldc_I4_0); + foreach (FieldInfo field in fields) + { + Type ft = field.FieldType; + Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); + gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); + gen.Emit(OpCodes.Ldarg_0); + gen.Emit(OpCodes.Ldfld, field); + gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("GetHashCode", new Type[] { ft }), null); + gen.Emit(OpCodes.Xor); + } + gen.Emit(OpCodes.Ret); + } + } + + public sealed class ParseException : Exception + { + int position; + + public ParseException(string message, int position) + : base(message) + { + this.position = position; + } + + public int Position + { + get { return position; } + } + + public override string ToString() + { + return string.Format(Res.ParseExceptionFormat, Message, position); + } + } + + internal class ExpressionParser + { + struct Token + { + public TokenId id; + public string text; + public int pos; + } + + enum TokenId + { + Unknown, + End, + Identifier, + StringLiteral, + IntegerLiteral, + RealLiteral, + Exclamation, + Percent, + Amphersand, + OpenParen, + CloseParen, + Asterisk, + Plus, + Comma, + Minus, + Dot, + Slash, + Colon, + LessThan, + Equal, + GreaterThan, + Question, + OpenBracket, + CloseBracket, + Bar, + ExclamationEqual, + DoubleAmphersand, + LessThanEqual, + LessGreater, + DoubleEqual, + GreaterThanEqual, + DoubleBar + } + + interface ILogicalSignatures + { + void F(bool x, bool y); + void F(bool? x, bool? y); + } + + interface IArithmeticSignatures + { + void F(int x, int y); + void F(uint x, uint y); + void F(long x, long y); + void F(ulong x, ulong y); + void F(float x, float y); + void F(double x, double y); + void F(decimal x, decimal y); + void F(int? x, int? y); + void F(uint? x, uint? y); + void F(long? x, long? y); + void F(ulong? x, ulong? y); + void F(float? x, float? y); + void F(double? x, double? y); + void F(decimal? x, decimal? y); + } + + interface IRelationalSignatures : IArithmeticSignatures + { + void F(string x, string y); + void F(char x, char y); + void F(DateTime x, DateTime y); + void F(TimeSpan x, TimeSpan y); + void F(char? x, char? y); + void F(DateTime? x, DateTime? y); + void F(TimeSpan? x, TimeSpan? y); + } + + interface IEqualitySignatures : IRelationalSignatures + { + void F(bool x, bool y); + void F(bool? x, bool? y); + } + + interface IAddSignatures : IArithmeticSignatures + { + void F(DateTime x, TimeSpan y); + void F(TimeSpan x, TimeSpan y); + void F(DateTime? x, TimeSpan? y); + void F(TimeSpan? x, TimeSpan? y); + } + + interface ISubtractSignatures : IAddSignatures + { + void F(DateTime x, DateTime y); + void F(DateTime? x, DateTime? y); + } + + interface INegationSignatures + { + void F(int x); + void F(long x); + void F(float x); + void F(double x); + void F(decimal x); + void F(int? x); + void F(long? x); + void F(float? x); + void F(double? x); + void F(decimal? x); + } + + interface INotSignatures + { + void F(bool x); + void F(bool? x); + } + + interface IEnumerableSignatures + { + void Where(bool predicate); + void Any(); + void Any(bool predicate); + void All(bool predicate); + void Count(); + void Count(bool predicate); + void Min(object selector); + void Max(object selector); + void Sum(int selector); + void Sum(int? selector); + void Sum(long selector); + void Sum(long? selector); + void Sum(float selector); + void Sum(float? selector); + void Sum(double selector); + void Sum(double? selector); + void Sum(decimal selector); + void Sum(decimal? selector); + void Average(int selector); + void Average(int? selector); + void Average(long selector); + void Average(long? selector); + void Average(float selector); + void Average(float? selector); + void Average(double selector); + void Average(double? selector); + void Average(decimal selector); + void Average(decimal? selector); + } + + static readonly Type[] predefinedTypes = { + typeof(Object), + typeof(Boolean), + typeof(Char), + typeof(String), + typeof(SByte), + typeof(Byte), + typeof(Int16), + typeof(UInt16), + typeof(Int32), + typeof(UInt32), + typeof(Int64), + typeof(UInt64), + typeof(Single), + typeof(Double), + typeof(Decimal), + typeof(DateTime), + typeof(TimeSpan), + typeof(Guid), + typeof(Math), + typeof(Convert) + }; + + static readonly Expression trueLiteral = Expression.Constant(true); + static readonly Expression falseLiteral = Expression.Constant(false); + static readonly Expression nullLiteral = Expression.Constant(null); + + static readonly string keywordIt = "it"; + static readonly string keywordIif = "iif"; + static readonly string keywordNew = "new"; + + static Dictionary keywords; + + Dictionary symbols; + IDictionary externals; + Dictionary literals; + ParameterExpression it; + string text; + int textPos; + int textLen; + char ch; + Token token; + + public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) + { + if (expression == null) throw new ArgumentNullException("expression"); + if (keywords == null) keywords = CreateKeywords(); + symbols = new Dictionary(StringComparer.OrdinalIgnoreCase); + literals = new Dictionary(); + if (parameters != null) ProcessParameters(parameters); + if (values != null) ProcessValues(values); + text = expression; + textLen = text.Length; + SetTextPos(0); + NextToken(); + } + + void ProcessParameters(ParameterExpression[] parameters) + { + foreach (ParameterExpression pe in parameters) + if (!string.IsNullOrEmpty(pe.Name)) + AddSymbol(pe.Name, pe); + if (parameters.Length == 1 && string.IsNullOrEmpty(parameters[0].Name)) + it = parameters[0]; + } + + void ProcessValues(object[] values) + { + for (int i = 0; i < values.Length; i++) + { + object value = values[i]; + if (i == values.Length - 1 && value is IDictionary) + { + externals = (IDictionary)value; + } + else + { + AddSymbol("@" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), value); + } + } + } + + void AddSymbol(string name, object value) + { + if (symbols.ContainsKey(name)) + throw ParseError(Res.DuplicateIdentifier, name); + symbols.Add(name, value); + } + + public Expression Parse(Type resultType) + { + int exprPos = token.pos; + Expression expr = ParseExpression(); + if (resultType != null) + if ((expr = PromoteExpression(expr, resultType, true)) == null) + throw ParseError(exprPos, Res.ExpressionTypeMismatch, GetTypeName(resultType)); + ValidateToken(TokenId.End, Res.SyntaxError); + return expr; + } + +#pragma warning disable 0219 + public IEnumerable ParseOrdering() + { + List orderings = new List(); + while (true) + { + Expression expr = ParseExpression(); + bool ascending = true; + if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending")) + { + NextToken(); + } + else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending")) + { + NextToken(); + ascending = false; + } + orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending }); + if (token.id != TokenId.Comma) break; + NextToken(); + } + ValidateToken(TokenId.End, Res.SyntaxError); + return orderings; + } +#pragma warning restore 0219 + + // ?: operator + Expression ParseExpression() + { + int errorPos = token.pos; + Expression expr = ParseLogicalOr(); + if (token.id == TokenId.Question) + { + NextToken(); + Expression expr1 = ParseExpression(); + ValidateToken(TokenId.Colon, Res.ColonExpected); + NextToken(); + Expression expr2 = ParseExpression(); + expr = GenerateConditional(expr, expr1, expr2, errorPos); + } + return expr; + } + + // ||, or operator + Expression ParseLogicalOr() + { + Expression left = ParseLogicalAnd(); + while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or")) + { + Token op = token; + NextToken(); + Expression right = ParseLogicalAnd(); + CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); + left = Expression.OrElse(left, right); + } + return left; + } + + // &&, and operator + Expression ParseLogicalAnd() + { + Expression left = ParseComparison(); + while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and")) + { + Token op = token; + NextToken(); + Expression right = ParseComparison(); + CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); + left = Expression.AndAlso(left, right); + } + return left; + } + + // =, ==, !=, <>, >, >=, <, <= operators + Expression ParseComparison() + { + Expression left = ParseAdditive(); + while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual || + token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater || + token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual || + token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual) + { + Token op = token; + NextToken(); + Expression right = ParseAdditive(); + bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual || + op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater; + if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType) + { + if (left.Type != right.Type) + { + if (left.Type.IsAssignableFrom(right.Type)) + { + right = Expression.Convert(right, left.Type); + } + else if (right.Type.IsAssignableFrom(left.Type)) + { + left = Expression.Convert(left, right.Type); + } + else + { + throw IncompatibleOperandsError(op.text, left, right, op.pos); + } + } + } + else if (IsEnumType(left.Type) || IsEnumType(right.Type)) + { + if (left.Type != right.Type) + { + Expression e; + if ((e = PromoteExpression(right, left.Type, true)) != null) + { + right = e; + } + else if ((e = PromoteExpression(left, right.Type, true)) != null) + { + left = e; + } + else + { + throw IncompatibleOperandsError(op.text, left, right, op.pos); + } + } + } + else + { + CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), + op.text, ref left, ref right, op.pos); + } + switch (op.id) + { + case TokenId.Equal: + case TokenId.DoubleEqual: + left = GenerateEqual(left, right); + break; + case TokenId.ExclamationEqual: + case TokenId.LessGreater: + left = GenerateNotEqual(left, right); + break; + case TokenId.GreaterThan: + left = GenerateGreaterThan(left, right); + break; + case TokenId.GreaterThanEqual: + left = GenerateGreaterThanEqual(left, right); + break; + case TokenId.LessThan: + left = GenerateLessThan(left, right); + break; + case TokenId.LessThanEqual: + left = GenerateLessThanEqual(left, right); + break; + } + } + return left; + } + + // +, -, & operators + Expression ParseAdditive() + { + Expression left = ParseMultiplicative(); + while (token.id == TokenId.Plus || token.id == TokenId.Minus || + token.id == TokenId.Amphersand) + { + Token op = token; + NextToken(); + Expression right = ParseMultiplicative(); + switch (op.id) + { + case TokenId.Plus: + if (left.Type == typeof(string) || right.Type == typeof(string)) + goto case TokenId.Amphersand; + CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos); + left = GenerateAdd(left, right); + break; + case TokenId.Minus: + CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos); + left = GenerateSubtract(left, right); + break; + case TokenId.Amphersand: + left = GenerateStringConcat(left, right); + break; + } + } + return left; + } + + // *, /, %, mod operators + Expression ParseMultiplicative() + { + Expression left = ParseUnary(); + while (token.id == TokenId.Asterisk || token.id == TokenId.Slash || + token.id == TokenId.Percent || TokenIdentifierIs("mod")) + { + Token op = token; + NextToken(); + Expression right = ParseUnary(); + CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos); + switch (op.id) + { + case TokenId.Asterisk: + left = Expression.Multiply(left, right); + break; + case TokenId.Slash: + left = Expression.Divide(left, right); + break; + case TokenId.Percent: + case TokenId.Identifier: + left = Expression.Modulo(left, right); + break; + } + } + return left; + } + + // -, !, not unary operators + Expression ParseUnary() + { + if (token.id == TokenId.Minus || token.id == TokenId.Exclamation || + TokenIdentifierIs("not")) + { + Token op = token; + NextToken(); + if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral || + token.id == TokenId.RealLiteral)) + { + token.text = "-" + token.text; + token.pos = op.pos; + return ParsePrimary(); + } + Expression expr = ParseUnary(); + if (op.id == TokenId.Minus) + { + CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos); + expr = Expression.Negate(expr); + } + else + { + CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos); + expr = Expression.Not(expr); + } + return expr; + } + return ParsePrimary(); + } + + Expression ParsePrimary() + { + Expression expr = ParsePrimaryStart(); + while (true) + { + if (token.id == TokenId.Dot) + { + NextToken(); + expr = ParseMemberAccess(null, expr); + } + else if (token.id == TokenId.OpenBracket) + { + expr = ParseElementAccess(expr); + } + else + { + break; + } + } + return expr; + } + + Expression ParsePrimaryStart() + { + switch (token.id) + { + case TokenId.Identifier: + return ParseIdentifier(); + case TokenId.StringLiteral: + return ParseStringLiteral(); + case TokenId.IntegerLiteral: + return ParseIntegerLiteral(); + case TokenId.RealLiteral: + return ParseRealLiteral(); + case TokenId.OpenParen: + return ParseParenExpression(); + default: + throw ParseError(Res.ExpressionExpected); + } + } + + Expression ParseStringLiteral() + { + ValidateToken(TokenId.StringLiteral); + char quote = token.text[0]; + string s = token.text.Substring(1, token.text.Length - 2); + int start = 0; + while (true) + { + int i = s.IndexOf(quote, start); + if (i < 0) break; + s = s.Remove(i, 1); + start = i + 1; + } + //if (quote == '\'') { + // if (s.Length != 1) + // throw ParseError(Res.InvalidCharacterLiteral); + // NextToken(); + // return CreateLiteral(s[0], s); + //} + NextToken(); + return CreateLiteral(s, s); + } + + Expression ParseIntegerLiteral() + { + ValidateToken(TokenId.IntegerLiteral); + string text = token.text; + if (text[0] != '-') + { + ulong value; + if (!UInt64.TryParse(text, out value)) + throw ParseError(Res.InvalidIntegerLiteral, text); + NextToken(); + if (value <= int.MaxValue) return CreateLiteral((int)value, text); + if (value <= uint.MaxValue) return CreateLiteral((uint)value, text); + if (value <= long.MaxValue) return CreateLiteral((long)value, text); + return CreateLiteral(value, text); + } + else + { + long value; + if (!Int64.TryParse(text, out value)) + throw ParseError(Res.InvalidIntegerLiteral, text); + NextToken(); + if (value >= Int32.MinValue && value <= Int32.MaxValue) + return CreateLiteral((int)value, text); + return CreateLiteral(value, text); + } + } + + Expression ParseRealLiteral() + { + ValidateToken(TokenId.RealLiteral); + string text = token.text; + object value = null; + char last = text[text.Length - 1]; + if (last == 'F' || last == 'f') + { + float f; + if (Single.TryParse(text.Substring(0, text.Length - 1), out f)) value = f; + } + else + { + double d; + if (Double.TryParse(text, out d)) value = d; + } + if (value == null) throw ParseError(Res.InvalidRealLiteral, text); + NextToken(); + return CreateLiteral(value, text); + } + + Expression CreateLiteral(object value, string text) + { + ConstantExpression expr = Expression.Constant(value); + literals.Add(expr, text); + return expr; + } + + Expression ParseParenExpression() + { + ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); + NextToken(); + Expression e = ParseExpression(); + ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected); + NextToken(); + return e; + } + + Expression ParseIdentifier() + { + ValidateToken(TokenId.Identifier); + object value; + if (keywords.TryGetValue(token.text, out value)) + { + if (value is Type) return ParseTypeAccess((Type)value); + if (value == (object)keywordIt) return ParseIt(); + if (value == (object)keywordIif) return ParseIif(); + if (value == (object)keywordNew) return ParseNew(); + NextToken(); + return (Expression)value; + } + if (symbols.TryGetValue(token.text, out value) || + externals != null && externals.TryGetValue(token.text, out value)) + { + Expression expr = value as Expression; + if (expr == null) + { + expr = Expression.Constant(value); + } + else + { + LambdaExpression lambda = expr as LambdaExpression; + if (lambda != null) return ParseLambdaInvocation(lambda); + } + NextToken(); + return expr; + } + if (it != null) return ParseMemberAccess(null, it); + throw ParseError(Res.UnknownIdentifier, token.text); + } + + Expression ParseIt() + { + if (it == null) + throw ParseError(Res.NoItInScope); + NextToken(); + return it; + } + + Expression ParseIif() + { + int errorPos = token.pos; + NextToken(); + Expression[] args = ParseArgumentList(); + if (args.Length != 3) + throw ParseError(errorPos, Res.IifRequiresThreeArgs); + return GenerateConditional(args[0], args[1], args[2], errorPos); + } + + Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos) + { + if (test.Type != typeof(bool)) + throw ParseError(errorPos, Res.FirstExprMustBeBool); + if (expr1.Type != expr2.Type) + { + Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null; + Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null; + if (expr1as2 != null && expr2as1 == null) + { + expr1 = expr1as2; + } + else if (expr2as1 != null && expr1as2 == null) + { + expr2 = expr2as1; + } + else + { + string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null"; + string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null"; + if (expr1as2 != null && expr2as1 != null) + throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2); + throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2); + } + } + return Expression.Condition(test, expr1, expr2); + } + + Expression ParseNew() + { + NextToken(); + ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); + NextToken(); + List properties = new List(); + List expressions = new List(); + while (true) + { + int exprPos = token.pos; + Expression expr = ParseExpression(); + string propName; + if (TokenIdentifierIs("as")) + { + NextToken(); + propName = GetIdentifier(); + NextToken(); + } + else + { + MemberExpression me = expr as MemberExpression; + if (me == null) throw ParseError(exprPos, Res.MissingAsClause); + propName = me.Member.Name; + } + expressions.Add(expr); + properties.Add(new DynamicProperty(propName, expr.Type)); + if (token.id != TokenId.Comma) break; + NextToken(); + } + ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); + NextToken(); + Type type = DynamicExpression.CreateClass(properties); + MemberBinding[] bindings = new MemberBinding[properties.Count]; + for (int i = 0; i < bindings.Length; i++) + bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]); + return Expression.MemberInit(Expression.New(type), bindings); + } + + Expression ParseLambdaInvocation(LambdaExpression lambda) + { + int errorPos = token.pos; + NextToken(); + Expression[] args = ParseArgumentList(); + MethodBase method; + if (FindMethod(lambda.Type, "Invoke", false, args, out method) != 1) + throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda); + return Expression.Invoke(lambda, args); + } + + Expression ParseTypeAccess(Type type) + { + int errorPos = token.pos; + NextToken(); + if (token.id == TokenId.Question) + { + if (!type.IsValueType || IsNullableType(type)) + throw ParseError(errorPos, Res.TypeHasNoNullableForm, GetTypeName(type)); + type = typeof(Nullable<>).MakeGenericType(type); + NextToken(); + } + if (token.id == TokenId.OpenParen) + { + Expression[] args = ParseArgumentList(); + MethodBase method; + switch (FindBestMethod(type.GetConstructors(), args, out method)) + { + case 0: + if (args.Length == 1) + return GenerateConversion(args[0], type, errorPos); + throw ParseError(errorPos, Res.NoMatchingConstructor, GetTypeName(type)); + case 1: + return Expression.New((ConstructorInfo)method, args); + default: + throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, GetTypeName(type)); + } + } + ValidateToken(TokenId.Dot, Res.DotOrOpenParenExpected); + NextToken(); + return ParseMemberAccess(type, null); + } + + Expression GenerateConversion(Expression expr, Type type, int errorPos) + { + Type exprType = expr.Type; + if (exprType == type) return expr; + if (exprType.IsValueType && type.IsValueType) + { + if ((IsNullableType(exprType) || IsNullableType(type)) && + GetNonNullableType(exprType) == GetNonNullableType(type)) + return Expression.Convert(expr, type); + if ((IsNumericType(exprType) || IsEnumType(exprType)) && + (IsNumericType(type)) || IsEnumType(type)) + return Expression.ConvertChecked(expr, type); + } + if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) || + exprType.IsInterface || type.IsInterface) + return Expression.Convert(expr, type); + throw ParseError(errorPos, Res.CannotConvertValue, + GetTypeName(exprType), GetTypeName(type)); + } + + Expression ParseMemberAccess(Type type, Expression instance) + { + if (instance != null) type = instance.Type; + int errorPos = token.pos; + string id = GetIdentifier(); + NextToken(); + if (token.id == TokenId.OpenParen) + { + if (instance != null && type != typeof(string)) + { + Type enumerableType = FindGenericType(typeof(IEnumerable<>), type); + if (enumerableType != null) + { + Type elementType = enumerableType.GetGenericArguments()[0]; + return ParseAggregate(instance, elementType, id, errorPos); + } + } + Expression[] args = ParseArgumentList(); + MethodBase mb; + switch (FindMethod(type, id, instance == null, args, out mb)) + { + case 0: + throw ParseError(errorPos, Res.NoApplicableMethod, + id, GetTypeName(type)); + case 1: + MethodInfo method = (MethodInfo)mb; + if (!IsPredefinedType(method.DeclaringType)) + throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType)); + if (method.ReturnType == typeof(void)) + throw ParseError(errorPos, Res.MethodIsVoid, + id, GetTypeName(method.DeclaringType)); + return Expression.Call(instance, method, args); + default: + throw ParseError(errorPos, Res.AmbiguousMethodInvocation, + id, GetTypeName(type)); + } + } + else + { + MemberInfo member = FindPropertyOrField(type, id, instance == null); + if (member == null) + throw ParseError(errorPos, Res.UnknownPropertyOrField, + id, GetTypeName(type)); + return member is PropertyInfo ? + Expression.Property(instance, (PropertyInfo)member) : + Expression.Field(instance, (FieldInfo)member); + } + } + + static Type FindGenericType(Type generic, Type type) + { + while (type != null && type != typeof(object)) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == generic) return type; + if (generic.IsInterface) + { + foreach (Type intfType in type.GetInterfaces()) + { + Type found = FindGenericType(generic, intfType); + if (found != null) return found; + } + } + type = type.BaseType; + } + return null; + } + + Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos) + { + ParameterExpression outerIt = it; + ParameterExpression innerIt = Expression.Parameter(elementType, ""); + it = innerIt; + Expression[] args = ParseArgumentList(); + it = outerIt; + MethodBase signature; + if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1) + throw ParseError(errorPos, Res.NoApplicableAggregate, methodName); + Type[] typeArgs; + if (signature.Name == "Min" || signature.Name == "Max") + { + typeArgs = new Type[] { elementType, args[0].Type }; + } + else + { + typeArgs = new Type[] { elementType }; + } + if (args.Length == 0) + { + args = new Expression[] { instance }; + } + else + { + args = new Expression[] { instance, Expression.Lambda(args[0], innerIt) }; + } + return Expression.Call(typeof(Enumerable), signature.Name, typeArgs, args); + } + + Expression[] ParseArgumentList() + { + ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); + NextToken(); + Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0]; + ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected); + NextToken(); + return args; + } + + Expression[] ParseArguments() + { + List argList = new List(); + while (true) + { + argList.Add(ParseExpression()); + if (token.id != TokenId.Comma) break; + NextToken(); + } + return argList.ToArray(); + } + + Expression ParseElementAccess(Expression expr) + { + int errorPos = token.pos; + ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected); + NextToken(); + Expression[] args = ParseArguments(); + ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected); + NextToken(); + if (expr.Type.IsArray) + { + if (expr.Type.GetArrayRank() != 1 || args.Length != 1) + throw ParseError(errorPos, Res.CannotIndexMultiDimArray); + Expression index = PromoteExpression(args[0], typeof(int), true); + if (index == null) + throw ParseError(errorPos, Res.InvalidIndex); + return Expression.ArrayIndex(expr, index); + } + else + { + MethodBase mb; + switch (FindIndexer(expr.Type, args, out mb)) + { + case 0: + throw ParseError(errorPos, Res.NoApplicableIndexer, + GetTypeName(expr.Type)); + case 1: + return Expression.Call(expr, (MethodInfo)mb, args); + default: + throw ParseError(errorPos, Res.AmbiguousIndexerInvocation, + GetTypeName(expr.Type)); + } + } + } + + static bool IsPredefinedType(Type type) + { + foreach (Type t in predefinedTypes) if (t == type) return true; + return false; + } + + static bool IsNullableType(Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + static Type GetNonNullableType(Type type) + { + return IsNullableType(type) ? type.GetGenericArguments()[0] : type; + } + + static string GetTypeName(Type type) + { + Type baseType = GetNonNullableType(type); + string s = baseType.Name; + if (type != baseType) s += '?'; + return s; + } + + static bool IsNumericType(Type type) + { + return GetNumericTypeKind(type) != 0; + } + + static bool IsSignedIntegralType(Type type) + { + return GetNumericTypeKind(type) == 2; + } + + static bool IsUnsignedIntegralType(Type type) + { + return GetNumericTypeKind(type) == 3; + } + + static int GetNumericTypeKind(Type type) + { + type = GetNonNullableType(type); + if (type.IsEnum) return 0; + switch (Type.GetTypeCode(type)) + { + case TypeCode.Char: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return 1; + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + return 2; + case TypeCode.Byte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + return 3; + default: + return 0; + } + } + + static bool IsEnumType(Type type) + { + return GetNonNullableType(type).IsEnum; + } + + void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos) + { + Expression[] args = new Expression[] { expr }; + MethodBase method; + if (FindMethod(signatures, "F", false, args, out method) != 1) + throw ParseError(errorPos, Res.IncompatibleOperand, + opName, GetTypeName(args[0].Type)); + expr = args[0]; + } + + void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos) + { + Expression[] args = new Expression[] { left, right }; + MethodBase method; + if (FindMethod(signatures, "F", false, args, out method) != 1) + throw IncompatibleOperandsError(opName, left, right, errorPos); + left = args[0]; + right = args[1]; + } + + Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos) + { + return ParseError(pos, Res.IncompatibleOperands, + opName, GetTypeName(left.Type), GetTypeName(right.Type)); + } + + MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess) + { + BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | + (staticAccess ? BindingFlags.Static : BindingFlags.Instance); + foreach (Type t in SelfAndBaseTypes(type)) + { + MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, + flags, Type.FilterNameIgnoreCase, memberName); + if (members.Length != 0) return members[0]; + } + return null; + } + + int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method) + { + BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | + (staticAccess ? BindingFlags.Static : BindingFlags.Instance); + foreach (Type t in SelfAndBaseTypes(type)) + { + MemberInfo[] members = t.FindMembers(MemberTypes.Method, + flags, Type.FilterNameIgnoreCase, methodName); + int count = FindBestMethod(members.Cast(), args, out method); + if (count != 0) return count; + } + method = null; + return 0; + } + + int FindIndexer(Type type, Expression[] args, out MethodBase method) + { + foreach (Type t in SelfAndBaseTypes(type)) + { + MemberInfo[] members = t.GetDefaultMembers(); + if (members.Length != 0) + { + IEnumerable methods = members. + OfType(). + Select(p => (MethodBase)p.GetGetMethod()). + Where(m => m != null); + int count = FindBestMethod(methods, args, out method); + if (count != 0) return count; + } + } + method = null; + return 0; + } + + static IEnumerable SelfAndBaseTypes(Type type) + { + if (type.IsInterface) + { + List types = new List(); + AddInterface(types, type); + return types; + } + return SelfAndBaseClasses(type); + } + + static IEnumerable SelfAndBaseClasses(Type type) + { + while (type != null) + { + yield return type; + type = type.BaseType; + } + } + + static void AddInterface(List types, Type type) + { + if (!types.Contains(type)) + { + types.Add(type); + foreach (Type t in type.GetInterfaces()) AddInterface(types, t); + } + } + + class MethodData + { + public MethodBase MethodBase; + public ParameterInfo[] Parameters; + public Expression[] Args; + } + + int FindBestMethod(IEnumerable methods, Expression[] args, out MethodBase method) + { + MethodData[] applicable = methods. + Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }). + Where(m => IsApplicable(m, args)). + ToArray(); + if (applicable.Length > 1) + { + applicable = applicable. + Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))). + ToArray(); + } + if (applicable.Length == 1) + { + MethodData md = applicable[0]; + for (int i = 0; i < args.Length; i++) args[i] = md.Args[i]; + method = md.MethodBase; + } + else + { + method = null; + } + return applicable.Length; + } + + bool IsApplicable(MethodData method, Expression[] args) + { + if (method.Parameters.Length != args.Length) return false; + Expression[] promotedArgs = new Expression[args.Length]; + for (int i = 0; i < args.Length; i++) + { + ParameterInfo pi = method.Parameters[i]; + if (pi.IsOut) return false; + Expression promoted = PromoteExpression(args[i], pi.ParameterType, false); + if (promoted == null) return false; + promotedArgs[i] = promoted; + } + method.Args = promotedArgs; + return true; + } + + Expression PromoteExpression(Expression expr, Type type, bool exact) + { + if (expr.Type == type) return expr; + if (expr is ConstantExpression) + { + ConstantExpression ce = (ConstantExpression)expr; + if (ce == nullLiteral) + { + if (!type.IsValueType || IsNullableType(type)) + return Expression.Constant(null, type); + } + else + { + string text; + if (literals.TryGetValue(ce, out text)) + { + Type target = GetNonNullableType(type); + Object value = null; + switch (Type.GetTypeCode(ce.Type)) + { + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + value = ParseNumber(text, target); + break; + case TypeCode.Double: + if (target == typeof(decimal)) value = ParseNumber(text, target); + break; + case TypeCode.String: + value = ParseEnum(text, target); + break; + } + if (value != null) + return Expression.Constant(value, type); + } + } + } + if (IsCompatibleWith(expr.Type, type)) + { + if (type.IsValueType || exact) return Expression.Convert(expr, type); + return expr; + } + return null; + } + + static object ParseNumber(string text, Type type) + { + switch (Type.GetTypeCode(GetNonNullableType(type))) + { + case TypeCode.SByte: + sbyte sb; + if (sbyte.TryParse(text, out sb)) return sb; + break; + case TypeCode.Byte: + byte b; + if (byte.TryParse(text, out b)) return b; + break; + case TypeCode.Int16: + short s; + if (short.TryParse(text, out s)) return s; + break; + case TypeCode.UInt16: + ushort us; + if (ushort.TryParse(text, out us)) return us; + break; + case TypeCode.Int32: + int i; + if (int.TryParse(text, out i)) return i; + break; + case TypeCode.UInt32: + uint ui; + if (uint.TryParse(text, out ui)) return ui; + break; + case TypeCode.Int64: + long l; + if (long.TryParse(text, out l)) return l; + break; + case TypeCode.UInt64: + ulong ul; + if (ulong.TryParse(text, out ul)) return ul; + break; + case TypeCode.Single: + float f; + if (float.TryParse(text, out f)) return f; + break; + case TypeCode.Double: + double d; + if (double.TryParse(text, out d)) return d; + break; + case TypeCode.Decimal: + decimal e; + if (decimal.TryParse(text, out e)) return e; + break; + } + return null; + } + + static object ParseEnum(string name, Type type) + { + if (type.IsEnum) + { + MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field, + BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static, + Type.FilterNameIgnoreCase, name); + if (memberInfos.Length != 0) return ((FieldInfo)memberInfos[0]).GetValue(null); + } + return null; + } + + static bool IsCompatibleWith(Type source, Type target) + { + if (source == target) return true; + if (!target.IsValueType) return target.IsAssignableFrom(source); + Type st = GetNonNullableType(source); + Type tt = GetNonNullableType(target); + if (st != source && tt == target) return false; + TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st); + TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt); + switch (sc) + { + case TypeCode.SByte: + switch (tc) + { + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Byte: + switch (tc) + { + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Int16: + switch (tc) + { + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.UInt16: + switch (tc) + { + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Int32: + switch (tc) + { + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.UInt32: + switch (tc) + { + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Int64: + switch (tc) + { + case TypeCode.Int64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.UInt64: + switch (tc) + { + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return true; + } + break; + case TypeCode.Single: + switch (tc) + { + case TypeCode.Single: + case TypeCode.Double: + return true; + } + break; + default: + if (st == tt) return true; + break; + } + return false; + } + + static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2) + { + bool better = false; + for (int i = 0; i < args.Length; i++) + { + int c = CompareConversions(args[i].Type, + m1.Parameters[i].ParameterType, + m2.Parameters[i].ParameterType); + if (c < 0) return false; + if (c > 0) better = true; + } + return better; + } + + // Return 1 if s -> t1 is a better conversion than s -> t2 + // Return -1 if s -> t2 is a better conversion than s -> t1 + // Return 0 if neither conversion is better + static int CompareConversions(Type s, Type t1, Type t2) + { + if (t1 == t2) return 0; + if (s == t1) return 1; + if (s == t2) return -1; + bool t1t2 = IsCompatibleWith(t1, t2); + bool t2t1 = IsCompatibleWith(t2, t1); + if (t1t2 && !t2t1) return 1; + if (t2t1 && !t1t2) return -1; + if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2)) return 1; + if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1)) return -1; + return 0; + } + + Expression GenerateEqual(Expression left, Expression right) + { + return Expression.Equal(left, right); + } + + Expression GenerateNotEqual(Expression left, Expression right) + { + return Expression.NotEqual(left, right); + } + + Expression GenerateGreaterThan(Expression left, Expression right) + { + if (left.Type == typeof(string)) + { + return Expression.GreaterThan( + GenerateStaticMethodCall("Compare", left, right), + Expression.Constant(0) + ); + } + return Expression.GreaterThan(left, right); + } + + Expression GenerateGreaterThanEqual(Expression left, Expression right) + { + if (left.Type == typeof(string)) + { + return Expression.GreaterThanOrEqual( + GenerateStaticMethodCall("Compare", left, right), + Expression.Constant(0) + ); + } + return Expression.GreaterThanOrEqual(left, right); + } + + Expression GenerateLessThan(Expression left, Expression right) + { + if (left.Type == typeof(string)) + { + return Expression.LessThan( + GenerateStaticMethodCall("Compare", left, right), + Expression.Constant(0) + ); + } + return Expression.LessThan(left, right); + } + + Expression GenerateLessThanEqual(Expression left, Expression right) + { + if (left.Type == typeof(string)) + { + return Expression.LessThanOrEqual( + GenerateStaticMethodCall("Compare", left, right), + Expression.Constant(0) + ); + } + return Expression.LessThanOrEqual(left, right); + } + + Expression GenerateAdd(Expression left, Expression right) + { + if (left.Type == typeof(string) && right.Type == typeof(string)) + { + return GenerateStaticMethodCall("Concat", left, right); + } + return Expression.Add(left, right); + } + + Expression GenerateSubtract(Expression left, Expression right) + { + return Expression.Subtract(left, right); + } + + Expression GenerateStringConcat(Expression left, Expression right) + { + return Expression.Call( + null, + typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }), + new[] { left, right }); + } + + MethodInfo GetStaticMethod(string methodName, Expression left, Expression right) + { + return left.Type.GetMethod(methodName, new[] { left.Type, right.Type }); + } + + Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) + { + return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right }); + } + + void SetTextPos(int pos) + { + textPos = pos; + ch = textPos < textLen ? text[textPos] : '\0'; + } + + void NextChar() + { + if (textPos < textLen) textPos++; + ch = textPos < textLen ? text[textPos] : '\0'; + } + + void NextToken() + { + while (Char.IsWhiteSpace(ch)) NextChar(); + TokenId t; + int tokenPos = textPos; + switch (ch) + { + case '!': + NextChar(); + if (ch == '=') + { + NextChar(); + t = TokenId.ExclamationEqual; + } + else + { + t = TokenId.Exclamation; + } + break; + case '%': + NextChar(); + t = TokenId.Percent; + break; + case '&': + NextChar(); + if (ch == '&') + { + NextChar(); + t = TokenId.DoubleAmphersand; + } + else + { + t = TokenId.Amphersand; + } + break; + case '(': + NextChar(); + t = TokenId.OpenParen; + break; + case ')': + NextChar(); + t = TokenId.CloseParen; + break; + case '*': + NextChar(); + t = TokenId.Asterisk; + break; + case '+': + NextChar(); + t = TokenId.Plus; + break; + case ',': + NextChar(); + t = TokenId.Comma; + break; + case '-': + NextChar(); + t = TokenId.Minus; + break; + case '.': + NextChar(); + t = TokenId.Dot; + break; + case '/': + NextChar(); + t = TokenId.Slash; + break; + case ':': + NextChar(); + t = TokenId.Colon; + break; + case '<': + NextChar(); + if (ch == '=') + { + NextChar(); + t = TokenId.LessThanEqual; + } + else if (ch == '>') + { + NextChar(); + t = TokenId.LessGreater; + } + else + { + t = TokenId.LessThan; + } + break; + case '=': + NextChar(); + if (ch == '=') + { + NextChar(); + t = TokenId.DoubleEqual; + } + else + { + t = TokenId.Equal; + } + break; + case '>': + NextChar(); + if (ch == '=') + { + NextChar(); + t = TokenId.GreaterThanEqual; + } + else + { + t = TokenId.GreaterThan; + } + break; + case '?': + NextChar(); + t = TokenId.Question; + break; + case '[': + NextChar(); + t = TokenId.OpenBracket; + break; + case ']': + NextChar(); + t = TokenId.CloseBracket; + break; + case '|': + NextChar(); + if (ch == '|') + { + NextChar(); + t = TokenId.DoubleBar; + } + else + { + t = TokenId.Bar; + } + break; + case '"': + case '\'': + char quote = ch; + do + { + NextChar(); + while (textPos < textLen && ch != quote) NextChar(); + if (textPos == textLen) + throw ParseError(textPos, Res.UnterminatedStringLiteral); + NextChar(); + } while (ch == quote); + t = TokenId.StringLiteral; + break; + default: + if (Char.IsLetter(ch) || ch == '@' || ch == '_') + { + do + { + NextChar(); + } while (Char.IsLetterOrDigit(ch) || ch == '_'); + t = TokenId.Identifier; + break; + } + if (Char.IsDigit(ch)) + { + t = TokenId.IntegerLiteral; + do + { + NextChar(); + } while (Char.IsDigit(ch)); + if (ch == '.') + { + t = TokenId.RealLiteral; + NextChar(); + ValidateDigit(); + do + { + NextChar(); + } while (Char.IsDigit(ch)); + } + if (ch == 'E' || ch == 'e') + { + t = TokenId.RealLiteral; + NextChar(); + if (ch == '+' || ch == '-') NextChar(); + ValidateDigit(); + do + { + NextChar(); + } while (Char.IsDigit(ch)); + } + if (ch == 'F' || ch == 'f') NextChar(); + break; + } + if (textPos == textLen) + { + t = TokenId.End; + break; + } + throw ParseError(textPos, Res.InvalidCharacter, ch); + } + token.id = t; + token.text = text.Substring(tokenPos, textPos - tokenPos); + token.pos = tokenPos; + } + + bool TokenIdentifierIs(string id) + { + return token.id == TokenId.Identifier && string.Equals(id, token.text, StringComparison.OrdinalIgnoreCase); + } + + string GetIdentifier() + { + ValidateToken(TokenId.Identifier, Res.IdentifierExpected); + string id = token.text; + if (id.Length > 1 && id[0] == '@') id = id.Substring(1); + return id; + } + + void ValidateDigit() + { + if (!Char.IsDigit(ch)) throw ParseError(textPos, Res.DigitExpected); + } + + void ValidateToken(TokenId t, string errorMessage) + { + if (token.id != t) throw ParseError(errorMessage); + } + + void ValidateToken(TokenId t) + { + if (token.id != t) throw ParseError(Res.SyntaxError); + } + + Exception ParseError(string format, params object[] args) + { + return ParseError(token.pos, format, args); + } + + Exception ParseError(int pos, string format, params object[] args) + { + return new ParseException(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args), pos); + } + + static Dictionary CreateKeywords() + { + Dictionary d = new Dictionary(StringComparer.OrdinalIgnoreCase); + d.Add("true", trueLiteral); + d.Add("false", falseLiteral); + d.Add("null", nullLiteral); + d.Add(keywordIt, keywordIt); + d.Add(keywordIif, keywordIif); + d.Add(keywordNew, keywordNew); + foreach (Type type in predefinedTypes) d.Add(type.Name, type); + return d; + } + } + + static class Res + { + public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once"; + public const string ExpressionTypeMismatch = "Expression of type '{0}' expected"; + public const string ExpressionExpected = "Expression expected"; + public const string InvalidCharacterLiteral = "Character literal must contain exactly one character"; + public const string InvalidIntegerLiteral = "Invalid integer literal '{0}'"; + public const string InvalidRealLiteral = "Invalid real literal '{0}'"; + public const string UnknownIdentifier = "Unknown identifier '{0}'"; + public const string NoItInScope = "No 'it' is in scope"; + public const string IifRequiresThreeArgs = "The 'iif' function requires three arguments"; + public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'"; + public const string BothTypesConvertToOther = "Both of the types '{0}' and '{1}' convert to the other"; + public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other"; + public const string MissingAsClause = "Expression is missing an 'as' clause"; + public const string ArgsIncompatibleWithLambda = "Argument list incompatible with lambda expression"; + public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form"; + public const string NoMatchingConstructor = "No matching constructor in type '{0}'"; + public const string AmbiguousConstructorInvocation = "Ambiguous invocation of '{0}' constructor"; + public const string CannotConvertValue = "A value of type '{0}' cannot be converted to type '{1}'"; + public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'"; + public const string MethodsAreInaccessible = "Methods on type '{0}' are not accessible"; + public const string MethodIsVoid = "Method '{0}' in type '{1}' does not return a value"; + public const string AmbiguousMethodInvocation = "Ambiguous invocation of method '{0}' in type '{1}'"; + public const string UnknownPropertyOrField = "No property or field '{0}' exists in type '{1}'"; + public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists"; + public const string CannotIndexMultiDimArray = "Indexing of multi-dimensional arrays is not supported"; + public const string InvalidIndex = "Array index must be an integer expression"; + public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'"; + public const string AmbiguousIndexerInvocation = "Ambiguous invocation of indexer in type '{0}'"; + public const string IncompatibleOperand = "Operator '{0}' incompatible with operand type '{1}'"; + public const string IncompatibleOperands = "Operator '{0}' incompatible with operand types '{1}' and '{2}'"; + public const string UnterminatedStringLiteral = "Unterminated string literal"; + public const string InvalidCharacter = "Syntax error '{0}'"; + public const string DigitExpected = "Digit expected"; + public const string SyntaxError = "Syntax error"; + public const string TokenExpected = "{0} expected"; + public const string ParseExceptionFormat = "{0} (at index {1})"; + public const string ColonExpected = "':' expected"; + public const string OpenParenExpected = "'(' expected"; + public const string CloseParenOrOperatorExpected = "')' or operator expected"; + public const string CloseParenOrCommaExpected = "')' or ',' expected"; + public const string DotOrOpenParenExpected = "'.' or '(' expected"; + public const string OpenBracketExpected = "'[' expected"; + public const string CloseBracketOrCommaExpected = "']' or ',' expected"; + public const string IdentifierExpected = "Identifier expected"; + } +} diff --git a/ams/Djy.Common/Interface/IDapperDBBase.cs b/ams/Djy.Common/Interface/IDapperDBBase.cs index 334e454..d3b0bad 100644 --- a/ams/Djy.Common/Interface/IDapperDBBase.cs +++ b/ams/Djy.Common/Interface/IDapperDBBase.cs @@ -14,7 +14,7 @@ namespace Common.Repository.Interface /// /// /// - IDbConnection GetConn(); + IDbConnection GetConn(string type=null); /// /// 无参数存储过程 /// diff --git a/ams/Djy.Common/Interface/IRepository.cs b/ams/Djy.Common/Interface/IRepository.cs index 3001e31..e79f86d 100644 --- a/ams/Djy.Common/Interface/IRepository.cs +++ b/ams/Djy.Common/Interface/IRepository.cs @@ -12,8 +12,11 @@ // 仓储接口 // *********************************************************************** +using Common.Repository.Core; using Microsoft.EntityFrameworkCore; using System; +using System.Collections.Generic; +using System.Data; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; @@ -89,4 +92,8 @@ namespace Common.Repository.Interface #endregion } + + + + } \ No newline at end of file diff --git a/ams/djy.Model/Ams/AMS_Cntrno.cs b/ams/djy.Model/Ams/AMS_Cntrno.cs index 7301c5b..0bd224e 100644 --- a/ams/djy.Model/Ams/AMS_Cntrno.cs +++ b/ams/djy.Model/Ams/AMS_Cntrno.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace djy.Model.Ams { + [Table("AMS_Cntrno")] public class AMS_Cntrno { /// diff --git a/ams/djy.Model/Ams/AMS_House.cs b/ams/djy.Model/Ams/AMS_House.cs index 7246540..aaada91 100644 --- a/ams/djy.Model/Ams/AMS_House.cs +++ b/ams/djy.Model/Ams/AMS_House.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace djy.Model.Ams { + [Table("AMS_House")] public class AMS_House { /// diff --git a/ams/djy.Model/Ams/AMS_Master.cs b/ams/djy.Model/Ams/AMS_Master.cs index d4bf998..a5b0562 100644 --- a/ams/djy.Model/Ams/AMS_Master.cs +++ b/ams/djy.Model/Ams/AMS_Master.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; namespace djy.Model.Ams { - public class AMSMaster + [Table("AMS_Master")] + public class AMS_Master { /// /// 主键 diff --git a/ams/djy.Model/AmsDto/AMSDto.cs b/ams/djy.Model/AmsDto/AMSDto.cs index 5fed60e..8c43504 100644 --- a/ams/djy.Model/AmsDto/AMSDto.cs +++ b/ams/djy.Model/AmsDto/AMSDto.cs @@ -2,11 +2,9 @@ using System; using System.Collections.Generic; -namespace djy.Model.Ams +namespace AMSDto { - public class AMSDto - { - } + public class AMSSaveDto { diff --git a/ams/djy.Paas.IService/Ams/IAms.cs b/ams/djy.Paas.IService/Ams/IAms.cs deleted file mode 100644 index ae5fdac..0000000 --- a/ams/djy.Paas.IService/Ams/IAms.cs +++ /dev/null @@ -1,17 +0,0 @@ -using djy.Model.Ams; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace djy.IService.Ams -{ - public interface IAms - { - void SaveInfo(AMSSaveDto Dto); - - - - } -} diff --git a/ams/djy.Paas.IService/Ams/IAmsService.cs b/ams/djy.Paas.IService/Ams/IAmsService.cs new file mode 100644 index 0000000..4ebf59e --- /dev/null +++ b/ams/djy.Paas.IService/Ams/IAmsService.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using djy.Model; +using Common; +using AMSDto; + +namespace djy.Paas.IService +{ + /// + /// 账单中心模块 + /// + public interface IAmsService:IsBase + { + /// + ///保存单据 + /// + /// + public void SaveInfo(AMSSaveDto dto); + + + } +} diff --git a/ams/djy.paas.Service/Ams/AmsService.cs b/ams/djy.paas.Service/Ams/AmsService.cs new file mode 100644 index 0000000..70c9459 --- /dev/null +++ b/ams/djy.paas.Service/Ams/AmsService.cs @@ -0,0 +1,43 @@ +using AMSDto; +using djy.Paas.IService; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Dapper; +using System.Data; +using System.Data.SqlClient; +using Common; +using djy.Model.Ams; +using Shared.Repository; + +namespace djy.Service.Ams +{ + /// + /// AMS + /// + public class AmsService : ServBase, IAmsService + { + + DapperDBBase dapper = new DapperDBBase(); + public async void SaveInfo(AMSSaveDto dto) + { + + + AMS_Master master = dto.MapTo(); + + using (var transaction = dapper.BeginTransaction(dapper.GetConn())) + { + master.CreateTime = DateTime.Now; + var gid = await dapper.InsertByEntityAsync(master); + + transaction.Rollback(); + + + + + } + } + } +} diff --git a/ams/djy.paas.Service/Ams/ServiceAms.cs b/ams/djy.paas.Service/Ams/ServiceAms.cs deleted file mode 100644 index 6d588db..0000000 --- a/ams/djy.paas.Service/Ams/ServiceAms.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Common.Repository; -using Common.Repository.Interface; -using djy.IService.Ams; -using djy.Model.Ams; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - - -namespace djy.Service.Ams -{ - public class ServiceAms : IAms - { - private IDapperDBBase dapperDB; - public void SaveInfo(AMSSaveDto Dto) - { - - - - - } - } -} diff --git a/ams/djyweb_djyPaasApi/Controllers/ApiBase.cs b/ams/djy.paas.Service/Djy/ApiBase.cs similarity index 96% rename from ams/djyweb_djyPaasApi/Controllers/ApiBase.cs rename to ams/djy.paas.Service/Djy/ApiBase.cs index 423feed..03f9b76 100644 --- a/ams/djyweb_djyPaasApi/Controllers/ApiBase.cs +++ b/ams/djy.paas.Service/Djy/ApiBase.cs @@ -8,11 +8,9 @@ using System.Reflection; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using djy.Paas.IService; -using Swashbuckle.AspNetCore.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; using djy.Model; -namespace djy_AmsApi.Controllers +namespace djy.Service { /// @@ -177,6 +175,17 @@ namespace djy_AmsApi.Controllers /// protected string GetLoginName { get { return GetClaimsValue("loginname"); } } + /// + /// 获取登录公司名称 + /// + protected string GetLoginComptName { get { return GetClaimsValue("COMNAME"); } } + + + /// + /// 获取登录公司id + /// + protected string GetLoginCompId { get { return GetClaimsValue("CompId"); } } + /// /// 获取登录类型 /// @@ -305,4 +314,7 @@ namespace djy_AmsApi.Controllers } + + + } diff --git a/ams/djy.paas.Service/Djy/AutoMapperExt.cs b/ams/djy.paas.Service/Djy/AutoMapperExt.cs new file mode 100644 index 0000000..0be0a7e --- /dev/null +++ b/ams/djy.paas.Service/Djy/AutoMapperExt.cs @@ -0,0 +1,74 @@ +// *********************************************************************** +// Assembly : FairUtility +// Author : Yubao Li +// Created : 08-27-2015 +// +// Last Modified By : Yubao Li +// Last Modified On : 08-27-2015 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +// *********************************************************************** + +using AutoMapper; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace djy.Service +{ + public static class AutoMapperExt + { + /// + /// 类型映射 + /// + public static T MapTo(this object obj) + { + if (obj == null) return default(T); + + var config = new MapperConfiguration(cfg => cfg.CreateMap(obj.GetType(), typeof(T))); + var mapper = config.CreateMapper(); + return mapper.Map(obj); + } + + /// + /// 集合列表类型映射 + /// + public static List MapToList(this IEnumerable source) + { + Type sourceType = source.GetType().GetGenericArguments()[0]; //获取枚举的成员类型 + var config = new MapperConfiguration(cfg => cfg.CreateMap(sourceType, typeof(TDestination))); + var mapper = config.CreateMapper(); + + return mapper.Map>(source); + } + + /// + /// 集合列表类型映射 + /// + public static List MapToList(this IEnumerable source) + { + var config = new MapperConfiguration(cfg => cfg.CreateMap(typeof(TSource), typeof(TDestination))); + var mapper = config.CreateMapper(); + + return mapper.Map>(source); + } + + /// + /// 类型映射 + /// + public static TDestination MapTo(this TSource source, TDestination destination) + where TSource : class + where TDestination : class + { + if (source == null) return destination; + + var config = new MapperConfiguration(cfg => cfg.CreateMap(typeof(TSource), typeof(TDestination))); + var mapper = config.CreateMapper(); + return mapper.Map(source); + } + + } +} \ No newline at end of file diff --git a/ams/djy.paas.Service/Djy/DbContext.cs b/ams/djy.paas.Service/Djy/DbContext.cs index 9bc9345..65172b1 100644 --- a/ams/djy.paas.Service/Djy/DbContext.cs +++ b/ams/djy.paas.Service/Djy/DbContext.cs @@ -9,6 +9,9 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Common; +using System.Data; +using Shared.Repository; + namespace djy.Service { /// @@ -27,7 +30,7 @@ namespace djy.Service /// /// 账单中心库 /// - public const string BillCenter = "billcenter"; + public const string AMSCenter = "AMS"; /// /// 日志库 /// @@ -44,8 +47,6 @@ namespace djy.Service /// public static IdleBus DbBus=new IdleBus(TimeSpan.FromSeconds(20)); - - //public static IFreeSql DbBus.Get(DbList.djypublicedb); /// /// RedisDB /// @@ -74,7 +75,6 @@ namespace djy.Service else { DbBus.Register(item.SysKey, () => new FreeSqlBuilder().UseConnectionString((DataType)item.DataType, item.ConnString) - // .UseAutoSyncStructure(sysOptionConfig.YsWebconfig.IsDev) .UseAutoSyncStructure(false) .UseNoneCommandParameter(true) .UseMonitorCommand(cmd => @@ -90,17 +90,13 @@ namespace djy.Service return true; } - ///// - ///// 数据库初始化 - ///// - //public static ReturnResult DbInit() - //{ - // var rs = new ReturnResult(); - // DbBus.Get(DbList.djypublicedb).CodeFirst.SyncStructure(); - // DbBus.Get(DbList.djypublicedb).CodeFirst.SyncStructure(); - // rs.OK("生成成功!"); - // return rs; - //} + + + + + + + } } diff --git a/ams/djy.paas.Service/Djy/ServBase.cs b/ams/djy.paas.Service/Djy/ServBase.cs index 0df1f1c..9e4594e 100644 --- a/ams/djy.paas.Service/Djy/ServBase.cs +++ b/ams/djy.paas.Service/Djy/ServBase.cs @@ -122,7 +122,7 @@ namespace djy.Service return count > 0 ? true : false; } - + } diff --git a/ams/djyweb_djyPaasApi/Controllers/AMS/AmsController.cs b/ams/djyweb_djyPaasApi/Controllers/AMS/AmsController.cs index e40e319..78c101e 100644 --- a/ams/djyweb_djyPaasApi/Controllers/AMS/AmsController.cs +++ b/ams/djyweb_djyPaasApi/Controllers/AMS/AmsController.cs @@ -1,24 +1,27 @@ -using Common; -using Common.Repository.Interface; -using djy.Model.Ams; +using AMSDto; +using Common; +using djy.Service; using djy.Service.Ams; - +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; namespace djy_AmsApi.Controllers { - public class AmsController : ApiBase - { - private ServiceAms ams; - + [AllowAnonymous] + public class AmsController : ApiBase + { + + AmsService ser =new AmsService(); + [HttpPost("Add")] public Response Add(AMSSaveDto Dto) { var result = new Response(); try { - ams.Add(Dto); + + ser.SaveInfo(Dto); } catch (Exception ex) { diff --git a/ams/djyweb_djyPaasApi/Startup.cs b/ams/djyweb_djyPaasApi/Startup.cs index 6010a95..d6c0afd 100644 --- a/ams/djyweb_djyPaasApi/Startup.cs +++ b/ams/djyweb_djyPaasApi/Startup.cs @@ -18,6 +18,9 @@ using System.Net.Mime; using AutoMapper; using djy_AmsApi; using djy.Service; +using Common.Repository.Interface; +using Shared.Repository; + namespace djy_AmsApi { @@ -71,7 +74,7 @@ namespace djy_AmsApi } //automapper services.AddAutoMapper(typeof(AutoMapperConfig)); - + // services.AddHangfire(config => { config.UseRedisStorage(sysOptionConfig.YsWebconfig.Redis, new Hangfire.Redis.RedisStorageOptions { Prefix = "hf_"+sysOptionConfig.YsWebconfig.WebName,InvisibilityTimeout=TimeSpan.FromHours(1) }); @@ -98,8 +101,8 @@ namespace djy_AmsApi x.UseDashboard(); }); } - - + + services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "djy_AmsApi", Version = "v1" }); diff --git a/ams/djyweb_djyPaasApi/appsettings.Development.json b/ams/djyweb_djyPaasApi/appsettings.Development.json index 04ac6dc..83130dc 100644 --- a/ams/djyweb_djyPaasApi/appsettings.Development.json +++ b/ams/djyweb_djyPaasApi/appsettings.Development.json @@ -28,13 +28,14 @@ "Rbmq_UserName": "djy_paas", "Rbmq_Password": "123qwe", "Rbmq_Sqlhost": "Data Source =192.168.1.205,1433; Initial Catalog=TestDjyLogs; Persist Security Info=True; User ID =test; Password=test123;pooling=true", + "DapperDbString": "Data Source =123.234.225.158,28000; Initial Catalog=DevAMS; Persist Security Info=True; User ID =dev; Password=dev123;pooling=true;max pool size=2", "DataConnList": [ { "SysKey": "AMS", - "TitleName": "账单数据库", + "TitleName": "AMS", "Index": 100, "DataType": 1, "Status": 0, diff --git a/ams/djyweb_djyPaasApi/appsettings.Prod.json b/ams/djyweb_djyPaasApi/appsettings.Prod.json index 84d9606..1e23902 100644 --- a/ams/djyweb_djyPaasApi/appsettings.Prod.json +++ b/ams/djyweb_djyPaasApi/appsettings.Prod.json @@ -27,17 +27,17 @@ "Rbmq_UserName": "admin", "Rbmq_Password": "admin", "Rbmq_Sqlhost": "Data Source =172.31.85.154; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true", + "DapperDbString": "Data Source =123.234.225.158,28000; Initial Catalog=DevAMS; Persist Security Info=True; User ID =dev; Password=dev123;pooling=true;max pool size=2", "DataConnList": [ { - "SysKey": "billcenter", + "SysKey": "AMS", - "TitleName": "账单数据库", + "TitleName": "AMS", "Index": 100, "DataType": 1, "Status": 0, - "ConnString": "Data Source =172.31.85.154; Initial Catalog=BillCenter; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true;max pool size=2" - + "ConnString": "Data Source =123.234.225.158,28000; Initial Catalog=DevAMS; Persist Security Info=True; User ID =dev; Password=dev123;pooling=true;max pool size=2" }, { "SysKey": "djydb", diff --git a/ams/djyweb_djyPaasApi/appsettings.json b/ams/djyweb_djyPaasApi/appsettings.json index 8f3b4f0..6ef5ecb 100644 --- a/ams/djyweb_djyPaasApi/appsettings.json +++ b/ams/djyweb_djyPaasApi/appsettings.json @@ -27,17 +27,16 @@ "Rbmq_UserName": "admin", "Rbmq_Password": "admin", "Rbmq_Sqlhost": "Data Source =172.31.85.154; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true", + "DapperDbString": "Data Source =123.234.225.158,28000; Initial Catalog=DevAMS; Persist Security Info=True; User ID =dev; Password=dev123;pooling=true;max pool size=2", "DataConnList": [ { - "SysKey": "billcenter", - - "TitleName": "账单数据库", + "SysKey": "AMS", + "TitleName": "AMS", "Index": 100, "DataType": 1, "Status": 0, - "ConnString": "Data Source =172.31.85.154; Initial Catalog=BillCenter; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true;max pool size=2" - + "ConnString": "Data Source =123.234.225.158,28000; Initial Catalog=DevAMS; Persist Security Info=True; User ID =dev; Password=dev123;pooling=true;max pool size=2" }, { "SysKey": "djydb", @@ -46,8 +45,6 @@ "Index": 100, "DataType": 1, "Status": 0, - // "ConnString": "Data Source =172.17.0.3; Initial Catalog=djy_PaasData; Persist Security Info=True; User ID =sa; Password=QDdjy2021*;pooling=true", - //"ConnString": "Data Source =djypaas.myshipping.net,5099; Initial Catalog=djy_PubliceData; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true""ConnString": "Data Source =djypaas.myshipping.net,5099; Initial Catalog=djy_PubliceData; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true" "ConnString": "Data Source =172.31.85.154,1433; Initial Catalog=djy_PubliceData; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true" }, @@ -57,8 +54,6 @@ "Index": 100, "DataType": 1, "Status": 0, - // "ConnString": "Data Source =172.17.0.3; Initial Catalog=djy_logs; Persist Security Info=True; User ID = sa; Password=QDdjy2021*;pooling=true", - //"ConnString": "Data Source =djypaas.myshipping.net,5099; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true" "ConnString": "Data Source =172.31.85.154,1433; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true" }, @@ -69,9 +64,6 @@ "Index": 100, "DataType": 1, "Status": 0, - // "ConnString": "Data Source =172.17.0.3; Initial Catalog=MYSHIPPINGTEST8; Persist Security Info=True; User ID =sa; Password=QDdjy2021*;pooling=true", - //"ConnString": "Data Source =172.31.85.154; Initial Catalog=MYSHIPPINGTEST8; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true", - //"ConnString": "Data Source =djypaas.myshipping.net,5099; Initial Catalog=DsPingTai; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true", "ConnString": "Data Source =172.31.85.154,1433; Initial Catalog=DsPingTai; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true" }