標題是想模仿 這個 的說 但是好像還是不夠長
如果你是個無法忍受任何原因任何方式出現相同 OR 相似重複程式碼片段 性格乖僻扭曲的傢伙的話
當你在玩 LINQ + entity framework 的時候 一定遇過這個困擾
public void Kill(int id)
{
using (TransactionScope tran = new TransactionScope())
{
UserInfo UserInfo =
db.UserInfo.FirstOrDefault(x => x.UserID == id);
if (UserInfo != null)
{
db.UserInfo.Remove(UserInfo);
}
UserAccount userAccount =
db.UserAccount.FirstOrDefault(x => x.UserID == id);
if (userAccount != null)
{
db.UserAccount.Remove(userAccount);
}
db.SaveChanges();
tran.Complete();
}
}
如果你覺得這段非常自然毫無異常 恭喜 你不是個 性格乖僻扭曲的傢伙
性格乖僻扭曲的傢伙 會說 如果今天這樣怎辦
重複片段 不能接受啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊~
當下決定 反射吧!男孩
public void Kill(int id)
{
using (TransactionScope tran = new TransactionScope())
{
dynamic DbSetArr = new dynamic[]
{
db.UserInfo,
db.UserAccount
};
foreach (var dbSet in DbSetArr)
{
RemoveByUserID(id, "UserID", dbSet);
}
db.SaveChanges();
tran.Complete();
}
}
private void RemoveByUserID<T>(int id, string p, DbSet<T> dbSet) where T : class
{
T target = dbSet.FirstOrDefault(
x => (int)x.GetType()
.GetProperty("UserID")
.GetValue(dbSet) == id
);
if (target != null)
{
dbSet.Remove(target);
}
}
you usually right, but this time you are left
你通常是正確的 但是這次不行
沒錯 LINQ To Entity 有後天性功能缺乏症候群 你沒辦法在 FirstOrDefault() 這類 LINQ 方法內 搞些卑鄙手段
就在不信邪試盡各種伎倆鼻青臉腫之後 果斷決定 stackoverflow 上的各位 借給我一點元氣吧!!!
public void Kill(int id)
{
using (TransactionScope tran = new TransactionScope())
{
DbSet[] DbSetArr = new DbSet[]
{
db.UserInfo,
db.UserAccount
};
foreach (var dbSet in DbSetArr)
{
RemoveByUserID(id, "UserID", dbSet);
}
db.SaveChanges();
tran.Complete();
}
}
private void RemoveByUserID(int id, string columnName, DbSet dbSet)
{
Type type =
dbSet.GetType().GetGenericArguments()[0];
ParameterExpression parameter = Expression.Parameter(type, "x");
//x.ColumnName
MemberExpression left = Expression.Property(parameter,
columnName);
//id (Constant Value)
ConstantExpression right = Expression.Constant(id);
//x.ColumnName == id
BinaryExpression filter = Expression.Equal(left, right);
//x => x.ColumnName == id
LambdaExpression firstOrDefaultMetod = Expression.Lambda(filter, parameter);
MethodCallExpression resultExp = Expression.Call(
typeof(Queryable),
"FirstOrDefault",
new Type[] { type },
dbSet.AsQueryable().Expression,
Expression.Quote(firstOrDefaultMetod)
);
object target =
dbSet.AsQueryable().Provider.Execute(resultExp);
if (target != null)
{
dbSet.Remove(target);
}
}
太讚了 I don't know WTF is it, but it works
----工作人員清單----
----番外編的番外編----
我們都知道 泛型 是利於泛用 而非利於重用
所以如果上面的方法是個泛型方法...
public void Kill(int id)
{
using (TransactionScope tran = new TransactionScope())
{
RemoveByUserID<UserInfo>(id, "UserID", db.UserInfo);
RemoveByUserID<UserAccount>(id, "UserID", db.UserAccount);
db.SaveChanges();
tran.Complete();
}
}
private void RemoveByUserID<T>(int id, string columnName, DbSet<T> dbSet) where T : class
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
T target =
dbSet.AsQueryable().Provider.Execute<T>(Expression.Call(
typeof(Queryable),
"FirstOrDefault",
new Type[] { typeof(T) },
dbSet.AsQueryable().Expression,
Expression.Quote(
Expression.Lambda(
Expression.Equal(
Expression.Property(parameter,
columnName),
Expression.Constant(id)
),
parameter
)
)
));
if (target != null)
{
dbSet.Remove(target);
}
}
效果自然是一樣 但是由於 泛型的限制
RemoveByUserID<UserInfo>(id, "UserID", db.UserInfo);
RemoveByUserID<UserAccount>(id, "UserID", db.UserAccount);
這段變得無法跑 foreach
這時候就能用反射囉 (請注意是能用 但是這不代表這是個好方法 過度使用反射未必會得到好結果 ._.)
public void Kill(int id)
{
using (TransactionScope tran = new TransactionScope())
{
object[] DbSetArr = new object[]
{
db.UserInfo,
db.UserAccount
};
foreach (var dbSet in DbSetArr)
{
RemoveByUserID(id, dbSet);
}
db.SaveChanges();
tran.Complete();
}
}
private void RemoveByUserID(int id, object dbSet)
{
Type type =
dbSet.GetType().GetGenericArguments()[0];
//此處必須加上 BindingFlags 才能反射到 private method
MethodInfo removeMethod = this.GetType().GetMethod(
"Remove",
BindingFlags.NonPublic | BindingFlags.Instance
).MakeGenericMethod(type);
removeMethod.Invoke(this, new object[] { id, "UserID", dbSet });
}
private void Remove<T>(int id, string columnName, DbSet<T> dbSet) where T : class
{
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "x");
//x.ColumnName
MemberExpression left = Expression.Property(parameter,
columnName);
//x.ColumnName 7
ConstantExpression right = Expression.Constant(id);
//x.ColumnName == 7
BinaryExpression filter = Expression.Equal(left, right);
//x => x.ColumnName == 7
LambdaExpression firstOrDefaultMetod = Expression.Lambda(filter, parameter);
MethodCallExpression resultExp = Expression.Call(
typeof(Queryable),
"FirstOrDefault",
new Type[] { type },
dbSet.AsQueryable().Expression,
Expression.Quote(firstOrDefaultMetod)
);
T target =
dbSet.AsQueryable().Provider.Execute<T>(resultExp);
if (target != null)
{
dbSet.Remove(target);
}
}