2013-04-01 6 views
1

Какие программисты необходимы, чтобы обобщать различные системы данных в стандартном, последовательном и мощном виде. В мире разработки .NET Microsoft ADO.NET отвечает этой потребности. Вместо , беспокоясь о мелочах, связанных с различными базами данных, системы, программисты, использующие ADO.NET, фокусируются на самом содержимом данных.Отдельные базы данных ADO.NET

Из книги "ADO.NET 4 Шаг за шагом"

Всегда думал следующую структуру.

ADO.NET может быть sepearated на две части:

1. Классический ADO.NET (DataSets, DataTables и т.д.). В классическом варианте существуют различные поставщики соединений DB, каждый из которых преобразует внутренние данные DB в .NET. Например, MS SQL Server хранит данные одним способом, а Oracle - в другом. Таким образом, мы можем изменить базу данных с меняющимся поставщиком.

Кажется, что это волшебная пилюля, однако все инструкции ADO.NET жестко закодированы.

Для MS SQL top 10 select statement SELECT TOP 10 ROWS FROM TABLE, а для Oracle SELECT ROWS FROM TABELE WHERE ROWNUM <= 10. Кажется, что замена БД и провайдера не поможет, не так ли?

2. Структура сущности. Эта структура имеет внутренние независимых операторов языка, который преобразуется в выбраной DB statesment, который выглядит, как поистине волшебной таблетки:

LINQ -> Внутренний EF оператор -> MS SQL DB,

LINQ -> Внутренние EF заявление -> Oracle DB.

Так можно ли просто изменить БД и быть почти независимым от БД в классическом ADO.NET?

ответ

3

Так можно ли просто изменить БД и быть почти независимым от БД в классическом ADO.NET?

Конечно, вы можете, но чтобы иметь возможность сделать это, мы должны развенчать это утверждение.

Кажется, что это волшебная пилюля, однако все утверждения ADO.NET жестко закодированы.

Это не побочный продукт ADO.NET - это побочный продукт вашей архитектуры. Вы создаете SQL-запросы не в том месте. Вам нужны конкретные, специфичные для поставщика модели, которые могут создавать заявления, которые отличаются между поставщиками. Это не так плохо, как кажется - большинство заявлений могут быть автоматически сгенерированы с использованием отражения - это просто особые случаи.

Например, скажем, у меня была модель так:

public class Employee 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 
    public DateTime DateOfBirth { get; set; } 
} 

и давайте говорить, что я хотел, чтобы создать SELECT заявление из этого.Ну, я буду первым нужна пара атрибутов, чтобы сказать мне, что собственность является PK и какие свойства полей данных:

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] 
internal sealed class DataFieldAttribute : Attribute 
{ 
    public DataFieldAttribute() 
    { 
    } 
} 

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] 
sealed class PrimaryKeyAttribute : Attribute 
{ 
    public PrimaryKeyAttribute() 
    { 
    } 
} 

и теперь мне нужно, чтобы украсить этот класс:

public class Employee 
{ 
    [PrimaryKey] 
    public int ID { get; set; } 
    [DataField] 
    public string Name { get; set; } 
    [DataField] 
    public DateTime DateOfBirth { get; set; } 
} 

и теперь я просто нужен простой процесс, чтобы создать SELECT заявления, поэтому сначала давайте создадим базовую модель данных класса:

public abstract class DataModelBase 
{ 
    protected string _primaryKeyField; 
    protected List<string> _props = new List<string>(); 

    public DataModelBase() 
    { 
     PropertyInfo pkProp = this.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(PrimaryKeyAttribute), false).Length > 0).FirstOrDefault(); 
     if (pkProp != null) 
     { 
      _primaryKeyField = pkProp.Name; 
     } 

     foreach (PropertyInfo prop in this.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(DataFieldAttribute), false).Length > 0)) 
     { 
      _props.Add(prop.Name); 
     } 
    } 

    public virtual string TableName { get { return this.GetType().Name; } } 

    public virtual string InsertStatement 
    { 
     get 
     { 
      return string.Format("INSERT INTO [{0}] ({1}) VALUES ({2})", 
       this.TableName, 
       GetDelimitedSafeFieldList(", "), 
       GetDelimitedSafeParamList(", ")); 
     } 
    } 

    public virtual string UpdateStatement 
    { 
     get 
     { 
      return string.Format("UPDATE [{0}] SET {1} WHERE [{2}] = @{2}", 
       this.TableName, 
       GetDelimitedSafeSetList(", "), 
       _primaryKeyField); 
     } 
    } 

    public virtual string DeleteStatement 
    { 
     get 
     { 
      return string.Format("DELETE [{0}] WHERE [{1}] = @{1}", 
       this.TableName, 
       _primaryKeyField); 
     } 
    } 

    public virtual string SelectStatement 
    { 
     get 
     { 
      return string.Format("SELECT [{0}], {1} FROM [{2}]", 
       _primaryKeyField, 
       GetDelimitedSafeFieldList(", "), 
       this.TableName); 
     } 
    } 

    protected string GetDelimitedSafeParamList(string delimiter) 
    { 
     return string.Join(delimiter, _props.Select(k => string.Format("@{0}", k))); 
    } 

    protected string GetDelimitedSafeFieldList(string delimiter) 
    { 
     return string.Join(delimiter, _props.Select(k => string.Format("[{0}]", k))); 
    } 

    protected string GetDelimitedSafeSetList(string delimiter) 
    { 
     return string.Join(delimiter, _props.Select(k => string.Format("[{0}] = @{0}", k))); 
    } 
} 

и теперь давайте наследовать от этой модели данных:

public class Employee : DataModelBase 

и стрела, теперь я могу получить эти заявления в любое время, когда они мне нужны, и эти заявления работают для любого конкретного поставщика прямо сейчас.

И тогда я буду использовать Dapper to get the data, потому что он использует интерфейс IDbConnection как то, что вам нужно и это смешно быстро - и там вы идете - поставщик независимого решения, которое было бы легко расширить, чтобы построить версию Oracle в Employee, если необходимо.

Эта структура имеет внутренние независимых операторов языка, который преобразуется в выбраной DB statesment, который выглядит, как поистине волшебной таблетки

Конечно, это может выглядеть как волшебную таблетку, но это действительно проклятие в много способов. У вас нет гибкости (по крайней мере, это непросто) создавать инструкции, оптимизированные для ваших нужд, для поддержки больших транзакций и баз данных томов. Вы действительно подчиняетесь хозяину здесь. .NET Entity Framework строит эти инструкции для вас, и я даже не могу подсчитать, сколько вопросов по StackOverflow было выполнено, как я могу изменить SQL, создаваемый этим оператором LINQ, используя платформу .NET Entity Framework.

+0

Как мы можем добавить метод создания таблицы здесь или сгенерировать сценарий создания таблицы с помощью Poco класса? – ashy

0

смотрел, чтобы поиграть с этим. Я знаю, что это старый пост. благодаря приведенному выше примеру здесь мало что изменилось. Еще много предстоит сделать

using System.Collections.Generic; 
using System.Reflection; 
using Dapper; 
using System.Linq; 
using AppAttributes; 
using System.ComponentModel.DataAnnotations; 
using System; 

public abstract class DataModelBase 
{ 
    protected string _primaryKeyField; 
    protected List<string> _props = new List<string>(); 
    protected List<BuildClass> _class = new List<BuildClass>(); 

public DataModelBase() 
{ 
    PropertyInfo pkProp = this.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length > 0).FirstOrDefault(); 
    if (pkProp != null) 
    { 
     _primaryKeyField = pkProp.Name; 
    } 

    foreach (PropertyInfo prop in this.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(DataFieldAttribute), false).Length > 0)) 
    { 
     _props.Add(prop.Name); 
    } 

    foreach(PropertyInfo prop in this.GetType().GetProperties()) 
    { 
     if(prop.GetCustomAttributes<ExcludeAttribute>().Count<ExcludeAttribute>() > 0) continue; 
     MaxLengthAttribute maxLength = prop.GetCustomAttribute<MaxLengthAttribute>(); 
     MinLengthAttribute minLength = prop.GetCustomAttribute< MinLengthAttribute>(); 
     StringLengthAttribute stringLength = prop.GetCustomAttribute< StringLengthAttribute>(); 
     RequiredAttribute required = prop.GetCustomAttribute<RequiredAttribute>(); 
     RangeAttribute range = prop.GetCustomAttribute<RangeAttribute>(); 
     DataTypeAttribute dataType = prop.GetCustomAttribute<DataTypeAttribute>(); 
     KeyAttribute key = prop.GetCustomAttribute<KeyAttribute>(); 

     var kyk = prop.PropertyType; 
     //var sss = kyk.FullName.; 


     var cl = new BuildClass 
     { 
      Name = prop.Name, 
      MaxLength = maxLength != null 
      ? (int?)maxLength.Length 
      : stringLength != null 
       ? (int?)stringLength.MaximumLength : null, 
      MinLength = minLength != null 
      ? (int?)minLength.Length 
      : stringLength != null 
       ? (int?)stringLength.MinimumLength : null, 
      PrimaryKey = key != null ? true : false, 
      Type = prop.PropertyType.Name.ToString() 
     }; 
     _class.Add(cl); 
    } 
} 

[Exclude] 
public virtual string TableName { get { return this.GetType().Name; } } 

[Exclude] 
public virtual string InsertStatement 
{ 
    get { 
     return string.Format("INSERT INTO [{0}] ({1}) VALUES ({2})", 
      this.TableName, 
      GetDelimitedSafeFieldList(", "), 
      GetDelimitedSafeParamList(", ")); 
    } 
} 

[Exclude] 
public virtual string UpdateStatement 
{ 
    get { 
     return string.Format("UPDATE [{0}] SET {1} WHERE [{2}] = @{2}", 
      this.TableName, 
      GetDelimitedSafeSetList(", "), 
      _primaryKeyField); 
    } 
} 

[Exclude] 
public virtual string DeleteStatement 
{ 
    get { 
     return string.Format("DELETE [{0}] WHERE [{1}] = @{1}", 
      this.TableName, 
      _primaryKeyField); 
    } 
} 

[Exclude] 
public virtual string SelectStatement 
{ 
    get { 
     return string.Format("SELECT [{0}], {1} FROM [{2}]", 
      _primaryKeyField, 
      GetDelimitedSafeFieldList(", "), 
      this.TableName); 
    } 
} 

[Exclude] 
public virtual string CreateStatement 
{ 
    get { 
     return "CREATE TABLE " + TableName+" (" + GetDelimetedCreateParamList(",") 
      + ", CONSTRAINT PK_" 
      + _class.Where(c=>c.PrimaryKey).FirstOrDefault().Name 
      + " PRIMARY KEY(" 
      + string.Join(",", _class.Where(c=>c.PrimaryKey).Select(c=>c.Name)) + "))"; 
    } 
} 

protected string GetDelimetedCreateParamList(string delimeter) 
{ 
    return string.Join(delimeter, _class.Select(k => string.Format(" {0} {1} ({2}) {3}" + Environment.NewLine, 
     k.Name, 
     GetSqlType(k.Type), 
     k.MaxLength, 
     k.NotNull == true || k.PrimaryKey == true ? "NOT NULL " : "" 
     //k.PrimaryKey == true ? "PRIMARY KEY" : "" 

     ).Replace("()", "")) 
     ); 
} 

protected string GetSqlType(string type) 
{ 
    switch(type.ToUpper()) 
    { 
     case "INT16": 
      return "smallint"; 
     case "INT16?": 
      return "smallint"; 
     case "INT32": 
      return "int"; 
     case "INT32?": 
      return "int"; 
     case "INT64": 
      return "bigint"; 
     case "INT64?": 
      return "bigint"; 
     case "STRING": 
      return "NVARCHAR"; 
     case "XML": 
      return "Xml"; 
     case "BYTE": 
      return "binary"; 
     case "BYTE?": 
      return "binary"; 
     case "BYTE[]": 
      return "varbinary"; 
     case "GUID": 
      return "uniqueidentifier"; 
     case "GUID?": 
      return "uniqueidentifier"; 
     case "TIMESPAN": 
      return "time"; 
     case "TIMESPAN?": 
      return "time"; 
     case "DECIMAL": 
      return "money"; 
     case "DECIMAL?": 
      return "money"; 
     case "bool": 
      return "bit"; 
     case "bool?": 
      return "but"; 
     case "DateTime": 
      return "datetime"; 
     case "datetime?": 
      return "datetime"; 
     case "double": 
      return "float"; 
     case "double?": 
      return "float"; 
     case "char[]": 
      return "nchar"; 


    } 
    return "UNKNOWN"; 
} 

private string CreateField(BuildClass column) 
{ 
    return " " + column.Name + " " + column.Type + " (" + column.MaxLength + ") "; 
} 

protected string GetDelimitedSafeParamList(string delimiter) 
{ 
    return string.Join(delimiter, _props.Select(k => string.Format("@{0}", k))); 
} 

protected string GetDelimitedSafeFieldList(string delimiter) 
{ 
    return string.Join(delimiter, _props.Select(k => string.Format("[{0}]", k))); 
} 

protected string GetDelimitedSafeSetList(string delimiter) 
{ 
    return string.Join(delimiter, _props.Select(k => string.Format("[{0}] = @{0}", k))); 
} 

}

public class BuildClass 
{ 
    public string Name { get; set; } 
    public string Type { get; set; } 
    public bool PrimaryKey { get; set; } 
    //public bool ForeignKey { get; set; } 
    public int? MinLength { get; set; } 
    public int? MaxLength { get; set; } 
    public bool NotNull { get; set; } = false; 

}