由于当时正在学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。
要想知道我怎么解决的,看下一篇吧。