/// <summary> /// Initializes a new instance of the <see cref="TableEqualityComparer{T}"/> class. /// </summary> /// <param name="typeInfo">The information about the table.</param> public TableEqualityComparer(SqlTypeInfo typeInfo) { InitialHash = typeInfo.FullyQualifiedTableName.GetHashCode() * -1977; StringComparer = typeInfo.Adapter.StringComparer; List <MemberGetter> stringGetters = new List <MemberGetter>(); List <MemberGetter> otherGetters = new List <MemberGetter>(); List <MemberGetter> byteGetters = new List <MemberGetter>(); foreach (SqlColumn column in typeInfo.EqualityColumns) { MemberGetter getter = column.Getter; if (column.Type == typeof(byte[])) { byteGetters.Add(getter); } else if (column.Type == typeof(string) && StringComparer != System.StringComparer.Ordinal) { stringGetters.Add(getter); } else { otherGetters.Add(getter); } } StringGetters = stringGetters.Count == 0 ? Array.Empty <MemberGetter>() : stringGetters.ToArray(); Getters = otherGetters.Count == 0 ? Array.Empty <MemberGetter>() : otherGetters.ToArray(); ByteArrayGetters = byteGetters.Count == 0 ? Array.Empty <MemberGetter>() : byteGetters.ToArray(); }
/// <summary> /// Creates or gets a cached <see cref="SqlBuilder{T}"/>. /// </summary> /// <typeparam name="T">The table type.</typeparam> /// <returns>The <see cref="SqlBuilder{T}"/>.</returns> public static SqlBuilder <T> Builder <T>() where T : class { Type type = typeof(T); if (BuilderCache.TryGetValue(type, out object obj)) { return((SqlBuilder <T>)obj); } SqlTypeInfo typeInfo = new SqlTypeInfo(type, Dialect); SqlBuilder <T> builder = new SqlBuilder <T>(typeInfo); return((SqlBuilder <T>)BuilderCache.GetOrAdd(type, builder)); }
/// <summary> /// Converts a list of objects to a list of CSV rows. /// </summary> /// <typeparam name="T">The type of object.</typeparam> /// <param name="list">A list of objects to convert to a csv.</param> /// <param name="separater">The separater between each value.</param> /// <param name="printColumnNames">Determines if the column names should be printed.</param> /// <returns>A list of CSV rows.</returns> public static IEnumerable <string> ToCsv <T>(IEnumerable <T> list, string separater = ",", bool printColumnNames = true) where T : class { SqlTypeInfo typeInfo = ExtraCrud.TypeInfo <T>(); IReadOnlyList <SqlColumn> columns = typeInfo.AutoKeyColumn == null ? typeInfo.Columns : typeInfo.Columns.Where(c => !c.IsAutoKey).ToArray(); if (columns.Count != 0) { MemberGetter[] getters = new MemberGetter[columns.Count]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < columns.Count; i++) { SqlColumn column = columns[i]; getters[i] = columns[i].Getter; sb.Append('\"').Append(column.ColumnName.Replace("\"", "\"\"")).Append('\"').Append(separater); } sb.Remove(sb.Length - separater.Length, separater.Length); if (printColumnNames) { yield return(sb.ToString()); } foreach (T obj in list) { sb.Clear(); foreach (MemberGetter getter in getters) { object value = getter(obj); if (value == null) { sb.Append("NULL"); } else { sb.Append('\"').Append(value.ToString().Replace("\"", "\"\"")).Append('\"'); } sb.Append(separater); } sb.Remove(sb.Length - separater.Length, separater.Length); yield return(sb.ToString()); } } }
/// <summary> /// Sets the adapter for the queries/builder. This can be a custom adapter. The builder /// for the given type will be purged from the cache if it is not using the given adapter. /// </summary> /// <typeparam name="T">The table type.</typeparam> /// <param name="adapter">The adapter used to generate SQL commands.</param> public static void SetAdapter <T>(ISqlAdapter adapter) where T : class { if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } Type type = typeof(T); SqlBuilder <T> builder; if (BuilderCache.TryGetValue(type, out object obj)) { builder = (SqlBuilder <T>)obj; if (ReferenceEquals(builder.Info.Adapter, adapter)) { return; } } SqlTypeInfo typeInfo = new SqlTypeInfo(type, adapter); builder = new SqlBuilder <T>(typeInfo); BuilderCache.AddOrUpdate(type, builder, (t, old) => builder); }
/// <summary> /// Initializes a new instance of the <see cref="SqlBuilder{T}"/> class. /// </summary> /// <param name="typeInfo">The information about the table.</param> /// <param name="threadSafety">The thread safety level to assign the lazy delegates.</param> public SqlBuilder(SqlTypeInfo typeInfo, LazyThreadSafetyMode threadSafety = LazyThreadSafetyMode.ExecutionAndPublication) { if (typeInfo.Type.IsGenericTypeDefinition && typeInfo.Type.GetGenericTypeDefinition() == typeof(List<>)) throw new InvalidOperationException("List<> is not a valid table type."); if (typeInfo.Type.IsArray) throw new InvalidOperationException("Array<> is not a valid table type."); if (typeInfo.Type == typeof(string)) throw new InvalidOperationException("String is not a valid table type."); Info = typeInfo; BulkStagingTable = Info.Adapter.CreateTempTableName(Info.Type.Name + (Math.Abs(Info.Type.FullName.GetHashCode()) % 99793)); SqlQueries<T> queries = new SqlQueries<T>() { InsertAutoSync = CreateAutoSync(Info.InsertAutoSyncColumns), UpdateAutoSync = CreateAutoSync(typeInfo.UpdateAutoSyncColumns), LazyUpdateObj = new Lazy<DbObjBool<T>>(() => CreateUpdateObj()), LazyBulkDelete = new Lazy<DbListInt<T>>(() => CreateBulkDelete(), threadSafety), LazyBulkGet = new Lazy<DbListList<T>>(() => CreateBulkGet(), threadSafety), LazyBulkInsert = new Lazy<DbListVoid<T>>(() => CreateBulkInsert(), threadSafety), LazyBulkInsertIfNotExists = new Lazy<DbListInt<T>>(() => CreateBulkInsertIfNotExists(), threadSafety), LazyBulkUpdate = new Lazy<DbListInt<T>>(() => CreateBulkUpdate(), threadSafety), LazyBulkUpsert = new Lazy<DbListInt<T>>(() => CreateBulkUpsert(), threadSafety), LazyTruncate = new Lazy<DbVoid>(() => CreateTruncate(), threadSafety), LazyDeleteList = new Lazy<DbWhereInt<T>>(() => CreateDeleteList(), threadSafety), LazyGetDistinct = new Lazy<DbTypeWhereList<T>>(() => CreateGetDistinct(), threadSafety), LazyGetDistinctLimit = new Lazy<DbTypeLimitList<T>>(() => CreateGetDistinctLimit(), threadSafety), LazyGetKeys = new Lazy<DbWhereList<T>>(() => CreateGetKeys(), threadSafety), LazyGetLimit = new Lazy<DbLimitList<T>>(() => CreateGetLimit(), threadSafety), LazyInsertIfNotExists = new Lazy<DbTBool<T>>(() => CreateInsertIfNotExists(), threadSafety), LazyRecordCount = new Lazy<DbWhereInt<T>>(() => CreateRecordCount(), threadSafety), LazyUpsert = new Lazy<DbTBool<T>>(() => CreateUpsert(), threadSafety), LazyGetFilter = new Lazy<DbTypeWhereList<T>>(() => CreateGetFilterList(), threadSafety), LazyGetFilterLimit = new Lazy<DbTypeLimitList<T>>(() => CreateGetFilterLimit(), threadSafety), }; Queries = queries; queries.Insert = CreateInsert(); queries.Update = CreateUpdate(); queries.Delete = CreateDelete(); queries.Get = CreateGet(); queries.GetList = CreateGetList(); DataReaderFactory = new DataReaderFactory(typeof(T), typeInfo.Columns.Where(c => c.Getter != null).Select(c => c.Property)); if (typeInfo.EqualityColumns.Count == 1) { if ((EqualityColumns[0].Type == typeof(string) && Adapter.StringComparer != StringComparer.Ordinal) || EqualityColumns[0].Type == typeof(byte[])) EqualityComparer = new TableEqualityComparer<T>(Info); else EqualityComparer = new TableKeyEqualityComparer<T>(FullyQualifiedTableName, EqualityColumns[0]); Type type = typeInfo.EqualityColumns[0].Type; TypeCode typeCode = Type.GetTypeCode(type); switch (typeCode) { case TypeCode.Int16: Create<short>(threadSafety); break; case TypeCode.Int32: Create<int>(threadSafety); break; case TypeCode.Int64: Create<long>(threadSafety); break; case TypeCode.SByte: Create<sbyte>(threadSafety); break; case TypeCode.Single: Create<float>(threadSafety); break; case TypeCode.String: Create<string>(threadSafety); break; case TypeCode.UInt16: Create<ushort>(threadSafety); break; case TypeCode.Double: Create<double>(threadSafety); break; case TypeCode.UInt32: Create<uint>(threadSafety); break; case TypeCode.UInt64: Create<ulong>(threadSafety); break; case TypeCode.Byte: Create<byte>(threadSafety); break; case TypeCode.Char: Create<char>(threadSafety); break; case TypeCode.DateTime: Create<DateTime>(threadSafety); break; case TypeCode.Decimal: Create<decimal>(threadSafety); break; default: if (type == typeof(Guid)) Create<Guid>(threadSafety); else if (type == typeof(DateTimeOffset)) Create<DateTimeOffset>(threadSafety); else if (type == typeof(TimeSpan)) Create<TimeSpan>(threadSafety); else if (type == typeof(byte[])) Create<byte[]>(threadSafety); else Create("Unsupported SQL key type: " + type, threadSafety); break; } } else { EqualityComparer = new TableEqualityComparer<T>(Info); Create("Composite key does not support this operation", threadSafety); } string paramsSelect = ParamsSelect(Info.SelectColumns); SelectMap.GetOrAdd(typeof(T), paramsSelect); }
/// <summary> /// Creates or gets a cached <see cref="SqlTypeInfo"/>. /// </summary> /// <typeparam name="T">The table type.</typeparam> /// <returns>The <see cref="SqlTypeInfo"/>.</returns> public static SqlTypeInfo TypeInfo <T>() where T : class { SqlTypeInfo typeInfo = Builder <T>().Info; return(typeInfo); }