Friday 21 August 2015

Simpler Framework with DbContext and DbSet

I am trying to design a base framework that utilises DbContext as a unit of work and its DbSet properties as repositories. There are some voices on the Internet suggesting this approach for simplicity, performance, faster development effort and being able to keep exposing Entity Framework goodness. I also try to use bounded context approach that is based on domain driven design.

I have a base context class that is derived from DbContext:
    public abstract class BaseContext : DbContext
    {
        static BaseContext()
        {
        }
        protected BaseContext()
            : base("name=FrameworkOneDatabase")
        { }               
    }
Then some bounded contexts. Below is one of them:
    public class ArticleBoundContext : BaseContext
    {
        public ArticleBoundContext()
        {            
        }

        public virtual DbSet<Article> Article { get; set; }
        public virtual DbSet<User> Submitter { get; set; }
    }
Also a basic service class to help me calling CRUD operations on any bounded context:
    public class CRUDService
    {
        private BaseContext _context;

        public CRUDService(BaseContext context)
        {
            this._context = context;
        }

        public void Insert(dynamic entityObject)
        {
            dynamic dbset = GetDbSetFromObject(entityObject);
            entityObject.ObjectState = ObjectState.Added;
            dbset.Add(entityObject);
            _context.ApplyStateChanges();
        }

        public void InsertOrUpdate(dynamic entityObject)
        {
            dynamic dbset = GetDbSetFromObject(entityObject);
            dbset.Attach(entityObject);  
            _context.ApplyStateChanges();
        }
        
        public void Delete(dynamic entityObject)
        {
            dynamic dbset = GetDbSetFromObject(entityObject);
            dbset.Remove(entityObject);
        }

        public async Task<int> Commit()
        {
            var result = await _context.SaveChangesAsync();
            return result;
        }

        public void Dispose()
        {
            _context.Dispose();
        }

        private dynamic GetDbSetFromObject(dynamic entityObject)
        {
            // if dynamicproxies wrapper is used then get the base object
            System.Type objectType = entityObject.GetType();
            if (objectType.Namespace == "System.Data.Entity.DynamicProxies")
            {
                objectType = objectType.BaseType;
            }

            var dbset = (from p in _context.GetType().GetProperties()
                    where p.PropertyType.IsGenericType
                    && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)
                    let entityType = p.PropertyType.GetGenericArguments().First()
                    where objectType == entityType
                    select p.GetValue(_context)).FirstOrDefault();

            if (dbset == null)
            {
                throw new System.ArgumentException("object type does not exist in the context");
            }

            return dbset;
        }
    }
The class has methods accepting an object then will find its corresponding DbSet member of the context. The method then call one of the DbSet operations. The GetDbSetFromObject() method is the one that will do the finding.

Then I can use all of the classes and structure above to do something like in the tests below:
    [TestClass]
    public class CRUDServiceTest
    {
        private ArticleBoundContext _context;
        private CRUDService _service;

        public CRUDServiceTest()
        {
            _context = new ArticleBoundContext();
            _service = new CRUDService(_context);
        }
        
        [TestMethod]
        public async Task CanInsertArticle()
        {
            Article article = new Article { Title = "title test " + DateTime.Now.ToString("HH:mm:ss"), Description = "desc", Url = "test.com", ObjectState = ObjectState.Added };
            article.Submitter = new User { Firstname = "first " + DateTime.Now.ToString("HH:mm:ss"), Lastname = "last", ObjectState = ObjectState.Added };
            _service.Insert(article);
            var insert = await _service.Commit();
            Assert.IsTrue(insert > 0);
        }

        [TestMethod]
        public async Task CanUpdateArticle()
        {
            Article article = _context.Article.FirstOrDefault(); 
            article.Title = "UPDATED TITLE " + DateTime.Now.ToString("HH:mm:ss");
            article.Description = "UPDATED DESCRIPTION";
            article.ObjectState = ObjectState.Modified;
            _service.InsertOrUpdate(article);
            var update = await _service.Commit();
            Assert.IsTrue(update > 0);
        }

        [TestMethod]
        public async Task CanUpdateSubmitter()
        {
            var article = _context.Article.FirstOrDefault(); 
            article.Submitter.Firstname = "UPDATED FIRSTNAME " + DateTime.Now.ToString("HH:mm:ss");
            article.Submitter.Lastname = "UPDATED LASTNAME " + DateTime.Now.ToString("HH:mm:ss");
            article.Submitter.ObjectState = ObjectState.Modified;

            _service.InsertOrUpdate(article);
            var update = await _service.Commit();
            Assert.IsTrue(update > 0);
        }
        
        [TestMethod]
        public async Task CanUpdateSubmitter_2()
        {
            var submitter = _context.Submitter.FirstOrDefault();
            submitter.Firstname = "UPDATED FIRSTNAME " + DateTime.Now.ToString("HH:mm:ss");
            submitter.Lastname = "UPDATED LASTNAME " + DateTime.Now.ToString("HH:mm:ss");
            submitter.ObjectState = ObjectState.Modified;

            _service.InsertOrUpdate(submitter);
            var update = await _service.Commit();
            Assert.IsTrue(update > 0);
        }

        [TestMethod]
        public async Task CanDeleteArticle()
        {
            var article = _context.Article.FirstOrDefault();

            _service.Delete(article);
            var result = await _service.Commit();

            Assert.IsTrue(result > 0);
        }

        [TestMethod]
        public async Task CanInsertAndDeleteSubmitter()
        {
            var submitter = new User();
            submitter.Firstname = "firstname " +DateTime.Now.ToString("HH:mm:ss");
            submitter.Lastname = "lastname " + DateTime.Now.ToString("HH:mm:ss");

            _service.Insert(submitter);
            var result = await _service.Commit();

            Assert.IsTrue(result > 0);
            var insertedSubmitter = await _context.Submitter.FindAsync(submitter.Id);
            Assert.IsTrue(insertedSubmitter.Firstname == submitter.Firstname && insertedSubmitter.Lastname == submitter.Lastname);

            _service.Delete(submitter);
            result = await _service.Commit();

            Assert.IsTrue(result > 0);
        }

        [TestMethod]
        public async Task ThrowExceptionWhenInsertingWrongObject()
        {
            try
            {
                int test = 5;
                _service.Insert(test);
                var insert = await _service.Commit();
            }
            catch (Exception ex)
            {
                Assert.IsInstanceOfType(ex, typeof(System.ArgumentException));
            }
        }
    }

Monday 3 August 2015

Memory Issue when Building Cordova Project in Visual Studio 2015

I have just upgraded my desktop to Windows 10 and installed Visual Studio 2015 Community Edition. When trying to build my first Apache Cordova project in VS2015, I got these errors:
Could not create the Java virtual machine
Error occurred during initialization of VM
Could not reserve enough space for object heap


After trying to google for the solution, I found one that is better than the others. It told me to add a new system variable _JAVA_OPTIONS with -Xmx512M as the value.

Below is the detail:
Go to Control Panel -> System -> Advanced, then click Environment Variables. Click New on the System variables section then put:
Variable name: _JAVA_OPTIONS
Variable value: -Xmx512M

Solved my issue right away.