[Serializable]
public sealed class user
{
public ObjectId id;
public string n;
public int age;
public Birthday birth;
public sealed class Birthday
{
public int y;
public int m;
public int d;
}
}
咋一看,有几个地方不规范,1类名首字母和公开字段没有大写,2公开的字段,而没有用属性,3字段命名没表达它的意思。现在逐个解释一下,类名和字段没大写首字母主要是数据库里的命名是遵循js的,用js表示时大家一般会这样写:
var user={id:ObjectId("123456"),n:"loogn",age:23,birth:{y:1989,m:7,d:7}}
然而,可能有人会说可以用MongoDB.Bson.Serialization.Attributes.BsonElement这样一个Attribute关联呢!不过我不会那样做,原因就是太麻烦了!这里可能还是有疑问,留到第3个问题说。
公开字段而没有用属性也是不合常理的,学校老师都交过,不管你能不能理解,反正就是要私有化字段,想公开请用属性,哈哈!不过我想了很久还是不走寻常路了,目前为止用字段没有出现过缺陷问题,我不保证以后不会,但我感觉几率十分小,就算真的有什么问题必需要用属性,后面加上{get;set;}也不麻烦吧!属性毕竟还是方法,用属性有多余的方法调用开销,而且实体类本来就是不寻常的类,一般只表示对象状态(用字段),属性里如果有逻辑(就像老师常说的年龄不能小于0且不能大于150等等!),你会放到这里做吗?显然你都是放在BLL里做!字段命名太短了没有表达它的意思,其实这个可以和第一个一起来说,MongoDB是无模式的,同一个合集可以保多个不规则的文档,如user集合:
public delegate void PreprocessHandler(ref T document);
有很多这样的情况,得到一个实体后总是要先处理一下才可被UI方便的使用,如用户头像为空时,给个默认的,PreprocessHandler就是给BLL处理留个方便的接口啦!
这里选择委托而不是其他的元素使程序更灵活(有匿名委托嘛,I like it!),大家注意到了吧,实体类是按引用传递的,其实这里有个坑,我跳进去了,但又爬上来了!然后这里立了个牌:"此处有坑,请绕道而行",以免匆忙赶路的你也栽个跟头儿。
有这样一个情况,如果你把一个null实体对象传入处理方法,又在处理方法里判断如果是null就实体化,这样是得不到预期效果的,此null非彼null呀,实体化后方法里的型参是指向新对象了,但传过来的实参还是指向null呢,这不是我们想要的,用ref便可以解决了,也许你会说可以把实体对象返回呀,是的,但个人不喜欢那种写法,那样处理方法最后还要写reurn代码,调用方法可能还得写代码接收,麻烦!
FindOne例子完了,但FindOne远远没完,你可以做各种你喜欢的重载。
再看Find得到多个文档的方法,这里我选择IList做为返回值,在BLL决不去操作MongoCursor,但有这样一个问题,设置Fields、Limit、排序等都是在MongoCursor上操作的呀,而且这些操作很可能都是从UI传过来的,所以这里用了一个MongoCursorSettings类封装了这些设置:
MongoCursorSettings
///
/// 游标设置
///
public class MongoCursorSettings
{
int m_skip = -1;
public int Skip
{
get { return m_skip; }
set { m_skip = value; }
}
int m_limit = -1;
public int Limit
{
get { return m_limit; }
set { m_limit = value; }
}
IMongoFields m_fields = null;
public IMongoFields Fields
{
get { return m_fields; }
set { m_fields = value; }
}
IMongoSortBy m_sortBy = null;
public IMongoSortBy SortBy
{
get { return m_sortBy; }
set { m_sortBy = value; }
}
internal void Set(MongoCursor cursor)
{
if (cursor == null)
return;
if (Skip != -1)
{
cursor.Skip = Skip;
}
if (Limit != -1)
{
cursor.Limit = Limit;
}
if (Fields != null)
{
cursor.Fields = Fields;
}
if (SortBy != null)
{
cursor.SetSortOrder(SortBy);
}
}
#region 链式方法
public static MongoCursorSettings Create()
{
return new MongoCursorSettings();
}
public MongoCursorSettings SetSkip(int skip)
{
this.Skip = skip;
return this;
}
public MongoCursorSettings SetLimit(int limit)
{
this.Limit = limit;
return this;
}
public MongoCursorSettings SetFields(IMongoFields fields)
{
this.Fields = fields;
return this;
}
public MongoCursorSettings SetFields(params string[] names)
{
this.Fields = MongoDB.Driver.Builders.Fields.Include(names);
return this;
}
public MongoCursorSettings SetSortOrder(IMongoSortBy sortBy)
{
this.SortBy = sortBy;
return this;
}
#endregion
}
代码不用解释,你可以扩展此类做更多设置而不用修改Find方法。
CursorToList方法也很简单,其实把PreprocessHandler写在DAL层的原因就是这个方法啦,心细的你肯定发现了把PreprocessHandler写在BLL更合理,但那样文档集合就要多遍历一遍,MongoCursor到IList一遍,PreprocessHandler处理IList又一遍!唉,程序员容易嘛~~~
当然,你也可以对Find做各种你喜欢的重载,更要写其他方面(Insert,Update,Remove...)的方法对BaseDAL类进行完善。
最后让亲看一下User浮云(其他浮云也是这个样):
public class User:BaseDAL
{
protected override string SetCollectionName()
{
return "user";
}
}
三、BLL层
有泛型就是意思,看BaseBLL:
///
/// 业务逻辑层基类
///
/// 数据访问类型
/// 文档模型类型
public abstract class BaseBLL where TDAL : DAL.BaseDAL,new()
{
protected TDAL dal = new TDAL();
public TDocument FindOne(IMongoQuery query, PreprocessHandler preprocess)
{
return dal.FindOne(query,preprocess);
}
}
基本上是对DAL的一个调用,无他!直接到它的子类,因为是逻辑层,比浮云多一点,可以算是个神马吧:
public sealed class User : BLL.BaseBLL
{
public user FindOneByName(string name)
{
var doc = base.FindOne(Query.EQ("u", name), P1);
return doc;
}
///
/// 保证不为null
///
///
private void P1(ref user doc)
{
if (doc == null)
{
doc = new user();
}
P2(ref doc);
}
///
/// 也许可以处理婴儿,哈哈
///
///
private void P2(ref user doc)
{
if (doc != null)
{
doc.age = 0;
}
}
}
代码也是很简单,其实这里有一个想法,很多实体类总是只有一种处理方法,可以在BaseBLL里写一个PreprocessHandler委托签名的虚方法做为默认处理方法, 在BaseBLL里就调用该方法,子类需要就可重写它,这样又简单了,为了方面查看,两个类的代码写在一块了:
///
/// BaseBLL的默认处理方法
///
///
protected virtual void Preprocess(ref TDocument doc)
{
return;
}
///
/// LG.BLL.User重写基类方法
///
///
protected override void Preprocess(ref user doc)
{
if (doc == null)
doc = new user();
if (doc.birth == null)
doc.birth = new user.Birthday();
}
到此,BLL事例也完了,当然,还要做更多的工作去完善。
///
/// 为了在一次请求中同类型逻辑对象只实例化一次,
/// 只能在http请求上下文中使用
///
public static class B
{
public static T Entity() where T : class, new()
{
string key = typeof(T).Name;
if (HttpContext.Current != null)
{
var bll = HttpContext.Current.Items[key] as T;
lock (key)
{
if (bll == null)
{
bll = new T();
HttpContext.Current.Items[key] = bll;
}
}
return bll;
}
else
{
return new T();
}
}
}
在UI或确定是有HTTP请求的上下文中都可以这样调用了(当然,如果基于其他上下文,也可以写一个类似上面的实例化方法):