顯示具有 MongoDB 標籤的文章。 顯示所有文章
顯示具有 MongoDB 標籤的文章。 顯示所有文章

2016年11月23日 星期三

C# + MongoDB 簡易型函式庫


    MongoDB的操作語法有別於實體資料庫,雖然支援LINQ,但新增/刪除/修改等方式仍然要以MongoDB Driver 的語法為主,所以不熟悉MongoDB的朋友們超級的痛苦,所以自製簡易版的函式庫,供大家參考囉~ 也歡迎大家多多交流。

public class MongoDBComponent
{
string _connString;
MongoClient _client;
IMongoDatabase _server;
public MongoDBComponent() {
  try
  {
    _connString = "mongodb://localhost:27017"; //未來要改為設定檔的方式,目前暫時以本機端的mongoDB為測試
    _client = new MongoClient(_connString);
    _server = _client.GetDatabase("mydb");
  }
  catch (Exception ex)
  {
    throw new Exception(ex.Message);
  }
 }
}

PS:注意事項
作者以本機端的MongoDB為測試機,記得要改_connString的設定值喔,另外要改為讀取設定值比較好維護 … 當然前提要安裝mongoDB Driver for c#

一、新增
public bool Add<T>(string tableName, T value )
{
  try
  {
    string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(value);
    BsonDocument document= BsonSerializer.Deserialize<BsonDocument>(jsonString);
    var collection = _server.GetCollection<BsonDocument>(tableName);
    collection.InsertOne(document);
    return true;
  }
  catch (Exception ex)
  {
     throw new Exception(ex.Message); 
  }
}
MongoDB是支援JSON格式,所有的物件經過JSON格式化後成為字串格式,才經由BsonDocument 轉成物件後,就可以傳入資料給MongoDB

二、修改
public bool Update<T>(string tableName,string searchKey, object searchValue, T value )
{
try
  {
   var collection = _server.GetCollection<BsonDocument>(tableName);
   var filter = Builders<BsonDocument>.Filter.Eq(searchKey, searchValue);
   var atts = value.GetType().GetProperties();

   //取得所有元素
   string attributeName = "";
   Type typeObj;
   PropertyInfo properInfo;
   object attributeValue;
   for (int i = 0; i < atts.Length - 1; i++)
   {
    attributeName = atts[i].Name.ToString();
    typeObj = value.GetType();
    properInfo = typeObj.GetProperty(attributeName);
    attributeValue = properInfo.GetValue(value);
    if (attributeValue == null || attributeName== searchKey)
    {
        continue;
    }
    var update = Builders<BsonDocument>.Update.Set(attributeName, attributeValue);
    var result = collection.UpdateOne(filter, update);
    }
    return true;
  }
  catch (Exception ex)
  {
    throw new Exception(ex.Message);
  }
}



MongoDB更新的方式要呼.update.set(''欄位名稱'',更新值), 但如果有多筆欄位值更新時,就.update.set(''欄位名稱1'',更新值).set(''欄位名稱2'',更新值) 一直串的set 串接在一起,但是想改為共用的原件,就只好用笨方法,找到物件的所有屬性後,逐一跑迴圈執行Update.Set 每個欄位就執行一次Update , 如果各位有更好的方法歡迎提供…

三、刪除
public bool Delete<T>(string tableName, string searchKey, object searchValue)
{
  try
  {
      var collection = _server.GetCollection<T>(tableName);
      var filter = Builders<T>.Filter.Eq(searchKey, searchValue);
      var result = collection.DeleteOne(filter);
      return true;
  }
  catch (Exception ex)
  {
      throw new Exception(ex.Message);
  }
}
刪除資料時,要先查詢出要刪除的那筆資料,所以呼叫Filter.Eq 找到那筆資料後,再呼叫DeleteOne ,如果說要刪除多筆時,MongoDB 有另外的函式呼叫多筆刪除喔,可別傻傻的一直呼叫DeleteOne, 這邊就不再做介紹,請有興趣的客倌去goole!

四、查詢資料
public IEnumerable<T> FindMany<T>(string tableName,Expression<Func<T,bool>>whereConditions)
{
  try
  {
    var collection = _server.GetCollection<T>(tableName).AsQueryable<T>();
    var result= collection.Where(whereConditions).AsQueryable();
    return result;
  }
  catch (Exception ex)
  {
    throw new Exception(ex.Message);
  }
}

查詢資料時為了可以自訂查詢條件,所以用 Expression<Func<T,bool>>whereConditions,允許傳入linqlamda 查詢條件,更具彈性,並將MongoDB的查詢資料列直接轉成Ienumerable<T>

五、練習 MongoDBComponent
public class Member :IEntityDB<Member>
{
[BsonId]
public ObjectId _id { get; set; }
public string Account { get; set; }
public string IdentityNumber { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Mobile { get; set; }
public string Tel { get; set; }
public string Address { get; set; }
public string Email { get; set; }
public DateTime CreateDate { get; set; }
private MongoDBComponent _MongoDBEntity = new MongoDBComponent();

public bool Add(Member entityMember)
{
    return _MongoDBEntity.Add<Member>("Member", entityMember);
}

public bool Delete(Member entityMember)
{
    return _MongoDBEntity.Delete<Member>("Member", "_id", entityMember._id);
}

public bool Update(Member entityMember)
{
    return _MongoDBEntity.Update<Member>("Member", "_id", entityMember._id, entityMember);
}

public Member FindOne(Member entityMember)
{
    return _MongoDBEntity.FindMany<Member>("Member", q => q._id == entityMember._id).FirstOrDefault();
}

public IEnumerable<Member> FindMany(Expression<Func<Member, bool>> whereConditions)
{
    return _MongoDBEntity.FindMany<Member>("Member", whereConditions);
 }
}
這樣c#就能保持使用entity Framework的習慣和彈性,具有基本新增/刪除/修改/查詢,如果有需要擴充其他方式,還請各位自已修改囉!

2016年9月12日 星期一

MongoDB進階教學

用 MongoDB 開發程式也面臨到許多難題,雖然mongo是適用物件儲存時非常具有彈性,但也有它的缺點,例如型別檢查是非常不嚴格的,如果資料欄位型態儲存失敗,也不會有任何的提示訊息,需要特別注意,因為MongoDB不支援某些SQL的型別,所以要先轉換型別,所以在LINQ上需要轉換型別時,可以參考以下的範例

var oders = (from b in realTimeDb.v_orderinfo4back
select new
{
CREATEDDATE = b.CREATEDDATE,
COLORNAME = b.colorname,
COST = b.COST,
deliverytypename = b.deliverytypename,
ITEMID = b.ITEMID,
}).AsEnumerable().Select(q=> new vOrderInfo4Back() {
CREATEDDATE = q.CREATEDDATE,
COLORNAME = q.COLORNAME,
COST = Convert.ToDouble(q.COST),
DELIVERYTYPENAME =q.deliverytypename,
ITEMID = Convert.ToDouble(q.ITEMID),
});
注意: SQLDeciaml型別,但mongodb 並沒有deciaml會被轉換成string,所以mongodb在型態檢查是不嚴格

建立mongoDB的索引
//-----add index------//
var keyCreateDates = MongoDB.Driver.Builders<vOrderInfo4Back>.IndexKeys.Ascending("CREATEDDATE");
_db.vOrderInfo4Back.Indexes.CreateOne(keyCreateDates);

var keyOrderid = MongoDB.Driver.Builders<vOrderInfo4Back>.IndexKeys.Ascending("ORDERID");
_db.vOrderInfo4Back.Indexes.CreateOne(keyOrderid);

var keySALENO = MongoDB.Driver.Builders<vOrderInfo4Back>.IndexKeys.Ascending("SALENO");
_db.vOrderInfo4Back.Indexes.CreateOne(keySALENO);


注意: MongoDB 會自動新增Collection 【如同SQL的資料表】,並不需要事先開table 儲存資料,所以如果要在程式中建立索引亦可以加快搜尋速度。

建立mongoDB的儲存模型
public class vOrderInfo4Back {
/// <summary>
/// 訂單日期
/// </summary>
public DateTime? CREATEDDATE { get; set; }
/// <summary>
/// 訂單編號
/// </summary>
public double ORDERID { get; set; }
/// <summary>
/// 項次
/// </summary>
public double ITEMID { get; set; }

/// <summary>
/// 顏色
/// </summary>
public string COLORNAME { get; set; }

/// <summary>
/// 成本
/// </summary>
public double? COST { get; set; }

/// <summary>
/// 配送方式
/// </summary>
public string DELIVERYTYPENAME { get; set; }
}
因為MongoDB十分有彈性,只有在儲存的model 增加欄位,儲存至mongoDB會自動增加欄位,並不用特別針對資料庫增加欄位,這點是NO SQL 的好處之一,只需著重程式開發上是否會影響之前的程式。

因為MongoDB 讀取時間會依預設的標準時間為主(US),所以要加上BsonDateTimeOptions 關鍵字 或者 註明當地時間 [BsonDateTimeOptions(kind=dateTimekind.Local)]
/// <summary>
/// 訂單日期
/// </summary>
[BsonDateTimeOptions]
public DateTime CREATEDDATE { get; set; }


注意:如果要讀取MongoDB日期欄位,需要加註 [BsonDateTimeOptions]在欄位的上方

單筆寫入MongoDB的範例:
var _db = new MallDbContext(); //是自己新增的物件,用來取得MongoDB的資料庫
_db.vOrderInfo4Back.InsertOne(new vOrderInfo4Back() {
COLORNAME = "",
COST=0,
ITEMID=1,
SALENO=1,
DELIVERYTYPENAME="測試"
});
_db.vOrderInfo4Back : 自己新增的物件回傳vorderInfo4Backcollection

如果有多筆資料要一次寫入MongoDB時,需要用InsertMany 可以加快mongo寫入的速度
_db.vOrderInfo4Back.InsertMany(orderList);

查詢MonoLINQ語法
var _db = new MallDbContext();
var vOrders=
_db.GetCollection<vOrderInfo4Back>("vOrderInfo4Back").AsQueryable<vOrderInfo4Back>();
//支援LINEQ語法
var orders = from q in vOrders where (string.IsNullOrEmpty(inputModel.productName) || q.SALENAME.Contains(inputModel.productName ?? ""))
&& q.CREATEDDATE >= startDate
&& q.CREATEDDATE <= endDate
&& q.SUPPLIERID == doubleFugoID
orderby q.CREATEDDATE descending
select q;


C#查詢Mongo原生語法
var builder = Builders<vOrderInfo4Back>.Filter;
var filter = builder.Gte(q => q.CREATEDDATE, startDate) & builder.Lte(q => q.CREATEDDATE, endDate) //GTE 如同>=LTE 如同<=

//商品名稱 (LIKE '% productName%' )
if (!string.IsNullOrWhiteSpace(inputModel.productName))
{
filter = filter & builder.Regex(q => q.SALENAME, "/.*" + inputModel.productName + ".*/");
}
// supplier
if (!string.IsNullOrWhiteSpace(fugoID))
{
filter = filter & builder.Eq(q => q.SUPPLIERID, doubleFUgoID);
}

//前台分類
if (categories2.Count > 0)
{
filter = filter & builder.In(q => q.SCLASSCODE, categories2);
}
var vOrders = _db.GetCollection<vOrderInfo4Back>("vOrderInfo4Back").Find(filter)

以上是我對MongoDB的基本瞭解,如果有任何補充的地方,還希望大家多多指教~ 感謝大家