Performing CRUD Operation with RavenDB using RSS Feed as an use case.

Rajnilari2015
Posted by in NoSql category on for Beginner level | Points: 250 | Views : 821 red flag
Rating: 5 out of 5  
 1 vote(s)

In this article, we will perform a simple CRUD operation on RavenDB by taking RSS feed as an example.


 Download source code for Performing CRUD Operation with RavenDB using RSS Feed as an use case.

Recommendation
Read Step by Step installation guide to RavenDB before this article.

Introduction

RavenDB is a Document Store Database for .NET that stores semi-structured or unstructured records/documents in JSON format.

Earlier we have published an article as Step by Step installaton guide to RavenDB. In this article, we will extend that further and perform a simple CRUD operation on that by taking RSS feed as an example.

Why we are using RSS Feed as an usecase ?

It's an expected question. But before going to answer, we would like to recommend to refer What is NoSQL? in order to get a better understanding. Now let us look into what RSS Feed is... Quoting Wikipedia

RSS (Rich Site Summary; originally RDF Site Summary; often called Really Simple Syndication) uses a family of standard web feed formats to publish frequently updated information: blog entries, news headlines, audio, video. An RSS document (called "feed", "web feed" or "channel") includes full or summarized text, and metadata, like publishing date and author's name.

We can figure out from the statement that, a RSS is purely a semi-structured/un-structured document data and it cannot be fit into a proper RDBMS or rather to say a RDBMS would not be a right choice for such data. Henceforth, is the inclination for NoSQL Document DB for which we will use RavenDB.

Environment Setup

We hope that Raven DB has been setup properly into the system. Fire up Visual Studio, and let us create a Console Application

Next from the NuGet Package Manager Console, let us issue the below command

PM> Install-Package RavenDB.Client

If Every thing goes smooth, we will receive

Using the code

Let us first start by creating a model as under

using System;

namespace ConsoleApplication1
{
    public class RSS
    {
        public string Id { get; set; }
        public string QuestionID { get; set; }
        public string QuestionTitle { get; set; }
        public string QuestionDescription { get; set; }
        public DateTime PublishDate { get; set; }
    }
}

This is a simple RSS class with some properties. Let us write the DBOperation class as under

using Raven.Client.Document;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    public class DBOperation
    {
        /// <summary>
        /// Insert RSS Feed
        /// </summary>
        /// <param name="rssFeed"></param>
        /// <returns></returns>
        public string InsertRssFeed(RSS rssFeed)
        {
            using (var ds = new DocumentStore
                    {
                        Url = "http://localhost:8080",
                        DefaultDatabase = "CRUDDemo"
                    }.Initialize())

            using (var session = ds.OpenSession())
            {
                //Stores entity in session,
                session.Store(rssFeed);

                //Saves all the pending changes to the server.
                session.SaveChanges();                             
            }
            return rssFeed.Id;
        }

        /// <summary>
        /// Update RSS Feed
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public RSS UpdateRssFeed(string id)
        {
            var modifiedRss = new RSS();
            using (var ds = new DocumentStore
            {
                Url = "http://localhost:8080",
                DefaultDatabase = "CRUDDemo"
            }.Initialize())

            using (var session = ds.OpenSession())
            {
                var rss = session.Load<RSS>(id); //find the record with the id

                if (null != rss)
                {
                    //update the question title and question description
                    rss.QuestionTitle = "This is an updated question title";
                    rss.QuestionDescription = "This is an updated question";

                    //Saves all the pending changes to the server.
                    session.SaveChanges();

                    //load the modified RSS record
                    modifiedRss = session.Load<RSS>(id);
                }                
            }
            return modifiedRss;
        }

        /// <summary>
        /// Delete RSS Feed
        /// </summary>
        /// <param name="id"></param>
        public void DeleteRssFeed(string id)
        {           
            using (var ds = new DocumentStore
            {
                Url = "http://localhost:8080",
                DefaultDatabase = "CRUDDemo"
            }.Initialize())

            using (var session = ds.OpenSession())
            {
                var rss = session.Load<RSS>(id); //find the record with the id

                if (null != rss)
                {
                    //marks for deletion
                    session.Delete(rss);

                    //deletion completed. Saves the pending changes to the server.
                    session.SaveChanges();                  
                }
            }
        }

        /// <summary>
        /// Read RSS Feed
        /// </summary>
        /// <param name="lstIDs"></param>
        /// <returns></returns>
        public List<RSS> ReadRssFeed(IEnumerable<string> lstIDs)
        {
            var rssCollection = new List<RSS>();
            using (var ds = new DocumentStore
            {
                Url = "http://localhost:8080",
                DefaultDatabase = "CRUDDemo"
            }.Initialize())
            using (var session = ds.OpenSession())
            {
                foreach (var id in lstIDs)
                {
                    rssCollection.Add(session.Load<RSS>(id));
                }

                //Find records whose question title starts with "NAudio"
                /* session
                 .Query<RSS>()
                 .Where(rss=>rss.QuestionTitle.StartsWith("NAudio"))
                 .ToList()
                 .ForEach((e) => rssCollection.Add(e));*/
            }
            return rssCollection;
        }
    }
}

Let us understand what we are doing here.

using (var ds = new DocumentStore
{
    Url = "http://localhost:8080",
    DefaultDatabase = "CRUDDemo"
}.Initialize())

As a first step, we are using the DocumentStore class that inherits from the abstract class DocumentStoreBase. The DocumentStore class is manages access to RavenDB and open sessions to work with RavenDB.The DocumentStore class needs a URL and optionally the name of the database. Our RavenDB server is running at 8080 port (at the time of installation we did so). Also we specified a DefaultDatabase name which is CRUDDemo here. The function Initialize() initializes the current instance.

The code piece

using (var session = ds.OpenSession())
{
    ........................
    ........................                            
}

From the DocumentStore we get the Session object which we will use for all the operations in RavenDB. The method OpenSession() opens the session.This Session object provides the transactional functionality and also prepares a set of modifications that are submitted to the database with the SaveChanges()method. The rest of the code is heavily documented to understand.

Now let us look at how we are loading the RSS feeds. We have a class call RSSReader.cs which has the below code

using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Xml;

namespace ConsoleApplication1
{
    public class RSSReader
    {
        public IEnumerable<RSS> GetRSSFeeds()
        {           
            string url = <RSS FEED URL>;            
            SyndicationFeed feed = null;

            using (var reader = XmlReader.Create(url))
            {
                feed = SyndicationFeed.Load(reader);
                var syndicationItems = feed.Items.ToArray();

                for (int i = 0; i < syndicationItems.Length; i++)
                {
                    yield return new RSS
                    {
                        QuestionID = syndicationItems[i].Id,
                        QuestionTitle = syndicationItems[i].Title.Text,
                        QuestionDescription = syndicationItems[i].Summary.Text,
                        PublishDate = syndicationItems[i].PublishDate.Date
                    };
                }
            }            
        }
    }
}

As can be figure out that, we are using SyndicationFeed Class that represents a top-level feed object, <feed> in Atom 1.0 and <rss> in RSS 2.0. It resides under System.ServiceModel.Syndication namespace.We are using yield statement for maintaining a stateful iteration.

And finally, the main invocation program which is as under

using System;
using System.Collections.Generic;
using System.Linq;
using static System.Threading.Tasks.Parallel; //please note the use of "using static" of C#6.0
using static System.Console; //please note the use of "using static" of C#6.0

namespace ConsoleApplication1
{
    class Program
    {
        static List<string> lstIDs = new List<string>();
        static void Main(string[] args)
        {
            InsertRSSFeed();            
            SelectRssFeed();
            UpdateRssFeed();
            DeleteRssFeed();
        }

        #region CRUD Ops

        private static void InsertRSSFeed()
        {
              ForEach<RSS> //please note the use of Parallel.ForEach
                (new RSSReader().GetRSSFeeds()
                ,feed => lstIDs.Add(new DBOperation().InsertRssFeed(feed)));      
        }

        private static void UpdateRssFeed()
        {
            var updatedRSSFeed = new DBOperation()
                  .UpdateRssFeed(lstIDs[0]);

            string strModifiedRSS = $"Id: {updatedRSSFeed.Id}";
            strModifiedRSS += $",QuestionID: {updatedRSSFeed.Id} ,QuestionTitle: {updatedRSSFeed.QuestionTitle} ";
            strModifiedRSS += $",QuestionDescription: {updatedRSSFeed.QuestionDescription} , PublishDate: {updatedRSSFeed.PublishDate}";

            Clear();
            WriteLine("--------------------------- MODIFIED RECORD -------------------------------");
            WriteLine(strModifiedRSS);
            ReadKey();
        }

        private static void DeleteRssFeed()
        {
                 new DBOperation()
                  .DeleteRssFeed(lstIDs[0]);
        }

        private static void SelectRssFeed()
        {
            new DBOperation()
                 .ReadRssFeed(lstIDs.Take(2))
                 .ForEach
                 (
                    i =>
                        WriteLine
                        (
                            i.Id + Environment.NewLine +
                            i.QuestionID + Environment.NewLine +
                            i.QuestionTitle + Environment.NewLine +
                            i.QuestionDescription + Environment.NewLine +
                            i.PublishDate + Environment.NewLine +
                            "----------------------------------------------------------------------------"
                        )                       
                 );
            ReadKey();
        }

        #endregion
    }
}

The program is preety simple to understand. But please not the use of using static of C#6.0.We are importing the static members of the Console class into our namespace System.This indicates that can use the static members (e.g. Clear(), WriteLine(),ReadKey()) of the Console class directly with no need to qualify them with their namespace or type name (e.g. System.Console.ReadKey() or Console.ReadKey()).

Also please note the use of Parallel.ForEach here. The benefit of using that is since the insert operation(s) are not sequential and can happen at an atomic way. So in order to perform the execution in a parallel way which uses multiple Threads and thereby performs faster execution, we are using Task Parallel Library's (TPL) ForEach method which executes a foreach operation on an System.Collections.IEnumerable in which iterations may run in parallel. It resides under the namespace System.Threading.Tasks.Parallel.

References

RavenDB Documentation

Conclusion

In this article, we have learnt about the appropriate use of a NOSql Document Database by performing CRUD operation using RavenDB taking RSS Feed as an example. Hope this will be useful. Zipped code attached.

Page copy protected against web site content infringement by Copyscape

About the Author

Rajnilari2015
Full Name: Niladri Biswas (RNA Team)
Member Level: Platinum
Member Status: Member,MVP
Member Since: 3/17/2015 2:41:06 AM
Country: India
-- Thanks & Regards, RNA Team


Login to vote for this post.

Comments or Responses

Login to post response

Comment using Facebook(Author doesn't get notification)