那么在经典 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 来获取数据,因为它IDbConnection
像您需要的那样利用接口,而且速度非常快 - 就这样 - 一个独立于提供商的解决方案,Employee
如果需要,可以轻松扩展以构建 Oracle 版本。
该框架具有内部独立的语言语句,可转换为选定的 DB 语句,看起来像真正的神奇药丸
当然,它可能看起来像一颗神奇的药丸,但在很多方面它确实是一种诅咒。您没有灵活性(至少这并不容易)来构建针对您的需求进行优化的语句以支持高事务和容量数据库。你真是受制于这里的主人。.NET Entity Framework 为您构建了这些语句,我什至无法计算 StackOverflow 上有多少关于如何更改利用 .NET Entity Framework 的 LINQ 语句生成的 SQL 的问题。