老规矩,不多哔哔
步骤
- 获取主键字段名称
- 这里就是与前面的博文基本一致了,获取类型,通过类型获取属性,通过属性匹配字段,不过这里多了一次判断,判断匹配的字段是否是主键
然后就愉快的拼接 SQL 字符串然后执行吧
画重点
由于本人懒,所以前面一直不想多说 ORM 映射的问题,但是躲得了初一躲不过十五,这里就是十五了(不好意思,一不小心就多了几句废话)。
我们是通过 反射机制来实现的 ORM 这里就不得不提到 Attribute 类,我们所有想要实现映射的类都必须直接或者间接的继承自此类才可以(这里说的比较绝对,具体我也没有怎么仔细查阅资料,有问题欢迎提出)
比如我们想要定义一个 表 的特性类的话,就可以这样做
[AttributeUsage(AttributeTargets.Class,AllowMultiple =false,Inherited =false)]
class TableAttribute : Attribute
{
private string _TableName;
private string _PK;
public TableAttribute(string tableName, string pk)
{
TableName = tableName;
PK = pk;
}
public string TableName { get => _TableName; set => _TableName = value; }
public string PK { get => _PK; set => _PK = value; }
}
代码是这样,那么里面那些个我们以前基本没有见过的属性是什么东西呢。
AttributeUsageAttribute
自定义属性必不可少的一个东西,自定义属性都是从它开始,我们通过它定义了这个特性类的一些主要属性,比如这个类的应用场景,是否可以被继承之类的。
对于它的众多成员我这里就只说我们常用的几个,毕竟我懒。。
AttributeTargets
这个表示我们这个特性类可以被使用到什么场景,具体它提供了一些选项
- AttributeTargets.All
这个属性可以被应用到程序中的所有元素上 - AttributeTargets.Class
这个属性只可以被用到 class 上 - AttributeTargets.Method
这个属性只可以被用到 Method 上 - AttributeTargets.Property
这个属性只可以被用到 Property 上 - 以此类推,它里面的可选项还很多,我就不一一列举了。
Inherited(继承属性)
这个属性表示了我们的这个特性类是否允许被其他的特性类继承,就相当于类被定义成 final 的类不可以被其他类继承是一个意思,可选值是一对布尔值 true OR false,ture 代表可以被继承,false 则不可以。
AllowMultiple(多属性实例)
这个属性表示是否允许元素中存在多个实例,同样是一对布尔值,true 代表可以,false 代表不可以。这里的多个实例的意思就是在同一个位置是否允许多次使用,假如我们 TestAttr 的是此属性 true ,如下代码就是行的,但是如果属性是 false,编译就会报错
[TestAttr(...)]
[TestAttr(...)]
特别情况
这是在看别人博文的时候看到的,转自此地
如果 AllowMultiple 属性和 Inherited 属性都设置为 true,则从另一个类继承的类可以继承一个属性,并在同一子类中应用同一属性的另一个实例。如果 AllowMultiple 设置为 false,则父类中的所有属性值将被子类中同一属性的新实例重写。
本篇正题
获取主键字段名称解决方案
- 设置对象类主键字段名称
定义 表 的特性类的时候,把 PK 也加入他的特性中,在对象类定义时设置 PK
或者有人觉得这样不是很好,想要主键管理主键,也可以,另外定义一个主键特性类, 在对象类定义时设置 PK - 在方法中通过 type.GetCustomAttributes(type,inherit) 方法获取主键名称
代码
特性表中就定义了 PK
[AttributeUsage(AttributeTargets.Class,AllowMultiple =false,Inherited =false)]
class TableAttribute : Attribute
{
private string _TableName;
private string _PK;
public TableAttribute(string tableName, string pk)
{
TableName = tableName;
PK = pk;
}
public string TableName { get => _TableName; set => _TableName = value; }
public string PK { get => _PK; set => _PK = value; }
}
另外定义一个 PK 特性类来管理 PK
[AttributeUsage(AttributeTargets.Class,AllowMultiple =false,Inherited =false)]
public class PrimaryKeyAttribute : Attribute
{
public PrimaryKeyAttribute(String primaryKey)
{
this.Value = primaryKey;
}
public string Value { get; protected set; }
public bool autoIncrement = false;
}
字段的特性类
[AttributeUsage(AttributeTargets.Property,AllowMultiple =false,Inherited =false)]
public class FieldAttribute : Attribute
{
private string _Fields;
private string _DataType;
private int _ValueLength;
public FieldAttribute(string fields, string types, int length)
{
Fields = fields;
DataType = types;
ValueLength = length;
}
public string Fields { get => _Fields; set => _Fields = value; }
public string DataType { get => _DataType; set => _DataType = value; }
public int ValueLength { get => _ValueLength; set => _ValueLength = value; }
}
使用上面的特性类的 对象类
[Table("dbo.Alvin","ID")]
[PrimaryKey("ID",autoIncrement = true)]
public class Record
{
private int _ID;
private string _Name;
private DateTime _InDate;
private string _InUser;
private DateTime _LastEditDate;
private string _LastEditUser;
//必须要通过这种方式(公开)创建字段,否者会访问不了
[Field("Name", "nvarchar", 50)]
public string Name { get => _Name; set => _Name = value; }
[Field("InDate", "datetime", 0)]
public DateTime InDate { get => _InDate; set => _InDate = value; }
[Field("InUser", "varchar", 15)]
public string InUser { get => _InUser; set => _InUser = value; }
[Field("LastEditDate", "datetime", 0)]
public DateTime LastEditDate { get => _LastEditDate; set => _LastEditDate = value; }
[Field("LastEditUser", "varchar", 15)]
public string LastEditUser { get => _LastEditUser; set => _LastEditUser = value; }
[Field("ID","int",0)]
public int ID { get => _ID; set => _ID = value; }
}
辅助类(SqlHelper)
public class SqlHelper : ISqlHelper
{
public const string ConnectionStringCfgKey = "ConnectionString";
private SqlHelper(){}
private static readonly Lazy<SqlHelper> LazyInstance = new Lazy<SqlHelper>(
()=>new SqlHelper(), LazyThreadSafetyMode.ExecutionAndPublication);
static SqlHelper()
{
}
private static ISqlHelper instance = null;
public static void SetInstance(ISqlHelper fakeSqlHelper)
{
instance = fakeSqlHelper;
}
public static void ResetInstance()
{
instance = null;
}
public static ISqlHelper Instance
{
get
{
if (null != instance) return instance;
return LazyInstance.Value;
}
}
public SqlConnection GetConnection()
{
return GetConnection(null);
}
public SqlConnection GetConnection(string connectionString)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
//connectionString =
// ConfigurationManager.AppSettings.Get(ConnectionStringCfgKey);
connectionString = @"Server=SCMISBIZTALK01;UID=intern;Password=Th!sls@l0ngPWD;Database=CSharpTraining";
}
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ArgumentException(
"Connection string must be specified " +
"either from parameter or App.config file.");
}
return new SqlConnection(connectionString);
}
private void EnsureConnectionOpened(DbConnection conn)
{
if (conn.State == ConnectionState.Open) return;
conn.Open();
}
private SqlCommand BuildCommnand(SqlConnection conn,
string sql,
List<SqlParameter> parameters)
{
var cmd = new SqlCommand(sql, conn);
if (null != parameters)
{
parameters.ForEach(p => cmd.Parameters.Add(p));
}
return cmd;
}
public SqlDataReader ExecuteQuery(SqlConnection conn,
string sql,
List<SqlParameter> parameters)
{
var cmd = BuildCommnand(conn, sql, parameters);
EnsureConnectionOpened(conn);
return cmd.ExecuteReader();
}
public int ExecuteNonQuery(string sql, List<SqlParameter> parameters)
{
using (var conn = GetConnection())
{
return ExecuteNonQuery(conn, sql, parameters);
}
}
public int ExecuteNonQuery(SqlConnection conn,
string sql,
List<SqlParameter> parameters)
{
var cmd = BuildCommnand(conn, sql, parameters);
EnsureConnectionOpened(conn);
return cmd.ExecuteNonQuery();
}
public T ExecuteScalar<T>(SqlConnection conn,
string sql,
List<SqlParameter> parameters)
{
var cmd = BuildCommnand(conn, sql, parameters);
EnsureConnectionOpened(conn);
var result = cmd.ExecuteScalar();
if (null == result || DBNull.Value == result)
{
return default(T);
}
return (T)Convert.ChangeType(result, typeof (T));
}
public T ExecuteScalar<T>(string sql, List<SqlParameter> parameters)
{
using (var conn = GetConnection())
{
return ExecuteScalar<T>(conn, sql, parameters);
}
}
}
操作方法(代码中应有的解释我已经注释出来)
private const string UPDATE = "UPDATE dbo.Alvin SET ";
public static int Update<TEntity>(TEntity entity)
{
using (var conn = SqlHelper.Instance.GetConnection())
{
Type type = typeof(TEntity);
PropertyInfo[] infos = type.GetProperties();
//获取主键的字段
string PK = "";
//两种方式
//方式一
/*object[] pkOBJ = type.GetCustomAttributes(typeof(TableAttribute),false);
if (pkOBJ != null)
{
PK = ((TableAttribute)pkOBJ[0]).PK;
Console.WriteLine("字段名称:{0}", PK);
}*/
//方式二
object[] pkOBJ = type.GetCustomAttributes(typeof(PrimaryKeyAttribute), false);
if (pkOBJ != null)
{
PK = ((PrimaryKeyAttribute)pkOBJ[0]).Value;
Console.WriteLine("字段名称:{0}", PK);
}
//定义SB
StringBuilder sb = new StringBuilder();
foreach (var info in infos)
{
object[] objs = info.GetCustomAttributes(typeof(FieldAttribute),false);
foreach (var obj in objs)
{
string key = ((FieldAttribute)obj).Fields;
//判断是否为主键,是就跳过
if (!key.Equals(PK))
{
//不是主键,则可以继续操作
sb.Append(key + "=");
sb.Append("'"+type.GetProperty(key).GetValue(entity, null)+"',");
}
}
}
//去掉多余的 , 号
sb.Append(")");
sb.Replace(",)", "");
//拼接 执行 sql
string sql = UPDATE + sb.ToString();
int updateNum = SqlHelper.Instance.ExecuteNonQuery(conn,
sql,
new List<SqlParameter>() { });
return updateNum;
}
}