0

出力を厳密に型指定されたリストにマップできる、非常に優れた SqlDataReader ラッパーがあります。

私が今見つけているのは、より多くの列を持つ大規模なデータセットでは、マッピングを最適化できれば、おそらくパフォーマンスが少し向上する可能性があるということです。

そう考えると、特に気になるセクションがあり、一番ヘヴィヒッターらしい。

私が本当に知りたいのは、このループを非同期にする方法があるかどうかです。私はそれがこの獣で世界にすべての違いをもたらすと感じています:)

Mapこれは、私がさらに改善できる場所を誰かが見ることができる場合に備えて、方法全体です...

IList<T> Map<T>

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace o7th.Class.Library.Data
{
public class WrapperTest
{

    public static string Message { set { _Msg = value; } get { return _Msg; } }
    private static string _Msg;

    // Instantiate our caching methods
    internal static Common.CustomCache _Cache = new Common.CustomCache();

    private static IEnumerable<T> Map<T>(SqlDataReader dr) where T : new()
    {
        var enumerableDataReader = dr.Cast<DbDataRecord>().AsEnumerable();
        var tObj = new T();
        PropertyInfo[] propertyInfo = tObj.GetType().GetProperties();
        var batches = enumerableDataReader.Batch(10000);
        var resultCollection = new ConcurrentBag<List<T>>();
        Parallel.ForEach(batches, batch => resultCollection.Add(MapThis<T>(propertyInfo, batch)));
        return resultCollection.SelectMany(m => m.Select(x => x));
    }

    private static List<T> MapThis<T>(PropertyInfo[] propertyInfo, IEnumerable<DbDataRecord> batch) where T : new()
    {
        var list = new List<T>();
        batch.AsParallel().ForAll(record =>
        {
            T obj = new T();
            foreach (PropertyInfo prop in propertyInfo)
            {
                var dbVal = record[prop.Name];
                if (!Equals(dbVal, DBNull.Value))
                {
                    prop.SetValue(obj, dbVal, null);
                }
            }
            list.Add(obj);
        });
        return list;
    }


    public static IEnumerable<T> GetResults<T>(string _Qry, System.Data.CommandType _QryType,
                                        string[] _ParamNames = null,
                                        object[] _ParamVals = null,
                                        System.Data.SqlDbType[] _ParamDTs = null,
                                        bool _ShouldCache = false,
                                        string _CacheID = "") where T : new()
    {
        // Create a reference to a potential already cached IList
        IEnumerable<T> _CachedItem = _Cache.Get<IEnumerable<T>>(_CacheID);
        // If we're already cached, there's no need to fire up the data access objects, so return the cached item instead
        if (_CachedItem != null && _ShouldCache)
        {
            return _CachedItem;
        }
        else
        {
            // Fire up our data access object
            using (Access db = new Access())
            {
                try
                {
                    // create a new ilist reference of our strongly typed class
                    IEnumerable<T> _Query = null;
                    // set the query type
                    db.QueryType = _QryType;
                    // set the query text
                    db.Query = _Qry;
                    // make sure we've got some parameters, if we do the set them to our db access object
                    if (_ParamNames != null)
                    {
                        // set the parameter names
                        db.ParameterNames = _ParamNames;
                        // set the parameter values
                        db.ParameterValues = _ParamVals;
                        // set the parameter data types
                        db.ParameterDataTypes = _ParamDTs;
                    }
                    // start using our db access :)  Fire off the GetResults method and return back a SqlDataReader to work on
                    using (SqlDataReader r = db.GetResults())
                    {
                        // make sure the data reader actually exists and contains some results
                        if (r != null && r.HasRows)
                        {
                            // map the data reader to our strongly type(s)
                            _Query = Map<T>(r);
                        }
                    }
                    // check if we should cache the results
                    if (_ShouldCache)
                    {
                        // if so, set the query object to the cache
                        _Cache.Set<IEnumerable<T>>(_Query, _CacheID);
                    }
                    // return our strongly typed list
                    return _Query;
                }
                catch (Exception ex)
                {
                    // Catch an exception if any, an write it out to our logging mechanism, in addition to adding it our returnable message property
                    _Msg += "Wrapper.GetResults Exception: " + ex.Message + db.Message;
                    ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.Wrapper.GetResults", _Msg);
                    // make sure this method returns a default List
                    return default(IList<T>);
                }
            }
        }
    }
}

public static class Extensions
{
    /// <summary>
    /// Take a collection and split it into smaller collections
    /// </summary>
    /// <typeparam name="T">The Type</typeparam>
    /// <param name="collection">The collection to split</param>
    /// <param name="batchSize">The size of each batch</param>
    /// <returns></returns>
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> collection, int batchSize)
    {
        var nextbatch = new List<T>(batchSize);
        if (collection == null)
        {
            yield break;
        }
        foreach (T item in collection)
        {
            nextbatch.Add(item);
            if (nextbatch.Count != batchSize)
            {
                continue;
            }
            yield return nextbatch;
            nextbatch = new List<T>(batchSize);
        }
        if (nextbatch.Count > 0)
        {
            yield return nextbatch;
        }
    }
}
}

db.GetResults()SqlClient.SqlDataReader を使用した単純な ExecuteReader です

ps これは私の最初の C# プロジェクトです。私は長い間基本/qbasic/vb プログラマーです =)

これが私のTest ConsoleAppです:

テスト

using o7th.Class.Library.Data;
using System;
using System.Collections.Generic;
using System.Threading;

namespace Testing
{
class Program
{
    static void Main(string[] args)
    {
        long startTime = DateTime.Now.Ticks;

        IList<Typing> _T = Wrapper.GetResults<Typing>("List.ZipSearch",
                                                    System.Data.CommandType.StoredProcedure,
                                                    new string[]{"@ZipCode", "@RadiusMile"},
                                                    new object[] { "01020", 10000 },
                                                    new System.Data.SqlDbType[] { System.Data.SqlDbType.VarChar, System.Data.SqlDbType.Float},
                                                    true, "TestCache1");
        long endTime = DateTime.Now.Ticks;
        TimeSpan timeTaken = new TimeSpan(endTime - startTime);
        Console.WriteLine("Task Took: " + timeTaken + " for: " + _T.Count + " records.");

        Thread.Sleep(2000);

        long startTime2 = DateTime.Now.Ticks;
        IEnumerable<Typing> _T2 = WrapperTest.GetResults<Typing>("List.ZipSearch",
                                                    System.Data.CommandType.StoredProcedure,
                                                    new string[] { "@ZipCode", "@RadiusMile" },
                                                    new object[] { "01020", 10000 },
                                                    new System.Data.SqlDbType[] { System.Data.SqlDbType.VarChar, System.Data.SqlDbType.Float },
                                                    true, "TestCache2");

        long endTime2 = DateTime.Now.Ticks;
        TimeSpan timeTaken2 = new TimeSpan(endTime2 - startTime2);
        Console.WriteLine("Task Took: " + timeTaken2 + " for: " + _T2 + " records.");
        Console.WriteLine("");
        Console.WriteLine("Press any key to continue...");

        Console.ReadKey();
    
    }

    partial class Typing {
        public long ZipID { get; set; }
        public string ZipCode { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string County { get; set; }
        public double Mileage { get; set; }
    }

}

}
4

2 に答える 2

0

String.ToUpper()呼び出すたびに、捨てるためだけに新しい文字列を作成していることに気付いていますか? そして、レコードごとに?

を使用していると思いますがHashTable、次のほうがよいかもしれません。

_ht = new Dictionary<string, PropertyInfo>(StringComparer.OrdinalIgnoreCase);

次に、次のように使用できます。

PropertyInfo info = _ht[_Rdr.GetName(i)];

Parallel.ForまたはParallel.ForEach、並列化したい場合は、見たいと思うかもしれません。

しかし、それだけではリフレクションを大量に使用することは避けられません。

しかし、私が本当にすべきだと思うのは、マッパーを構築することです (そして、おそらくそれをキャッシュします)。

IL を出力したくない場合は、式ツリーを使用することをお勧めします。

于 2013-10-20T21:46:03.630 に答える