【问题标题】:ADO.NET distinct data basesADO.NET 不同的数据库
【发布时间】:2013-04-01 15:13:47
【问题描述】:

程序员需要的是一种泛化不同数据系统的方法 以标准、一致和强大的方式。在 .NET 的世界中 应用程序开发,Microsoft ADO.NET 满足了这一需求。代替 担心与不同数据库相关的细节 系统,使用 ADO.NET 的程序员专注于数据内容本身。

摘自《ADO.NET 4 Step by Step》一书

一直在思考下一个结构。

ADO.NET 可以分为两部分:

1.经典 ADO.NET(数据集、数据表等)。在经典变体中,有不同的 DB 连接提供程序,每个都将 DB 内部数据转换为 .NET。例如,MS SQL Server 以一种方式保存数据,而 Oracle 以另一种方式保存数据。所以我们可以通过改变提供者来改变数据库。

似乎是灵丹妙药,但所有 ADO.NET 语句都是硬编码的。

对于 MS SQL,前 10 个选择语句是 SELECT TOP 10 ROWS FROM TABLE,对于 Oracle,是 SELECT ROWS FROM TABELE WHERE ROWNUM <= 10看来更改数据库和提供程序并没有帮助,不是吗?

2。实体框架。 该框架具有内部独立的语言语句,可转换为选定的 DB 语句,看起来像真正的神奇药丸:

LINQ -> 内部 EF 语句 -> MS SQL DB,

LINQ -> 内部 EF 语句 -> Oracle DB。

那么在经典 ADO.NET 中是否可以简单地更改数据库并几乎独立于数据库?

【问题讨论】:

    标签: ado.net


    【解决方案1】:

    那么在经典 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 接口就像你需要的那样并且它速度快得离谱 - 就这样 - 一个独立于提供商的解决方案,可以轻松扩展如有必要,构建 Employee 的 Oracle 版本。

    该框架具有内部独立的语言语句,可转换为选定的 DB 语句,看起来像真正的神奇药丸

    当然,它可能看起来像一颗神奇的药丸,但在很多方面它确实是一种诅咒。您没有灵活性(至少这并不容易)来构建针对您的需求进行优化的语句以支持高事务和容量数据库。你真是受制于这里的主人。 .NET Entity Framework 为您构建了这些语句,我什至无法计算 StackOverflow 上有多少关于如何利用 .NET Entity Framework 更改此 LINQ 语句生成的 SQL 的问题。

    【讨论】:

    • 如何在此处添加创建表方法或使用 poco 类生成创建表脚本?
    【解决方案2】:

    正盯着玩这个。我知道这是一个旧帖子。感谢上面的例子,这里几乎没有修改。还有很多工作要做

    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;
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-12
      • 2010-12-24
      • 1970-01-01
      • 1970-01-01
      • 2013-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多