Advertisement
Guest User

Crappy DAL

a guest
Feb 21st, 2012
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.70 KB | None | 0 0
  1. // Jeffrey Becker Jan 2012
  2. // Released to the public domain, use at your own risk!
  3.  
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Configuration;
  7. using System.Data;
  8. using System.Data.Common;
  9. using System.Linq;
  10. using System.Reflection;
  11. using System.Text.RegularExpressions;
  12.  
  13. namespace CrapyDataAccess
  14. {
  15.  
  16.     public class Db : IDisposable
  17.     {
  18.         private readonly DbConnection _connection;
  19.         private readonly HydraterFactory _hydraterFactory = new HydraterFactory();
  20.         private DbTransaction _transaction;
  21.         private readonly DbProviderFactory _factory;
  22.  
  23.         public Db(string connectionStringName)
  24.         {
  25.             var css = ConfigurationManager.ConnectionStrings[connectionStringName];
  26.             if (css == null)
  27.                 throw new InvalidOperationException(String.Format("No ConnectionString {0} defined.",
  28.                                                                   connectionStringName));
  29.  
  30.             if (String.IsNullOrEmpty(css.ConnectionString))
  31.                 throw new InvalidOperationException(
  32.                     String.Format("The ConnectionString {0} doesn't have a connectionString value defined.",
  33.                                   connectionStringName));
  34.  
  35.             if (String.IsNullOrEmpty(css.ProviderName))
  36.                 throw new InvalidOperationException(
  37.                     String.Format("The ConnectionString {0} doesn't have a providerName defined.", connectionStringName));
  38.  
  39.             _factory = DbProviderFactories.GetFactory(css.ProviderName);
  40.             if(_factory == null)
  41.             _connection = _factory.CreateConnection();
  42.             _connection.ConnectionString = css.ConnectionString;
  43.             _connection.Open();
  44.         }
  45.  
  46.  
  47.         public void Dispose()
  48.         {
  49.             if (_transaction != null)
  50.             {
  51.                 _transaction.Commit();
  52.                 _transaction.Dispose();
  53.             }
  54.             _connection.Dispose();
  55.            
  56.         }
  57.  
  58.         public IEnumerable<T> Execute<T>(string sql, object args = null)
  59.         {
  60.             using (var cmd = CreateCommand(sql, args))
  61.             using (var reader = cmd.ExecuteReader())
  62.             {
  63.                 var hydrater = _hydraterFactory.GetHydrater<T>();
  64.                 while (reader.Read())
  65.                 {
  66.                     yield return hydrater.Hydrate(reader);
  67.                 }
  68.             }
  69.         }
  70.  
  71.         public T Scalar<T>(string sql, object args = null)
  72.         {
  73.             return Execute<T>(sql, args).First();
  74.         }
  75.  
  76.         public void Run(string sql, object args = null)
  77.         {
  78.             using (var cmd = CreateCommand(sql, args))
  79.             {
  80.                 cmd.ExecuteNonQuery();
  81.             }
  82.         }
  83.  
  84.         public void Begin()
  85.         {
  86.             if (_transaction == null)
  87.             {
  88.                 _transaction = _connection.BeginTransaction();
  89.             }
  90.         }
  91.  
  92.         public void Commit()
  93.         {
  94.             if (_transaction != null)
  95.             {
  96.                 _transaction.Commit();
  97.                 _transaction.Dispose();
  98.                 _transaction = null;
  99.             }
  100.         }
  101.  
  102.         public void Rollback()
  103.         {
  104.             if (_transaction != null)
  105.             {
  106.                 _transaction.Rollback();
  107.                 _transaction.Dispose();
  108.                 _transaction = null;
  109.             }
  110.         }
  111.  
  112.         private DbCommand CreateCommand(string sql, object args)
  113.         {
  114.             if (String.IsNullOrEmpty(sql))
  115.                 throw new ArgumentException("Sql must not be null or empty");
  116.             var cmd = _connection.CreateCommand();
  117.             cmd.CommandText = sql;
  118.             if (_transaction != null)
  119.             {
  120.                 cmd.Transaction = _transaction;
  121.             }
  122.             if (args != null)
  123.             {
  124.                 var paramsToAdd = args.GetType()
  125.                     .GetProperties()
  126.                     .Select(p =>
  127.                                 {
  128.                                     var param = cmd.CreateParameter();
  129.                                     param.DbType = TypeConverter.ToDbType(p.PropertyType);
  130.                                     param.ParameterName = "@" + p.Name;
  131.                                     param.Value = p.GetValue(args, null) ?? DBNull.Value;
  132.                                     param.Direction = ParameterDirection.Input;
  133.                                     return param;
  134.                                 });
  135.                 foreach (var p in paramsToAdd)
  136.                 {
  137.                     if (Regex.IsMatch(sql, "\\b" + p.ParameterName + "\\b",
  138.                                       RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
  139.                     {
  140.                         cmd.Parameters.Add(p);
  141.                     }
  142.                 }
  143.             }
  144.             return cmd;
  145.         }
  146.  
  147.         #region Nested type: ColumnInfo
  148.  
  149.         private class ColumnInfo
  150.         {
  151.             private readonly bool _isNullable;
  152.             private readonly Type _parentType;
  153.             private readonly PropertyInfo _propertyInfo;
  154.  
  155.             public ColumnInfo(Type parentType, PropertyInfo propertyInfo)
  156.             {
  157.                 _parentType = parentType;
  158.                 _propertyInfo = propertyInfo;
  159.                 Name = _propertyInfo.Name;
  160.                 _isNullable = TypeConverter.IsNullable(_propertyInfo.PropertyType);
  161.             }
  162.  
  163.             public string Name { get; protected set; }
  164.  
  165.             public void AssignValue(object o, object instance)
  166.             {
  167.                 try
  168.                 {
  169.                     if (_isNullable && o == DBNull.Value)
  170.                         o = null;
  171.  
  172.                     _propertyInfo.SetValue(instance, o, null);
  173.                 }
  174.                 catch (ArgumentException ex)
  175.                 {
  176.                     throw new InvalidOperationException(
  177.                         String.Format("Error hydrating {0}.{1}: \"{2}\"", _parentType.Name, Name, ex.Message), ex);
  178.                 }
  179.             }
  180.         }
  181.  
  182.         #endregion
  183.  
  184.         #region Nested type: EntityHydrater
  185.  
  186.         private class EntityHydrater<T> : IHydrater<T>
  187.         {
  188.             private readonly EntityInfo _entityInfo;
  189.             private IDictionary<int, ColumnInfo> _lookup;
  190.  
  191.             public EntityHydrater(EntityInfo entityInfo)
  192.             {
  193.                 _entityInfo = entityInfo;
  194.             }
  195.  
  196.             #region IHydrater<T> Members
  197.  
  198.             public T Hydrate(IDataRecord dr)
  199.             {
  200.                 _lookup = _lookup ?? _entityInfo.BuildLookup(dr);
  201.                 object instance = Activator.CreateInstance(typeof (T));
  202.                 for (int i = 0; i < dr.FieldCount; i++)
  203.                 {
  204.                     if (_lookup.ContainsKey(i))
  205.                     {
  206.                         _lookup[i].AssignValue(dr.GetValue(i), instance);
  207.                     }
  208.                 }
  209.                 return (T) instance;
  210.             }
  211.  
  212.             #endregion
  213.         }
  214.  
  215.         #endregion
  216.  
  217.         #region Nested type: EntityInfo
  218.  
  219.         private class EntityInfo
  220.         {
  221.             private readonly ICollection<ColumnInfo> _columns;
  222.             private readonly Type _type;
  223.  
  224.             public EntityInfo(Type type)
  225.             {
  226.                 _type = type;
  227.                 bool hasParameterlessConstructor = _type.GetConstructors()
  228.                     .Any(c => !c.GetParameters().Any());
  229.  
  230.                 if (!hasParameterlessConstructor)
  231.                     throw new InvalidOperationException(
  232.                         String.Format("{0} does not have a public parameterless constructor.", _type.Name));
  233.  
  234.                 _columns = _type.GetProperties()
  235.                     .Where(p => p.CanWrite)
  236.                     .Select(p => new ColumnInfo(_type, p))
  237.                     .ToList();
  238.             }
  239.  
  240.             public object CreateInstance()
  241.             {
  242.                 return Activator.CreateInstance(_type);
  243.             }
  244.  
  245.             public IDictionary<int, ColumnInfo> BuildLookup(IDataRecord dr)
  246.             {
  247.                 return
  248.                     Enumerable.Range(0, dr.FieldCount)
  249.                         .Select(i =>
  250.                                 new
  251.                                     {
  252.                                         Index = i,
  253.                                         Column =
  254.                                     _columns.FirstOrDefault(c => string.Compare(c.Name, dr.GetName(i), true) == 0)
  255.                                     })
  256.                         .Where(x => x.Column != null)
  257.                         .ToDictionary(x => x.Index, x => x.Column);
  258.             }
  259.         }
  260.  
  261.         #endregion
  262.  
  263.         #region Nested type: HydraterFactory
  264.  
  265.         private class HydraterFactory
  266.         {
  267.             private readonly Dictionary<Type, EntityInfo> _entityInfos = new Dictionary<Type, EntityInfo>();
  268.  
  269.             public IHydrater<T> GetHydrater<T>()
  270.             {
  271.                 Type type = typeof (T);
  272.  
  273.                 if (TypeConverter.IsScalarType(type))
  274.                     return new ScalarHydrater<T>();
  275.                 if (!_entityInfos.ContainsKey(type))
  276.                 {
  277.                     _entityInfos.Add(type, new EntityInfo(type));
  278.                 }
  279.                 return new EntityHydrater<T>(_entityInfos[type]);
  280.             }
  281.         }
  282.  
  283.         #endregion
  284.  
  285.         #region Nested type: IHydrater
  286.  
  287.         private interface IHydrater<T>
  288.         {
  289.             T Hydrate(IDataRecord dr);
  290.         }
  291.  
  292.         #endregion
  293.  
  294.         #region Nested type: ScalarHydrater
  295.  
  296.         private class ScalarHydrater<T> : IHydrater<T>
  297.         {
  298.             private readonly bool _isNullable;
  299.             private readonly Type _type;
  300.  
  301.             public ScalarHydrater()
  302.             {
  303.                 Type type = typeof (T);
  304.                 _isNullable = TypeConverter.IsNullable(type);
  305.                 _type = _isNullable ? TypeConverter.GetNonNullable(type) : type;
  306.             }
  307.  
  308.             #region IHydrater<T> Members
  309.  
  310.             public T Hydrate(IDataRecord dr)
  311.             {
  312.                 if (dr.FieldCount == 0)
  313.                     throw new InvalidOperationException("No fields were returned");
  314.                 object obj = dr.GetValue(0);
  315.                 if (obj == DBNull.Value)
  316.                 {
  317.                     if (!_isNullable)
  318.                     {
  319.                         throw new InvalidOperationException(String.Format("The Column {0} is NULL", dr.GetName(0)));
  320.                     }
  321.                     else
  322.                         return (T) (object) null;
  323.                 }
  324.                 else
  325.                     return (T) Convert.ChangeType(obj, _type);
  326.             }
  327.  
  328.             #endregion
  329.         }
  330.  
  331.         #endregion
  332.  
  333.         #region Nested type: TypeConverter
  334.  
  335.         private static class TypeConverter
  336.         {
  337.             private static readonly Dictionary<Type, DbType> TypeToDbType
  338.                = new Dictionary<Type, DbType>
  339.                 {
  340.                     {typeof (string), DbType.String}, {typeof (DateTime), DbType.DateTime}, {typeof (DateTime?), DbType.DateTime },
  341.                     {typeof (int), DbType.Int32}, {typeof (int?), DbType.Int32}, {typeof (long), DbType.Int64},
  342.                     {typeof (long?), DbType.Int64}, {typeof (bool), DbType.Boolean}, {typeof (bool?), DbType.Boolean},
  343.                     {typeof (byte[]), DbType.Binary}, {typeof (decimal), DbType.Decimal}, {typeof (decimal?), DbType.Decimal},
  344.                     {typeof (double), DbType.Double}, {typeof (double?), DbType.Double}, {typeof (float), DbType.Single},
  345.                     {typeof (float?), DbType.Single}, {typeof (Guid), DbType.Guid}, {typeof (Guid?), DbType.Guid}
  346.                 };
  347.  
  348.             public static bool IsScalarType(Type type)
  349.             {
  350.                 return TypeToDbType.ContainsKey(type);
  351.             }
  352.  
  353.             public static bool IsNullable(Type type)
  354.             {
  355.                 return !type.IsValueType ||
  356.                        (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>));
  357.             }
  358.  
  359.             public static Type GetNonNullable(Type type)
  360.             {
  361.                 return type.GetGenericArguments()[0];
  362.             }
  363.  
  364.             public static DbType ToDbType(Type type)
  365.             {
  366.                 if (!TypeToDbType.ContainsKey(type))
  367.                 {
  368.                     throw new InvalidOperationException(
  369.                         string.Format("Type {0} doesn't have a matching DbType configured", type.FullName));
  370.                 }
  371.  
  372.                 return TypeToDbType[type];
  373.             }
  374.         }
  375.  
  376.         #endregion
  377.     }
  378. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement