<commit_before>using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using Biggy.Extensions;

namespace Biggy
{
  /// <summary>
  /// A class that wraps your database table in Dynamic Funtime
  /// </summary>
  public abstract class DBTable<T> where T: new() {
    
    protected string ConnectionString;

    /// <summary>
    /// Returns an Open Connection
    /// </summary>
    internal abstract DbConnection OpenConnection();
    protected abstract string BuildSelect(string where, string orderBy = "", int limit = 0);
    protected abstract string GetSingleSelect(string where);
    public abstract string GetInsertReturnValueSQL();
    protected abstract string DbDelimiterFormatString { get; }
    protected abstract bool columnIsAutoIncrementing(string columnName);


    public virtual string DelimitedTableName {
      get {
        return string.Format(this.DbDelimiterFormatString, this.TableName);
      }
    }

    public virtual DbColumnMapping PrimaryKeyMapping { get; set; }
    public virtual string TableName { get; set; }
    public string DescriptorField { get; protected set; }
    public virtual DbColumnMappingLookup PropertyColumnMappings { get; private set; }

    protected void mapDbColumns() {
      this.PropertyColumnMappings = new DbColumnMappingLookup(this.DbDelimiterFormatString);
      var columnNames = this.getTableColumns();
      if(columnNames.Count > 0) {
        var item = new T();
        var itemType = item.GetType();
        var props = itemType.GetProperties();
        string replaceString = "[^a-zA-Z1-9]";
        var rgx = new Regex(replaceString);

        // First map all the columns. If an explicit attribute mapping exists, use it. Otherwise, math 'em up:
        foreach (var property in props) {
          string propertyName = rgx.Replace(property.Name.ToLower(), "");
          string columnName = columnNames.FirstOrDefault(c => rgx.Replace(c.ToLower(), "") == propertyName);

          if (columnName != null) {
            // Just map it:
            var newMapping = this.PropertyColumnMappings.Add(columnName, property.Name);
            newMapping.DataType = property.GetType();
          } else {
            // Look for a custom column name attribute:
            DbColumnAttribute mappedColumnAttribute = null;
            var attribute = property.GetCustomAttributes(false).FirstOrDefault(a => a.GetType() == typeof(DbColumnAttribute));
            if (attribute != null) {
              // Use the column name found in the attribute:
              mappedColumnAttribute = attribute as DbColumnAttribute;
              columnName = mappedColumnAttribute.Name;
            }
            var newMapping = this.PropertyColumnMappings.Add(columnName, property.Name);
            newMapping.DataType = property.GetType();
          }
        }

        // Now, find the PK  column. If one is explicitly set with an attribute, use that:
        var pkProperty = props.FirstOrDefault(p => p.GetCustomAttributes(false).Any(a => a.GetType() == typeof(PrimaryKeyAttribute)));
        PrimaryKeyAttribute pkAttribute = null;
        if (pkProperty != null) {
          pkAttribute = pkProperty.GetCustomAttributes(false).FirstOrDefault(a => a.GetType() == typeof(PrimaryKeyAttribute)) as PrimaryKeyAttribute;
          this.PrimaryKeyMapping = this.PropertyColumnMappings.FindByProperty(pkProperty.Name);
          this.PrimaryKeyMapping.IsPrimaryKey = true;
          this.PrimaryKeyMapping.IsAutoIncementing = pkAttribute.IsAutoIncrementing;
        } else {
          // Otherwise, try to find one:
          string pkCandidate = rgx.Replace(itemType.Name.ToLower(), "") + "id";
          string columnMatch = columnNames.FirstOrDefault(c => rgx.Replace(c.ToLower(), "") == pkCandidate);
          if (columnMatch != null) {
            this.PrimaryKeyMapping = this.PropertyColumnMappings.FindByColumn(columnMatch);
            bool isAuto = this.columnIsAutoIncrementing(this.PrimaryKeyMapping.ColumnName);
            this.PrimaryKeyMapping.IsAutoIncementing = isAuto;
          }
        }
      }
    }

    protected virtual List<string> getTableColumns() {
      var result = new List<string>();
      string sql = string.Format("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{0}'", this.TableName);
      using (var conn = OpenConnection()) {
        var rdr = CreateCommand(sql, conn, "").ExecuteReader();
        while (rdr.Read()) {
          result.Add((string)rdr["COLUMN_NAME"]);
        }
      }
      return result;
    }

    public DBTable(string connectionStringName) {
      var thingyType = this.GetType().GenericTypeArguments[0].Name;
      this.TableName = Inflector.Inflector.Pluralize(thingyType);
      ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
      this.mapDbColumns();
    }
    
    public DBTable(string connectionStringName, string tableName = "",
      string primaryKeyField = "", bool pkIsIdentityColumn = true)
    {
      ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
      TableName = tableName == "" ? this.GetType().Name : tableName;
      this.mapDbColumns();
    }

    /// <summary>
    /// Conventionally introspects the object passed in for a field that 
    /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy
    /// </summary>
    public bool HasPrimaryKey(object o)
    {
      return o.ToDictionary().ContainsKey(this.PrimaryKeyMapping.PropertyName);
    }

    /// <summary>
    /// If the object passed in has a property with the same name as your PrimaryKeyField
    /// it is returned here.
    /// </summary>
    public object GetPrimaryKey(object o) {
      object result = null;
      var lookup = o.ToDictionary();
      string propName = this.PrimaryKeyMapping.PropertyName;<commit_msg>Resolve table name based on generic type name not class name<commit_after>using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using Biggy.Extensions;

namespace Biggy
{
  /// <summary>
  /// A class that wraps your database table in Dynamic Funtime
  /// </summary>
  public abstract class DBTable<T> where T: new() {
    
    protected string ConnectionString;

    /// <summary>
    /// Returns an Open Connection
    /// </summary>
    internal abstract DbConnection OpenConnection();
    protected abstract string BuildSelect(string where, string orderBy = "", int limit = 0);
    protected abstract string GetSingleSelect(string where);
    public abstract string GetInsertReturnValueSQL();
    protected abstract string DbDelimiterFormatString { get; }
    protected abstract bool columnIsAutoIncrementing(string columnName);


    public virtual string DelimitedTableName {
      get {
        return string.Format(this.DbDelimiterFormatString, this.TableName);
      }
    }

    public virtual DbColumnMapping PrimaryKeyMapping { get; set; }
    public virtual string TableName { get; set; }
    public string DescriptorField { get; protected set; }
    public virtual DbColumnMappingLookup PropertyColumnMappings { get; private set; }

    protected void mapDbColumns() {
      this.PropertyColumnMappings = new DbColumnMappingLookup(this.DbDelimiterFormatString);
      var columnNames = this.getTableColumns();
      if(columnNames.Count > 0) {
        var item = new T();
        var itemType = item.GetType();
        var props = itemType.GetProperties();
        string replaceString = "[^a-zA-Z1-9]";
        var rgx = new Regex(replaceString);

        // First map all the columns. If an explicit attribute mapping exists, use it. Otherwise, math 'em up:
        foreach (var property in props) {
          string propertyName = rgx.Replace(property.Name.ToLower(), "");
          string columnName = columnNames.FirstOrDefault(c => rgx.Replace(c.ToLower(), "") == propertyName);

          if (columnName != null) {
            // Just map it:
            var newMapping = this.PropertyColumnMappings.Add(columnName, property.Name);
            newMapping.DataType = property.GetType();
          } else {
            // Look for a custom column name attribute:
            DbColumnAttribute mappedColumnAttribute = null;
            var attribute = property.GetCustomAttributes(false).FirstOrDefault(a => a.GetType() == typeof(DbColumnAttribute));
            if (attribute != null) {
              // Use the column name found in the attribute:
              mappedColumnAttribute = attribute as DbColumnAttribute;
              columnName = mappedColumnAttribute.Name;
            }
            var newMapping = this.PropertyColumnMappings.Add(columnName, property.Name);
            newMapping.DataType = property.GetType();
          }
        }

        // Now, find the PK  column. If one is explicitly set with an attribute, use that:
        var pkProperty = props.FirstOrDefault(p => p.GetCustomAttributes(false).Any(a => a.GetType() == typeof(PrimaryKeyAttribute)));
        PrimaryKeyAttribute pkAttribute = null;
        if (pkProperty != null) {
          pkAttribute = pkProperty.GetCustomAttributes(false).FirstOrDefault(a => a.GetType() == typeof(PrimaryKeyAttribute)) as PrimaryKeyAttribute;
          this.PrimaryKeyMapping = this.PropertyColumnMappings.FindByProperty(pkProperty.Name);
          this.PrimaryKeyMapping.IsPrimaryKey = true;
          this.PrimaryKeyMapping.IsAutoIncementing = pkAttribute.IsAutoIncrementing;
        } else {
          // Otherwise, try to find one:
          string pkCandidate = rgx.Replace(itemType.Name.ToLower(), "") + "id";
          string columnMatch = columnNames.FirstOrDefault(c => rgx.Replace(c.ToLower(), "") == pkCandidate);
          if (columnMatch != null) {
            this.PrimaryKeyMapping = this.PropertyColumnMappings.FindByColumn(columnMatch);
            bool isAuto = this.columnIsAutoIncrementing(this.PrimaryKeyMapping.ColumnName);
            this.PrimaryKeyMapping.IsAutoIncementing = isAuto;
          }
        }
      }
    }

    protected virtual List<string> getTableColumns() {
      var result = new List<string>();
      string sql = string.Format("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '{0}'", this.TableName);
      using (var conn = OpenConnection()) {
        var rdr = CreateCommand(sql, conn, "").ExecuteReader();
        while (rdr.Read()) {
          result.Add((string)rdr["COLUMN_NAME"]);
        }
      }
      return result;
    }

    public DBTable(string connectionStringName) {
      var thingyType = this.GetType().GenericTypeArguments[0].Name;
      this.TableName = Inflector.Inflector.Pluralize(thingyType);
      ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
      this.mapDbColumns();
    }
    
    public DBTable(string connectionStringName, string tableName = "",
      string primaryKeyField = "", bool pkIsIdentityColumn = true)
    {
      ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
      TableName = tableName == "" ? this.GetType().GenericTypeArguments[0].Name : tableName;
      this.mapDbColumns();
    }

    /// <summary>
    /// Conventionally introspects the object passed in for a field that 
    /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy
    /// </summary>
    public bool HasPrimaryKey(object o)
    {
      return o.ToDictionary().ContainsKey(this.PrimaryKeyMapping.PropertyName);
    }

    /// <summary>
    /// If the object passed in has a property with the same name as your PrimaryKeyField
    /// it is returned here.
    /// </summary>
    public object GetPrimaryKey(object o) {
      object result = null;
      var lookup = o.ToDictionary();
      string propName = this.PrimaryKeyMapping.PropertyName;
{'fraction_non_alphanumeric': 0.09979732468585327, 'fraction_numerical': 0.001053911633563032, 'mean_word_length': 2.9896507115135833, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6151876', 'n_tokens_mistral': 3235, 'n_tokens_neox': 3192, 'n_words': 847}