Tuesday, February 25, 2014

Some performance comparison between techniques in C#

List<string>(map.Keys) vs map.Keys.ToList()

The following codes compare the List<string>(map.Keys) and map.Keys.ToList(). The results seems to suggest that there is no conclusive comparison

Dictionary<string, string> map = new Dictionary<string, string>();
for (int i = 0; i < 1000000; ++i)
{
 map[i.ToString()] = i.ToString();
}

for (int k = 0; k < 5; ++k)
{
 double time1 = 0;
 double time2 = 0;
 for (int i = 0; i < 300; ++i)
 {
  DateTime start_time = DateTime.Now;
  List<string> keys = new List<string>(map.Keys);
  DateTime end_time = DateTime.Now;
  TimeSpan ts = end_time - start_time;
  time1 += ts.TotalMilliseconds;

  start_time = DateTime.Now;
  keys = map.Keys.ToList();
  end_time = DateTime.Now;
  ts = end_time - start_time;
  time2 += ts.TotalMilliseconds;
 }


 //Console.WriteLine("TimeSpan1: {0} ms", time1);
 //Console.WriteLine("TimeSpan2: {0} ms", time2);
 if (time1 > time2)
 {
  Console.WriteLine("++ new List<string>(map.Keys) is better");
 }
 else if (time1 < time2)
 {
  Console.WriteLine("++ map.Keys.ToList() is better");
 }
}

Wednesday, January 15, 2014

CTabcontrol with each tab containing a widget (Yii)

Suppose in Yii, we want to pass the content of a widget to a tab in the tab control

<?php
ob_start();
$this->widget('zii.widgets.CListView', array(
    'dataProvider'=>$vulnerdataProvider,
    'itemView'=>'_latest_vulner'
));
$tab1Content=ob_get_contents();
ob_end_clean();

$this->widget('zii.widgets.jui.CJuiTabs',array(
    'tabs'=>array(
        'Tab1'=> array('content' => $tab1Content,'id' => 'tab1'),
        'tab2'=>array('content'=>'Content for tab 2', 'id'=>'tab2'),
    ),
    // additional javascript options for the tabs plugin
    'options'=>array(
        'collapsible'=>true,
    ),
));
?>

CTabView with partial view in each tab (Yii)

Suppose using Yii framework and we want to add a tab control in which each tab shows a partial view, the following codes shows how this can be done:

<?php
$nc_view=$this->renderPartial('_viewNodeConfig', array('project'=>$model), $return=true);
$user_view=$this->renderPartial('_viewUser', array('project'=>$model), $return=true);
$sim_view=$this->renderPartial('_viewSimulation', array('project'=>$model), $return=true);

$this->widget('CTabView',array(
    'activeTab'=>'tab2',
    'tabs'=>array(
        'tab1'=>array(
            'title'=>'Users',
            'content'=>$user_view,
        ),
        'tab2'=>array(
            'title'=>'Node Types',
            'content'=>$nc_view,
        ),
        'tab3'=>array(
            'title'=>'Simulations',
            'content'=>$sim_view,
        )
    ),
    'htmlOptions'=>array(
        //'style'=>'width:500px;'
    )
));
?>
In the above codes, the _viewNodeConfig.php is a partial view which looks like the following:
<?php 
$node_configs=new CActiveDataProvider('NodeConfig', 
    array(
     'criteria'=>array( 
      'condition'=>'audit_project_id=:projectId', 
      'params'=>array(':projectId'=>$project->id),
     ),
     'pagination'=>array( 
      'pageSize'=>4,
     ), 
    )
   );
   
$this->widget('zii.widgets.CListView', array(
 'dataProvider'=>$node_configs,
 'viewData'=>array('project'=>$project),
 'itemView'=>'/nodeConfig/_view',
)); 
?>

Sunday, January 12, 2014

Cascade delete in Yii model by overriding beforeDelete() and afterDelete() method

Yii provides Cascade using relations with the cascade constraint defined in database, which allows the user to delete cascade data when a model is deleted. However, in some cases, the user may want to perform this on his own (e.g. there may be some sophisticated conditioning involved during the cascade delete, or other resources such as images to be deleted associated with the model). In such a case, the user can override the beforeDelete() and afterDelete() method to perform the clean up (e.g. delete images and cascaded objects associated with the deleted model). Suppose we have a model class Project, and we want to delete a set of users as well as an image associated with the deleted Project model:

Step 1: Override beforeDelete()

Add a variable idCache to the User model class in protected/models/User.php:
private $idCache;
Next override the beforeDelete() method to assign User->id to idCache:
public function beforeDelete()
{
 $this->idCache = $this->id;

 return parent::beforeDelete();
}
The purpose of having idCache is to cache the User->id attribute in the beforeDelete() method and use it in the afterDelete() method (since the $User->id will no longer be available in the afterDelete() method

Step 2: Override afterDelete()

Next we will delete the associated users and image to the deleted Project model by overriding the afterDelete() method:
public function afterDelete()
{
 $criteria = new CDbCriteria(array(
   'condition' => 'project_id=:projectId',
   'params' => array(
    ':projectId' => $this->idCache),
  ));

 $users_associated_with_project = User::model()->findAll($criteria);

 foreach ($users_associated_with_project as $user)
 {
  $user->delete();
 }
 
 $filename=$this->getImagePath($this->idCache);
 if(file_exists($filename))
 {
  unlink($filename);
 }

 parent::afterDelete();
}

Create CListView of a model class in another model's view (Yii)

Suppose we have two models: Project and User, in which each Project owns a number of Users. We wish to display a list of associated users in the project instance's view

Modify the protected/views/project/view.php by adding the following line in the script file:

<?php $this->renderPartial('_viewUser', array('project'=>$model)); ?>

Next create a partial view protected/views/project/_viewUser.php with the following codes for its implementation:

<?php 
$users=new CActiveDataProvider('User', 
    array(
     'criteria'=>array( 
      'condition'=>'audit_project_id=:projectId', 
      'params'=>array(':projectId'=>$project->id),
     ),
    ),
    array( 
     'pagination'=>array( 
      'pageSize'=>20,
     ), 
    )
   );
   
$this->widget('zii.widgets.CListView', array(
 'dataProvider'=>$users,
 'viewData'=>array('project'=>$project),
 'itemView'=>'/user/_view',
)); 
?>

Cascade create a model and attach it to another model in Controller in a One-to-Many relationship (Yii)

Suppose we have a situation where we a set of users, each of which belong to a particular project. In Yii, we already create two models, namely Project and User. The User model class contains an attribute project_id, which is the id attribute value of the associated Project model. Now a user cannot be created without knowing the project_id it is associated with

Step 1:

In the protected/views/project/view.php, add a link to create user associated with the project:

<?php echo CHtml::link(CHtml::encode('Create user under the project'), array('user/create', 'project_id'=>$model->id)); ?>
The project_id allows the UserController (i.e. the controller associated with the User model) to identify the project_id to which the created user should be associated with.

Step 2:

In the protected/controllers/UserController.php, add a private member variable _project:

private $_project = null;

Next in the UserController.php, find the method filters(), and add one line for projectContext filter:

/**
 * @return array action filters
 */
public function filters()
{
 return array(
  'accessControl', // perform access control for CRUD operations
  'projectContext + create index admin', //check to ensure valid event context
 );
}

Next in the UserController.php, defines the following methods:

public function filterProjectContext($filterChain) 
{
 //set the project identifier based on either the GET or POST input
 //request variables, since we allow both types for our actions 
 $project_id = null;
 if(isset($_GET['project_id'])) 
  $project_id = $_GET['project_id'];
 else
  if(isset($_POST['project_id'])) 
   $project_id = $_POST['project_id'];
 $this->loadProject($project_id);
 //complete the running of other filters and execute the requested action
 $filterChain->run();
}

protected function loadProject($project_id) 
{ 
 //if the project property is null, create it based on input id 
 if($this->_project===null) 
 {
  $this->_project=Project::model()->findbyPk($project_id); 
  if($this->_project===null)
  { 
   throw new CHttpException(404,'The requested project does not exist.'); 
  }
 }
 return $this->_project;
}
The above method allows Yii code to create and assign the _project with the content from database using the project_id passed in from Step 1. As a result, before the actionCreate() method is invoked, the _project is already properly initialized based project_id attribute passed in. The final step is to assign $user->project_id in the actionCreate() method of the UserController class, as shown below:
public function actionCreate()
{
 $model=new User;
 $model->project_id=$this->_project->id;

 // Uncomment the following line if AJAX validation is needed
 // $this->performAjaxValidation($model);

 if(isset($_POST['User']))
 {
  $model->attributes=$_POST['User'];
  if($model->save())
   $this->redirect(array('view','id'=>$model->id));
 }

 $this->render('create',array(
  'model'=>$model,
 ));
}

Use of beforeSave() and beforeValidate() for Yii models when writing to database

beforeValidate() is very handy for performing validation of the model before it is save to the database in Yii, below shows an example of how it is used in a Yii model class Project

protected function beforeValidate() 
{
 if($this->isNewRecord)
 {
  // set the create date, last updated date and the user doing the creating
  $this->audit_create_time=$this->audit_update_time=new CDbExpression('NOW()');
  $this->audit_update_time=new CDbExpression('NOW()'); 
  
  $existing_proj=Project::model()->find('projname=?', array($this->projname));
  if(isset($existing_proj))
  {
   return false;
  }
 }
 else
 {
  //not a new record, so just set the last updated time and last updated user id
  $this->audit_update_time=new CDbExpression('NOW()'); 
 }
 
 return parent::beforeValidate();
}
The above code checks whether a project with the same projname already exist in the database, if yes, then the validation return false, it also updates the create time and update time depending on whether the action is to create or update a project.

beforeSave() is useful to perform additional data processing before a model is saved to the database, below shows an example of how it is used in a Yii model class User

public function beforeSave()
{  
 if($this->isNewRecord)
 {
  $this->audit_db_create_time=$this->audit_db_update_time=new CDbExpression('NOW()');
 }
 else
 {
  $this->audit_db_update_time=new CDbExpression('NOW()'); 
 }
 
 $this->password=$this->encrypt($this->password);
 
 return TRUE;
}
The above code encrypt the password before it is saved to the database, it also updates the create time and update time for writing the model to the database depending on whether the action is to create or update a user