由於當時正在學WCF,就使用了WCF作爲數據提供,由於需要常駐內存,所以使用了WINDOWS服務作爲載體。
當時覺得難點有1個,就是傳遞查詢和排序條件,委託是不能在進程間傳遞的,幸虧有了 Expression 這個類,改一下參數的類型就行了,原來的代碼基本不用動。但是 Expression 也不能直接就在進程間傳遞。後來讀到了一篇文章介紹了 ExpressionSerializer 這個類,可以把表達式轉換成XML,或者再轉換回來。理論上可以了,就開始動手了。請看代碼:
public class ModelPvd<T> : IModelPvd where T : class
{
protected ReaderWriterLock LockerRW = new ReaderWriterLock();//數據操作鎖
protected object lockerForOp = new object();//反序列化鎖
protected ExpressionSerializer serializer { private set; get; }//序列化器
public string Name { private set; get; }//類名稱
protected List<T> Lm;//數據
Func<int, T> FModelPrvd;//獲取實體對象的委託,在類初始化時,會獲取所有數據保存在這個變量。
Action<List<T>, int> ADelDlgt;//列表刪除器,當數據庫刪除了一條數據,也從Lm裏刪除這條數據。
protected ILogWriter lw;//日誌記錄器
public ModelPvd(Func<List<T>> dataPrvd, Func<int, T> modelPrvd, Action<List<T>, int> delDlgt, bool isAd, ILogWriter ilog)
{
List<Assembly> assemblies = new List<Assembly> { typeof(T).Assembly, typeof(ExpressionType).Assembly, typeof(IQueryable).Assembly };
var resolver = new TypeResolver(assemblies, new[] { typeof(T), typeof(DateTime), typeof(decimal), typeof(object) });
var knownTypeConverter1 = new KnownTypeExpressionXmlConverter(resolver);
serializer = new ExpressionSerialization.ExpressionSerializer(resolver, new CustomExpressionXmlConverter[] { knownTypeConverter1 });
//上邊這一段是序列化器的生成,是從ExpressionSerializer的例子裏拷來的。
Lm = dataPrvd.Invoke();//獲取數據
Name = typeof(T).ToString();//因爲有好些數據表都要做這個功能,這個名字用來區分。
FModelPrvd = modelPrvd;
ADelDlgt = delDlgt;
lw = ilog;
}
public object[] GetList(int pageSize, int pageIndex, XElement match, XElement orderKeySelector, bool desc, out int recordCount)//查詢數據功能。
{
Expression<Func<T, bool>> p;
lock (lockerForOp)
{
p = serializer.Deserialize<Func<T, bool>>(match);
}
lw.Write(Name + " " + p.ToString() + " l " + pageSize.ToString());
int orderWay;//排序方式,0:按照表達式排序,1,隨機排序。
Expression<Func<T, int>> o = null;
orderWay = orderKeySelector == null ? 1 : 0;
if (orderWay == 0)
{
lock (lockerForOp)
{
o = serializer.Deserialize<Func<T, int>>(orderKeySelector);
}
}
List<T> r;
LockerRW.AcquireReaderLock(200);
try
{
var l = Lm.Where(p.Compile());
recordCount = l.Count();
switch (orderWay)
{
case 1:
r = l.OrderByDescending(x => Guid.NewGuid()).Take(pageSize).ToList();
break;
default:
if (desc)
r = l.OrderByDescending(o.Compile()).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
else
r = l.OrderBy(o.Compile()).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
break;
}
}
finally
{
LockerRW.ReleaseReaderLock();
}
if (r == null)
return null;
return r.ToArray();
}
//////查詢符合條件的數據數量。
public int GetCount(XElement match)
{
int rst;
Expression<Func<T, bool>> p;
lock (lockerForOp)
{
p = serializer.Deserialize<Func<T, bool>>(match);
}
lw.Write(Name + " " + p.ToString() + "c");
LockerRW.AcquireReaderLock(200);
try
{
rst = Lm.Count(p.Compile());
}
finally { LockerRW.ReleaseReaderLock(); }
return rst;
}
某一條數據發生變化(增刪改)時,調用這個方法同步。
public bool UpdateData(int Id)
{
var model = FModelPrvd.Invoke(Id);
LockerRW.AcquireWriterLock(1000);
try
{
ADelDlgt.Invoke(Lm, Id);
if (model != null)
Lm.Add(model);
}
finally
{
LockerRW.ReleaseWriterLock();
}
return true;
}
}
下面是實現的接口。
public interface IModelPvd
{
string Name { get; }
object[] GetList(int pageSize, int pageIndex, XElement match, XElement orderKeySelector, bool desc, out int recordCount);
int GetCount(XElement match);
bool UpdateData(int Id);
}
在WCF的服務實現類裏,代碼是這樣的:
public class Service1 : IService1
{
static List<IModelPvd> Limp;
static LogWriter Lw = new LogWriter();
static Service1()
{
GetAllData();
}
static void GetAllData()
{
Limp = new List<IModelPvd>();
Limp.Add(new ModelPvdAd<Model.XXX>(DAL.XXX.GetListByDAL, DAL.XXX.GetModel, (x, y) => x.RemoveAll(z => z.Id == y), Lw));
}
public int GetCount(string typeName, XElement match)
{
var m = Limp.Find(x => x.Name == typeName);
if (m != null)
return m.GetCount(match);
else
return 0;
}
public object[] GetList(string typeName, int pageSize, int pageIndex, XElement match, XElement orderKeySelector, bool desc, out int recordCount)
{
var m = Limp.Find(x => x.Name == typeName);
if (m != null)
return m.GetList(pageSize, pageIndex, match, orderKeySelector, desc, out recordCount);
else
{
recordCount = 0;
return null;
}
}
public bool UpdateData(string typeName, int Id)
{
var m = Limp.Find(x => x.Name == typeName);
if (m != null)
return m.UpdateData(Id);
else
return false;
}
}
還有WCF的接口代碼:
public interface IService1
{
[OperationContract]
[ServiceKnownType(typeof(Model.XXX))]
object[] GetList(string typeName, int pageSize, int pageIndex, XElement match, XElement orderKeySelector, bool desc, out int recordCount);
[OperationContract]
int GetCount(string typeName, XElement match);
[OperationContract]
bool UpdateData(string typeName, int Id);
}
上邊這幾段代碼,是服務器端。ExpressionSerializer 這個類會把傳過來的 XML 轉化成原始的LAMBDA表達式,一個 Expression<<Func<Model.XXX,bool>>
這樣的條件表達式,或者一個 Expression<Func<Model.XXX,int>>
這樣的排序表達式。如果想隨機排序,就把排序的 XML 傳個 null 過來。
再來看看客戶端:
public class ModelPvd<T> where T : class
{
protected object locker = new object();//序列化鎖
protected ExpressionSerializer serializer { private set; get; }//序列化器
public string Name { private set; get; }//類名稱
public Expression<Func<T, int>> PrimaryKey;
public ModelPvd(Expression<Func<T, int>> primaryKey)
{
List<Assembly> assemblies = new List<Assembly> { typeof(T).Assembly, typeof(ExpressionType).Assembly, typeof(IQueryable).Assembly };
var resolver = new TypeResolver(assemblies, new[] { typeof(T), typeof(DateTime), typeof(decimal), typeof(object) });
var knownTypeConverter1 = new KnownTypeExpressionXmlConverter(resolver);
serializer = new ExpressionSerialization.ExpressionSerializer(resolver, new CustomExpressionXmlConverter[] { knownTypeConverter1 });
Name = typeof(T).ToString();
PrimaryKey = primaryKey;
}
public List<T> GetList<Tm>(int pageSize, int pageIndex, Expression<Func<T, bool>> Efma, Expression<Func<T, Tm>> orderBy, bool desc, out int recordCount)
{
System.Xml.Linq.XElement xmlOrder = null;
if ((typeof(Tm) != typeof(int)) || orderBy == null)
{
xmlOrder = null;
}
else
{
lock (locker)
{
xmlOrder = serializer.Serialize(orderBy);
}
}
System.Xml.Linq.XElement xmlPredicate;
lock (locker)
{
xmlPredicate = serializer.Serialize(Efma);
}
DataCenter.Service1Client Dcsc = All.GetDataCenterService();
object[] l = Dcsc.GetList(typeof(T).ToString(), pageSize, pageIndex, xmlPredicate, xmlOrder, desc, out recordCount) as object[];
All.CloseDc(Dcsc);
var Lado = new List<T>();
foreach (object i in l)
{
Lado.Add(i as T);
}
return Lado;
}
public int GetCount(Expression<Func<T, bool>> Efma, bool IsRebuild = true)
{
System.Xml.Linq.XElement xmlPredicate;
lock (locker)
{
xmlPredicate = serializer.Serialize(Efma);
}
DataCenter.Service1Client Dcsc = All.GetDataCenterService();
int rst = Dcsc.GetCount(typeof(T).ToString(), xmlPredicate);
All.CloseDc(Dcsc);
return rst;
}
public bool UpdateData(int id)
{
DataCenter.Service1Client Dcsc = All.GetDataCenterService();
var l = Dcsc.UpdateData(typeof(T).ToString(), id);
All.CloseDc(Dcsc);
return l;
}
}
具體的BLL.XXX 裏,代碼是這樣的:
static ModelPvdAd<Model.XXX> Pvd = new ModelPvdAd<Model.XXX>(x => x.Id);
public List<Model.XXX> GetList(int pageSize, int pageIndex, Expression<Func<Model.XXX, bool>> Efma, Expression<Func<Model.XXX, int>> orderBy, bool desc, out int recordCount)
{
return Pvd.GetList(pageSize, pageIndex, Efma, orderBy, desc, out recordCount);
}
這樣就基本完成了,建個測試頁面試一下。
Expression<Func<Model.XXX,bool>> q = x=>x.id>3 && x.id<100;
int rc;
var l = BLL.XXX.GetList(30,1,q,x=>x.id,true,out rc);
綁定到 Repeater 沒有問題。
但是繼續測試的時候遇到了問題。一旦表達式中含有變量,服務端就報錯了。比如上邊的例子改成:
int i=3,j=100,rc;
var l = BLL.XXX.GetList(30,1,x=>x.id>i && x.id<j,x=>x.id,true,out rc);
這樣就不行了,原因是 表達式中的 i 和 j 在傳遞的時候,是臨時類的變量,而不是 3 和 100。
要想知道我怎麼解決的,看下一篇吧。