2014年5月26日 星期一

PMP renew certification

今天收到renew後的證書囉!
背後的學習過程是最有意義的一件事!
剛好今天也是加入新團隊的第一天,osu!

2014年5月23日 星期五

Fake team 的徵兆

專案執行上,由 Cross-functional team 從各部門各領域的成員一起來負責是最有效率的方法。

但是並非找了一群人進來專案後,就表示這是個真正的團隊!  

如果有以下徵兆,我們團隊可能是個 Fake Team !


1. 成員有定期參與會議,但是他們漫不經心。

2. 成員沒有分配足夠的時間在專案執行上。 

3. 功能部門主管命令成員執行其他工作或任務,導致專案工作被迫延後執行。

4. 團隊被賦予很多責任和工作,但是卻沒有實質權力(例如 : 決策)。

5. 專案做得再好,也不會有任何獎賞或是價值。 (例如:考績)


在這樣的Fake team, 當主管和成員沒有將心思放在專案的執行上,卻又要求專案如期如質產出, 有可能嗎?


2014年5月13日 星期二

Mongo DB repository for CRUD (1)

Mongo DB repository for CRUD

2.  Define a Customer model

using MongoDB.Bson.Serialization.Attributes;
public
class Customer
{
 [BsonId]
public string Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public DateTime LastModified { get; set; }
}

3.  interface

public interface ICustomerRepository
{
IEnumerable<Customer> GetAll();
Customer Get(string id);
Customer Add(Customer item);
bool Delete(string id);
bool Update(string id, Customer item);
}

4.  implement the CRUD methods in CustomerRepository : ICustomerRepository

Constructor and private object :
Note that the official Mongo suggest to use
MongoClient to build the connection , instead of using
MongoServer _server = MongoServer.Create(…);

Private MongoServer _server = null;
private MongoDatabase _database = null;
private MongoCollection _customers = null;

public CustomerRepository(string connection)
{
if (string.IsNullOrEmpty(connection))
{
connection = "mongodb://localhost:27017"; //Mongo's default port is 27017
}
MongoClient client = new MongoClient(connection);
this._server = client.GetServer();
this._database = _server.GetDatabase("Customers");
this._customers = _database.GetCollection<Customer>("Customers");
this._server.Connect();
}


Get all documents from "Customers" collection :
public IEnumerable<Customer> GetAll()
{
return _Customers.FindAllAs<Customer>();
}


Get a document with certain id
public Customer Get(string id)
{
IMongoQuery query = MongoDB.Driver.Builders.Query<Customer>.EQ(e =>e.Id, id);
return this._customers.FindAs<Customer>(query).FirstOrDefault();
}


Insert a document
public Customer Add(Customer item)
{
item.Id = MongoDB.Bson.ObjectId.GenerateNewId().ToString();
item.LastModified = DateTime.Now;
this._customers.Insert(item);
return item;
}


Delete a document with id
public bool Delete(string id)
{
IMongoQuery query = MongoDB.Driver.Builders.Query<Customer>.EQ(e => e.Id, id);
var result = _customers.Remove(query);
return result.DocumentsAffected == 1;
}


Update a document

public bool Update(string id, Customer item)
{
IMongoQuery query = MongoDB.Driver.Builders.Query<Customer>.EQ(e => e.Id, id);           
item.LastModified = DateTime.UtcNow;
IMongoUpdate update = MongoDB.Driver.Builders.Update
                .Set("Email", item.Email)
                .Set("LastModified", DateTime.Now)
                .Set("Name", item.Name)
                .Set("Phone", item.Phone);
var result = _customers.Update(query, update);
return result.UpdatedExisting;
}


5.  AP layer (Web API)
Just use the methods in Web API.


6.  Test with Fiddler

Create





Get a document


Update

Delete


Note that you can use OData in Web API with Mongo as well.
ex. http://localhost:6691/api/customer?$orderby=Phone

7.  Reference





Mongo DB install issue (update)

Mongo DB install issue

1.  Download Mongo DB
This demo is based on version 2.6.

2.  Create DB’s data directory
$\MongoDB 2.6 Standard\bin> md C:\MongoDB\data\db

3.  Start Mongo with the specific DB:data directory (Default is C:\data\db)
$\MongoDB 2.6 Standard\bin> mongod.exe --dbpath C:\MongoDB\data\db

PS. C:\MongoDB\data\db is what we create in step 2.



4.  Connect
$\MongoDB 2.6 Standard\bin> mongo

Client


Host



5.  OK, we will make the mongo as a windows service in the following setup.
6.  Create config file

First, we create a Mongo log folder :
$\MongoDB 2.6 Standard\bin> md C:\MongoDB\log

Create a config file and set the log filepath property …
C:\MongoDB>echo logpath=C:\MongoDB\log\mongo.log > C:\MongoDB\mongod.cfg

Set the DB:data directory to the config …
C:\MongoDB>echo dbpath=C:\MongoDB\data\db >> C:\MongoDB\mongod.cfg

then add the following settings into the “mongod.cfg”.
logappend=true




Start Mongo DB as a windows service

Install the Mongo service
$\MongoDB 2.6 Standard\bin> mongod.exe --config C:\MongoDB\mongod.cfg --install

Start the service
$\MongoDB 2.6 Standard\bin> net start MongoDB




5.  Stop the Mongo service
$\MongoDB 2.6 Standard\bin> net stop MongoDB

6.  Remove the service
$\MongoDB 2.6 Standard\bin> mongod.exe –remove


2014年5月12日 星期一

Enable Tracing in Web API


Enable Tracing in Web API

2.  Install or update the Microsoft ASP.NET Web API 2.1 Web Host

3.  Enable the tracing function in Web API’s  WebApiConfig

public static void Register(HttpConfiguration config)
{
//Enable tracing
config.EnableSystemDiagnosticsTracing();
}


4.  Result :



2014年5月9日 星期五

HiLo implemetation with Web API

HiLo implemetation with Web API
Author : JB


1.  Create table schema (with Sql Server)

CREATE TABLE HiLo
(
KEY_NAME varchar(100) NOT NULL,
NEXT_HI bigint NOT NULL,
MAX_VAL bigint NOT NULL default 0
);
ALTER TABLE HiLo ADD CONSTRAINT PK_HiLo PRIMARY KEY (KEY_NAME);



2.  SQL
Insert (Create HiLo) :
//Get
internal
void BindGetNextHiSql(ref Adaman.Infra.Lib.ISql sqlObj,String keyName)
{
sqlObj.SqlCommand = String.Format(@"
SELECT NEXT_HI,MAX_VAL FROM {0}
WHERE KEY_NAME=@KEY_NAME ", base.HILO_TABLE);

sqlObj.SqlParams = new SqlParameter[]{
new SqlParameter("@KEY_NAME", keyName)
};
}
//Get
internal void BindUptNextHiSql(
ref Adaman.Infra.Lib.ISql sqlObj, String keyName, long interval)
{
sqlObj.SqlCommand = String.Format(@"
UPDATE {0}
SET
NEXT_HI = CASE WHEN (NEXT_HI+1)*{1} > MAX_VAL THEN 0 ELSE (NEXT_HI+1) END
WHERE KEY_NAME=@KEY_NAME ", base.HILO_TABLE, interval);

sqlObj.SqlParams = new SqlParameter[]{
new SqlParameter("@KEY_NAME", keyName)
};
}



3.  Domain

Create HiLo :
Just call the sql to create a HiLo record into the database. No other business logic.

/// HiLoCreateManager
public class HiLoCreateManager : IDisposable
{
private IDbModule _dbModule = null;
public bool CreateSequence(Adaman.Infra.HiLo.HiLo hl)
{
bool isCreatedOk = false;
//Check if the Key name exists ?
if (this.ChkIfKeyNameExist(hl.KeyName))
isCreatedOk = false;
else {
this.CreateNewHiLoInstance(hl); //Use Get HiLo sql
isCreatedOk = true;
}
return isCreatedOk;
}
}

/// Create new HiLo instance in database
private void createNewHiLoInstance(Adaman.Infra.HiLo.HiLo hl)
{
ISql sqlObj = new Sql();               
using (SqlHiLoDomainManager sqlMnger = new SqlHiLoDomainManager())
{
sqlMnger.BindCreateNewHiLoInstanceSql(ref sqlObj, hl);
}
//insert
this._dbModule.ParaExecuteNonQuery(sqlObj.SqlCommand, sqlObj.SqlParams);
}


Get Next Hi :
In HiLo algorithm, we have a Singleton instance with minHi and maxHi. Every time one try to get the nextHi value, it comes from minHi to maxHi.
After the nextHi between the interval of these two values runs out, it will get another minHi and maxHi.
For example, when minHi = 0 and maxHi = 99, we will get nextHi like …
0, 1, …  99. After 99 is used, the minHi and maxHi will set to 100 and 199 as the interval is set to 100.
Besides, when we get the minHi/maxHi value, we must update the NEXT_HI for future use.

public sealed class HiLoGetValManager:IDisposable
{
        private String _keyName = String.Empty; //紀錄Key Name
        private long _minHiVal = 0;
        private long _maxHiVal = 0;

        private static long INTERVAL = 1000; //minHi~maxHi
        private static bool isCreated = false; //是否已intial min/max value
        private static object block = new object();
       
       
        /// Get Next HiLo value
        public long GetNextVal(String keyName)
        {
          
//First time loaded or change the key name
            if (!isCreated || !keyName.Equals(this._keyName))
            {
                this.syncSetInstance(keyName); //Get minHi/maxHi
                isCreated = true;
                this._keyName = keyName;
            }

            try
            {
                if (_minHiVal < _maxHiVal)
                {
                    _minHiVal++;
                }
                else //If minHi exceeds maxHi, get minHi/maxHi again!
                {
                    this.syncSetInstance(keyName);
                    _minHiVal++;
                }

                return this._minHiVal;
            }
            catch (Exception)
            {
                throw;
            }
        }


        /// <summary>
        /// 取得NEXT HI
        /// </summary>
        private void setMinMaxHi(String keyName)
        {
            long nextHi = 0;
            long maxVal = 0;

            try
            {
                TransactionOptions _transOptions = new TransactionOptions();
                _transOptions.IsolationLevel = System.Transactions.IsolationLevel.Serializable;
                _transOptions.Timeout = new TimeSpan(0, 1, 0); //timeout : 1 hr

                using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, _transOptions))
                using (SqlHiLoGetValueSingleton sqlHiLoGetValueSingleton = new SqlHiLoGetValueSingleton())
                {
                    //Get current Hi value
                    ISql sqlObj = new Adaman.Infra.Lib.Sql();
                    sqlHiLoGetValueSingleton.BindGetNextHiSql(ref sqlObj, keyName);
                    DataTable dtNextHi = this._dbModule.ParaGetDataTable(sqlObj.SqlCommand, sqlObj.SqlParams);
                    if (dtNextHi != null && dtNextHi.Rows.Count > 0)
                    {
                        long.TryParse(dtNextHi.Rows[0].ItemArray[0].ToString(), out nextHi); //current Hi value
                        long.TryParse(dtNextHi.Rows[0].ItemArray[1].ToString(), out maxVal); //Max Value
                    }
                    sqlObj.Dispose();

                    //Set minHi/maxHi value with HiLo algorithm
                    this.setMinMaxHiValStrategy(ref nextHi, maxVal);

                    //Update NEXT HI in database
                    ISql sqlUpt = new Adaman.Infra.Lib.Sql();
                    sqlHiLoGetValueSingleton.BindUptNextHiSql(ref sqlUpt, keyName, INTERVAL);
                    this._dbModule.ParaGetDataTable(sqlUpt.SqlCommand, sqlUpt.SqlParams);
                    sqlUpt.Dispose();

                    scope.Complete();
                }
            }
            catch (Exception)
            {
                throw;
            }
           
        }

        /// lock the resource and avoid the instance be changed at the same time
        private void syncSetInstance(String keyName)
        {       
lock (block){
this.setMinMaxHi(keyName);
}
        }
        /// Set minHi/maxHi value
        private void setMinMaxHiValStrategy(ref long nextHi, long maxVal)
        {
            try
            {
                //Normal case
                this._minHiVal = nextHi * INTERVAL;
                this._maxHiVal = nextHi * INTERVAL + (INTERVAL - 1);
               
                //Check the MAX VALUE constraint
                if (this._minHiVal > maxVal)
                {
                    nextHi = 0;
                    this._minHiVal = nextHi * INTERVAL;
                    this._maxHiVal = nextHi * INTERVAL + (INTERVAL - 1);
                }
                else if (this._minHiVal <= maxVal && this._maxHiVal > maxVal)
                {
                    this._maxHiVal = maxVal;
                }
            }
            catch (Exception)
            {
                throw;
            }
           
        }
}


4.  Web API
I skipped the codes in Web API.
The important thing is that we must initial a singleton instance (
HiLoGetValManager) in Web API, in order to make sure everyone get the unique HiLo value with it.

So in WebApiConfig.cs

//Singleton instance
public static Icash.HiLo.Domain.HiLoGetValManager HiLoGetValMnger = null;

public
static void Register(HttpConfiguration config)
{
//
HiLoGetValMnger=new Domain.HiLoGetValManager();
}




5.  Test Result