今天也不知道哪根筋不对了,没有打Dota却想写一篇文章,其实自己也不知道写什么,专业技术较水,但真的很想写,自己文采又不够,所以还是只能写技术方面的文章啦!半瓶子还是可以晃荡晃荡的哈哈~

NOSQL近来势头不错,MongoDB更是其中的娇娇者,自己学nosql的时候也是参考了大量的资料,最终决定要从MongoDB入手的,最重要的原因有两点:1自己是简单的爱好者,一切问题我都在想是否有简单的方法解决,宁可停下来去思考大量时间,也不愿用笨方法马上去做,而MongoDB的操作大都很简单,2自己是JS的爱好者,没事就喜欢拿一本js的本从头到尾看一边,也不管记住多少,也不管用不用得到,就是喜欢,MongoDB以BSON格式存储,所以操作也起来也算得心应手!现在做一个项目正是用MongoDB做为数据库的,一开始没有DAL,BLL直接访问数据库,然后就到UI了,而且BLL是全静态的(我喜欢静态方法的调用简单,但狠静态类的不能继承!),当时考虑的是用MongoDB的驱动去操作太直白了!感觉没必要再写个DAL!,后来知道我想法还是很天真的,哈哈!下面就说现在的操作方式吧~

一、Module层

[转载]MongoDB的三层操作
    [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;         }     }
[转载]MongoDB的三层操作

咋一看,有几个地方不规范,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集合:

{id:1,n:"user1",desc:"我的描述"} {id:2,n:"user2"}

所以数据库必须保存文档的每一个元素的name(即id,name,desc,id,name),所以元素name越短越节省空间,本来是用name更能表达的,这里用了n,其实只要把常用的约定一下,绝大部分人都是可以接受的。

在user里还有个内嵌类Birthday,而这个类大写了首字母,我是这样考虑的,内嵌类名全部按C#命名规范,因为容器类有一个该内嵌类类型的字段,这里是birth,但如果找不到合适的缩写怎么办呢,直接小写内嵌类名就可以了,如内嵌城市类City,字段名为city就不会重复了。

二、DAL层

在这一层要写一个基类,完成这个基类后,其他的各各DAL类都是浮云了~,在写基类之前有一个MongoHelper,MongoHelper很简单,直接给出代码且不写解释:

[转载]MongoDB的三层操作
public class MongoHelper     {         static readonly MongoConnectionStringBuilder ConnectionStringBuilder;         static MongoHelper()         {             if (System.Web.Configuration.WebConfigurationManager.ConnectionStrings["dbConn"] != null)             {                 ConnectionStringBuilder = new MongoConnectionStringBuilder(System.Web.Configuration.WebConfigurationManager.ConnectionStrings["dbConn"].ConnectionString);             }             else if (System.Configuration.ConfigurationManager.ConnectionStrings["dbConn"] != null)             {                 ConnectionStringBuilder = new MongoConnectionStringBuilder(System.Configuration.ConfigurationManager.ConnectionStrings["dbConn"].ConnectionString);             }             else             {                 throw new Exception("web.config或app.config的connectionStrings无dbConn配置");             }         }          public static MongoServer GetServer(string connectionString)         {             var server = MongoServer.Create(connectionString);             return server;         }          public static MongoServer GetServer()         {             var server = MongoServer.Create(ConnectionStringBuilder);             return server;         }          public static MongoDatabase GetDatabase(string databaseName)         {             return GetServer().GetDatabase(databaseName);         }          public static MongoDatabase GetDatabase()         {             return GetDatabase(ConnectionStringBuilder.DatabaseName);         }     }
[转载]MongoDB的三层操作

完了后就可以写BaseDAL了,如果没有泛型,DAL的工作还真是索然无味,但现在用泛型DAL的工作有趣多了,先承上代码:

[转载]MongoDB的三层操作
    /// <summary>     /// 数据访问层基类     /// </summary>     /// <typeparam name="T">文档实体类</typeparam>     public abstract class BaseDAL<TDocument>     {         protected internal string CollectionName {  set; get; }          /// <summary>         /// 设置集合名         /// </summary>         protected abstract string SetCollectionName();          private MongoCollection<TDocument> m_collection;          /// <summary>         /// 根据CollectionName得到MongoCollection对象         /// </summary>         protected internal MongoCollection<TDocument> Collection         {             get             {                 if (m_collection == null)                 {                     CollectionName = SetCollectionName();                     m_collection = MongoHelper.GetDatabase().GetCollection<TDocument>(CollectionName);                 }                 return m_collection;             }         }          /// <summary>         /// 根据query条件得到一个文档对象         /// </summary>         /// <param name="query">查询条件</param>         /// <param name="preprocess">预处理方法</param>         /// <returns></returns>         public TDocument FindOne(IMongoQuery query, PreprocessHandler<TDocument> preprocess)         {             var document = Collection.FindOne(query);             if (preprocess != null)             {                 preprocess(ref document);             }             return document;         }          /// <summary>         /// 把MongoCursor转换成IList类型         /// </summary>         /// <param name="cursor">文档游标</param>         /// <param name="preprocess">预处理方法</param>         /// <returns></returns>         protected internal IList<TDocument> CursorToList(MongoCursor<TDocument> cursor, PreprocessHandler<TDocument> preprocess)         {             IList<TDocument> list = new List<TDocument>(30);             bool isPreprocess = preprocess != null;             foreach (TDocument document in cursor)             {                 var doc = document;                 if (isPreprocess)                     preprocess(ref doc);                 list.Add(doc);             }             return list;         }                /// <summary>         /// 根据query查询集合         /// </summary>         /// <param name="query">条件</param>         /// <param name="preprocess">预处理方法</param>         /// <returns></returns>         public IList<TDocument> Find(IMongoQuery query, MongoCursorSettings cursorSettings, PreprocessHandler<TDocument> preprocess)         {             var cursor = Collection.Find(query);             if (cursorSettings != null)             {                 cursorSettings.Set(cursor);             }             var list = CursorToList(cursor, preprocess);             return list;         }      }
[转载]MongoDB的三层操作

最上面的代码就是设置操作哪个集合,这里有一点感觉不爽,为什么属性的get和set不能分别为抽象的呢?!虽然可以把整个属性标记为abstract,但在实现类中也要写get和set的实现(set可以是空代码块),所以这里回归原本用了一个SetCollectionName的抽象方法让子类去设置自己对应的集合名。
当你得到MongoCollection对象,特别是MongoCollection<TDocument>这样的强类型对象,BaseDAL剩下的工作也成浮云了!(都是对驱动方法的封装和个性化处理),如FindOne方法,用到一个委托:

public delegate void PreprocessHandler<T>(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类封装了这些设置:

[转载]MongoDB的三层操作
    /// <summary>     /// 游标设置     /// </summary>     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     }
[转载]MongoDB的三层操作

相关文章: