Sony Arouje Blog

a programmer's log

Posts Tagged ‘Mongo db Windowless popup

Photo Album using Silverlight, Caliburn Micro and Mongo DB

with 14 comments

In this post I am going to explain a small Photo Album I created using Silverlight. With this app I also joined the band wagon of NoSQL movement. I used MongoDB as my backend for this application. This application is still in the development mode. I will add more functionality to this application like Public comments, admin module, etc. Currently this app shows the functionality of admin module, like upload photo, delete photo, etc. The application uses MVVM pattern and as usual I used Caliburn Micro here. I used several framework in the app as shown below

  • Silverlight
  • Caliburn Micro
  • Reactive Extension
  • MongoDB
  • WCF

In this application I used Reactive Extension (Rx) to perform Async service calls. I felt the Rx is a very powerful tool to perform Async calls. The one good advantage of Rx is you can place Async calls in Thread Pool and push it to dispatcher once the call completed. All these can be done in one or two lines of code. This way the UI will always responsive.

This is my first experiment with Mongo db, I can say it’s very easy to use and fast. There are several Mongo drivers for .NET available in the market and is free. I used the one from here. The advantage of Mongodb is, I don’t want to write code to map my POCO to tables. You can directly persist your entity to Mongodb. You can find a lot of article about Mongo db in internet

I don’t think I need to explain Caliburn Micro in detail here. I wrote a post that gives a brief introduction to Caliburn Micro.

Running MongoDB

Below are the steps to run MongoDB

  1. Download the latest version of Mongo db from Mongodb.org
  2. Extract the zip file to C:\Mongodb
  3. Create a Folder called data in C drive
  4. Create a subfolder called db inside data
  5. Goto command prompt and change directory to C:\Mongodb
  6. Type Mongod in the command prompt and hit enter

Now the Mongodb will listen to the default port and our app can make request. There are different ways of hosting Mongodb, you can get more details from Mongodb site.

The App

Below is the screen shot of my application.

image

This page will display the thumbnails of uploaded images. We can see the bigger image by clicking on the thumbnail as show below.

image 

The preview will display in a Windowless popup. We can delete the photo by clicking the Trash icon bottom right side of the image. We can upload images by clicking on the upload icon at the top right of the window. The upload page will get displayed as shown below.

image

When the user upload the image, the application will create a thumbnail version of the image and store along with the original image in the server. This helps to transfer and load the thumbnail page.

Let’s see the entity used in this application.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
namespace PicturePortfolio.Domain
{
    [DataContract]
    public class Photo
    {
        private const int THUMBWIDTH = 100;
        private const int THUMBHEIGHT = 100;
        public Photo()
        {
 
        }
        public Photo(PhotoPersistanceObject persistanceObject)
        {
            this.Name = persistanceObject.Name;
            this.Description = persistanceObject.Description;
            this.ID = persistanceObject.ID;
            this.PublicComments = persistanceObject.PublicComments;
            this.GUID = persistanceObject.GUID;
            
            this.LoadThumbNail();
        }

        [DataMember]
        public int ID { get; set; }

        [DataMember]
        public string GUID { get; set; }

        [DataMember]
        public byte[] ImageFile { get; set; }

        [DataMember]
        public byte[] ImageThumbNail { get; set; }

        [DataMember]
        public string Description { get; set; }

        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public List<PublicComment> PublicComments { get; set; }

        public PhotoPersistanceObject GetDataForPersistance()
        {
            PhotoPersistanceObject photoPersistance = new PhotoPersistanceObject();
            photoPersistance.ID = this.ID;
            photoPersistance.Name = this.Name;
            photoPersistance.ImagePath = "/test/" + this.Name;
            photoPersistance.Description = this.Description;
            photoPersistance.PublicComments = this.PublicComments;
            if (string.IsNullOrEmpty(this.GUID) == true)
                photoPersistance.GUID = Guid.NewGuid().ToString();
            else
                photoPersistance.GUID = this.GUID;
            return photoPersistance;
        }


        public void SaveImage()
        {
            if (this.ImageFile == null)
                throw new ArgumentNullException();
            try
            {
                byte[] buffer = this.ImageFile.ToArray();
                MemoryStream memStream = new MemoryStream();
                memStream.Write(buffer, 0, buffer.Length);

                this.SaveOrginalImage(memStream);
                this.SaveImageThumbNail(memStream);
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public void LoadImage()
        {
            string fullFileName = this.GetFileFullPath();
            this.ImageFile = this.GetImageAsByteArray(fullFileName);
        }

        public void LoadThumbNail()
        {
            string fullFileName = this.GetFilePath() + this.ThumbNailImageName;
            this.ImageThumbNail = this.GetImageAsByteArray(fullFileName);
        }

        #region Private Methods
        private void SaveOrginalImage(MemoryStream memStream)
        {
            System.Drawing.Image imgToSave = System.Drawing.Image.FromStream(memStream);
            imgToSave.Save(this.GetFileFullPath(), ImageFormat.Jpeg);
        }
        private void SaveImageThumbNail(MemoryStream memStream)
        {
            Image img = Image.FromStream(memStream);
            Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
            Image imageToSave = img.GetThumbnailImage (THUMBWIDTH, THUMBHEIGHT, myCallback, IntPtr.Zero);
            imageToSave.Save(this.GetFilePath() + ThumbNailImageName, System.Drawing.Imaging.ImageFormat.Jpeg);
        }

        private static bool ThumbnailCallback() { return false; }

        private byte[] GetImageAsByteArray(string fullFileName)
        {
            FileInfo fil = new FileInfo(fullFileName);
            if (fil.Exists == true)
            {
                FileStream fileStream = new FileStream(fullFileName, FileMode.Open, FileAccess.Read);
                byte[] buffer = new byte[fileStream.Length];
                fileStream.Read(buffer, 0, (int)fileStream.Length);
                fileStream.Close();
                return buffer;
            }
            else
                return null;
        }

        private string GetFilePath()
        {
            string filePath = AppDomain.CurrentDomain.BaseDirectory + "Images\\";
            return filePath;
        }
        private string GetFileFullPath()
        {
            string fullPath = this.GetFilePath() + this.Name;
            return fullPath;
        }
        private string ThumbNailImageName
        {
            get
            {
                string fileName = this.Name.Substring(0, Name.LastIndexOf("."));
                return fileName + "_thumb.jpg";
            }
        }
        #endregion
    }
}

 

The entity class is very simple. I may need to give a brief explanation about PhotoPersistanceObject used in the entity class. The PhotoPersistanceObject is used for persisting meta data of the image like Name, description etc. You might think that why cant we save Photo object directly, yes I also thought the same way but their is some issue with Mongodb driver I used here. It’s not supporting byte array neither binary, It allows to store binary in Mongodb but throws error while retrieving. So I used a trimmed down version of Photo entity to persist the data.

Async Calls using Thread Pool

As I told before one of the cool feature of Reactive Extension is it’s advantage of making async calls in thread pool. Even if we place async calls in separate thread in Silverlight still it will block the UI to load if we do it in page load. Because the our thread will work in UI. We can achieve the placing calls in thread pool using Rx. See the below code.

private void GetAllThumbnails()
{
    var func=Observable.FromEvent<GetAllPhotosCompletedEventArgs>(_service,"GetAllPhotosCompleted")
        .ObserveOn(Scheduler.ThreadPool);
    _service.GetAllPhotosAsync();
    func.ObserveOn(Scheduler.Dispatcher)
        .Select(result => result.EventArgs.Result)
        .Subscribe(s => this.ParseThumbnails(s.ToList()));
}

As you can see the service calls are doing in ThreadPool. Once the call is done we will change the ObserveOn to Dispatcher, other wise cross thread expection will throw. I call the GetAllThumbnails in the constructor of a ViewModel. As we are doing the call in Threadpool my UI will loaded without any delay, once the call is completed the thumbnails will start display in the listbox.

Mongodb Repository

I wrote a Generic repository similar to the one I wrote for EF. You can find the generic repository for EF in one of my post. Below is the Generic repository class for Mongodb.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB;
using MongoDB.Linq;
using MongoDB.Connections;
namespace PicturePortfolio.Persistance
{
    public class GenericRepository:IRepository
    {
        public GenericRepository()
        {
 
        }

        Mongo _mongoDb=null;
        public IMongoCollection<TEntity> GetQuery<TEntity>() where TEntity : class
        {
            if (_mongoDb == null)
            {
                _mongoDb = new Mongo();
                _mongoDb.Connect();
            }
            return DataBase.GetCollection<TEntity>(typeof(TEntity).Name + "s");
        }

        IMongoDatabase db = null;
        private IMongoDatabase DataBase
        {
            get
            {
                if (db == null)
                {
                    db = _mongoDb.GetDatabase("Protfolio");
                }
                return db;
            }
        }

        public IList<TEntity> GetAll<TEntity>() where TEntity : class
        {
            try
            {
                return GetQuery<TEntity>().Linq().ToList<TEntity>();
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public IList<TEntity> Find<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> criteria) where TEntity : class
        {
            return this.GetQuery<TEntity>().Linq<TEntity>().Where(criteria).ToList<TEntity>();
        }

        public TEntity Single<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> criteria) where TEntity : class
        {
            return this.GetQuery<TEntity>().Linq<TEntity>().Single(criteria);
        }

        public TEntity First<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> criteria) where TEntity : class
        {
            return this.GetQuery<TEntity>().Linq<TEntity>().First(criteria);
        }

        public void Add<TEntity>(TEntity entity) where TEntity : class
        {
            this.GetQuery<TEntity>().Save(entity);
        }

        public void Delete<TEntity>(TEntity entity) where TEntity : class
        {
            this.GetQuery<TEntity>().Remove(entity);
            
        }

        public void Delete<TEntity>(IEnumerable<TEntity> entites) where TEntity : class
        {
            throw new NotImplementedException();
        }

        public void Update<TEntity>(TEntity entity, TEntity old) where TEntity : class
        {
            this.GetQuery<TEntity>().Update(entity, old, UpdateFlags.None, true);
        }
    }
}

 

This app is in very primitive stage. you may need to do lot of refactoring to make use in real scenario. I will work on this app and upload it to codeplex some times later.

Download the current source here.

Follow

Get every new post delivered to your Inbox.

Join 166 other followers

%d bloggers like this: