1

プログラマーが必要としていたのは、さまざまなデータ システムを標準的で一貫性のある強力な方法で一般化する方法でした。.NET アプリケーション開発の世界では、Microsoft ADO.NET がそのニーズを満たしています。ADO.NET を使用するプログラマは、さまざまなデータベース システムに関連する細かい点を気にする代わりに、データ コンテンツ自体に注目します。

書籍「ADO.NET 4 ステップ バイ ステップ」より

常に次の構造を考えています。

ADO.NET は、次の 2 つの部分に分けることができます。

1. 従来の ADO.NET (DataSet、DataTable など)。従来のバリアントには、DB の内部データを .NET に変換する個別の DB 接続プロバイダーがあります。たとえば、MS SQL Server は 1 つの方法でデータを保持し、Oracle は別の方法でデータを保持します。したがって、プロバイダーを変更してデータベースを変更できます。

魔法の薬のように見えますが、すべての ADO.NET ステートメントはハードコードされています。

MS SQL のトップ 10 select ステートメントはSELECT TOP 10 ROWS FROM TABLE、Oracle の場合はSELECT ROWS FROM TABELE WHERE ROWNUM <= 10です。DBやプロバイダーを変えてもダメみたいですね。

2. エンティティ フレームワーク。このフレームワークには、選択された DB ステートメントに変換される内部の独立した言語ステートメントがあり、これは本当に魔法の薬のように見えます。

LINQ -> 内部 EF ステートメント -> MS SQL DB、

LINQ -> 内部 EF ステートメント -> Oracle DB。

では、単純に DB を変更して、従来の ADO.NET の DB からほとんど独立できるのでしょうか?

4

2 に答える 2

5

では、単純に DB を変更して、従来の ADO.NET の DB からほとんど独立できるのでしょうか?

もちろんできますが、それを可能にするためには、この声明を暴く必要があります。

魔法の薬のように見えますが、すべての 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 を使用してデータを取得しますIDbConnection。これは、必要応じて、Oracle バージョンを構築するために簡単に拡張できる、プロバイダーに依存しないソリューションですEmployee

このフレームワークには、選択された DB ステートメントに変換される内部の独立した言語ステートメントがあり、これは本当に魔法の薬のように見えます。

確かに、魔法の薬のように見えるかもしれませんが、実際には多くの点で呪いです. 大量のトランザクションとボリュームのデータベースをサポートするニーズに合わせて最適化されたステートメントを作成する柔軟性はありません (少なくとも簡単ではありません)。あなたは本当にここでマスターに服従しています。.NET Entity Framework はこれらのステートメントを作成します。.NET Entity Framework を利用して、この LINQ ステートメントによって生成される SQL を変更するにはどうすればよいかについて、StackOverflow に関する質問がいくつあるか数えきれません。

于 2013-04-03T13:09:22.783 に答える
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;

}

于 2016-07-11T12:01:31.877 に答える