lbrt.Net

Pinoy Software Engineer

IOS: Audiosession: audioplayer ducking on same session

I’ve been busy for almost a year now, just joined a company that is focus on improving developers skills and creating great applications! Learned a lot from my almost 2years of absence in blogging.

Before you continue, I’d like to note that this article assumes you already know or studied AVFoundation Framework and has knowledge to the usage/creation of AVPlayer and AVAudioplayer

So I learned a lot of IoS objective-c coding since my last blog, One of which has no resource on the net yet which is “Audiosession: audioplayer ducking on same session”. I’ve seen a lot of references on which “ducking” was implemented via settings below:

UInt32 value = 1; //1 means YES
AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(value), &value);
AudioSessionSetActive(YES);

But that code snippet basically allows ducking of sound from other audio sessions. What if you have two avplayers or one avplayer as background music and one avaudioplayer that well play as warning sound and you basically need the background music to “duck” (e.g. ducking is the lowering of volume of a sound to allow other sound to have emphasis, then increasing again in sound to its previous level once the other sound has finished).

here is a few lines of code credited to my co-worker “Ryan G.”.

-(void) setVolume:(float)volume withTime:(CMTime)time andAudioPlayer:(AVPlayer *)audioPlayer
{
    if(audioPlayer.currentItem.audioMix == nil)
    {
        NSArray *tracks = audioPlayer.currentItem.asset.tracks;
        
        NSMutableArray *params = [NSMutableArray arrayWithCapacity:tracks.count];
        for (AVAssetTrack *track in tracks) {
            AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
            
            [audioInputParams setVolume:volume atTime:time];
            [audioInputParams setTrackID:track.trackID];
            [params addObject:audioInputParams];
        }
        
        AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
        audioMix.inputParameters = params;
        audioPlayer.currentItem.audioMix = audioMix;
    }
    else
    {
        NSArray *params = audioPlayer.currentItem.audioMix.inputParameters;
        
        for (AVMutableAudioMixInputParameters *audioInputParams in params)
        {
            [audioInputParams setVolume:volume atTime:time];
        }
        
        AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
        audioMix.inputParameters = params;
        audioPlayer.currentItem.audioMix = audioMix;
    }
}

From there you basically just use it before and after sound playing of another player e.g.:

- (void) playWarningSound
{
   //Note: self.audioPlayer is a background music which is an instance of AVPlayer
   // self.audioplayer2 is a warning music which is an instance of AVAudioplayer

   //this will lower self.audioPlayer volume to 0.1
   [self setVolume:0.1 withTime:[self.audioPlayer currentTime] andAudioPlayer:self.audioPlayer1];
   self.audioplayer2.delegate = self;
   [self.audioplayer2 play];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
   //this will return self.audioPlayer volume to 1
   [self setVolume:1 withTime:[self.audioPlayer currentTime] andAudioPlayer:self.audioPlayer1];
}

Note: self.audioPlayer is a background music which is an instance of AVPlayer, self.audioplayer2 is a warning music which is an instance of AVAudioplayer

So that’s it, your audioplayer2 will have a normal volume while your audioplayer1 will “duck” in volume. Don’t have a working project for download since this is pretty much straight forward if you know objective-c before hand. Happy XCoding!!!

ASP.NET MVC: Creating Feed Wall

Here is a simple direct approach in creating a feed wall which is similar to the wall in facebook except for the omission of the posting of statuses. i’ve created this because i wanted to present my data from a feed in a wall like approach.

First is we need to create a control that will be the parent for our wall (e.g. EventFeedsControl).

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/javascript" src="<%= Url.Content("../../Scripts/Views/Shared/pk.eventfeeds.js") %>"></script>
<div class="div-event-feeds">
    <a href="#" id="btnEventFeed" class="dark_link">
        <span class="ui-icon ui-icon-signal-diag float-left button_link"></span>
        <label>Event Feed</label>
    </a>
    <ul id="update" class="ui-unordered-list-bullet-less"></ul>
    <a href="#" class="button_link ui-state-default" id="loadOlder">
        <label>Load Older</label>
    </a>
</div>

Next is to create the javascripts used by the control, note that make sure to point the above sample code’s js source to the correct path you would be using.


//Bind events
$(document).ready(function () {
    $("#btnEventFeed").click(function () {
        loadNotes();
    });

    $("#loadOlder").click(function () {
        loadMore();
    });

    $("#loadOlder").hover(
	function(){ 
		$(this).addClass("ui-state-hover"); 
	},
	function(){ 
		$(this).removeClass("ui-state-hover"); 
	}
)
});

//load all notes or latest
function loadNotes() {
    $.ajax({
        type: "POST",
        url: 'UploadPhotos/GetNotes',
        data: {},
        cache: false,
        dataType: "json",
        success: function (data) {
            $("ul#update").prepend(data.Html);
            $("ul#update li").slideDown("slow");
        },
        error: function (XMLHttpRequest) {
            var errorMsg = XMLHttpRequest.statusText;
        },
        complete: function (jsonData) {
            //TODO:
        }
    });
};
//end loadNotes

//load all notes or latest
function loadMore() {
    //find oldest id displayed
    var Id = $("ul#update li:last");
    if (Id[0]) {
        Id = Id.attr("id");
    } else {
        Id = 0;
    }
    //ajax call
    $.ajax({
        type: "POST",
        url: 'UploadPhotos/LoadMore',
        data: { Id : Id},
        cache: false,
        dataType: "json",
        success: function (data) {
            $("ul#update").append(data.Html);
            $("ul#update li").slideDown("slow");
        },
        error: function (XMLHttpRequest) {
            var errorMsg = XMLHttpRequest.statusText;
        },
        complete: function (jsonData) {
            //TODO:
        }
    });
};
//end LoadMore

Next is to create the control which will house the feeds (e.g. EventFeedControl). In this sample i’ve simply used a string.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Web.ViewModel.UploadPhotosViewModel>>" %>
<% foreach(var item in Model){ %>   
<li id="<%= item.Test %>">
    <div>
        <%= item.Test %>     
    </div>
</li>
<% } %>

Next is the controller, helpers and viewmodel codes

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Web.Helper;
using Web.ViewModel;

namespace Web.Controllers
{
    public class UploadPhotosController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public JsonResult Upload(HttpPostedFileBase file)
        {
            return Json(new { });
        }

        public JsonResult GetNotes()
        {
            var events = new List<UploadPhotosViewModel>();
            events.Add( new UploadPhotosViewModel() { Test = Guid.NewGuid().ToString() });

            return Json(new
            {
                Html = this.RenderPartialView("EventFeedControl", events)
            }, JsonRequestBehavior.AllowGet);
        }

        public JsonResult LoadMore(string Id)
        {
            var events = new List<UploadPhotosViewModel>();
            events.Add(new UploadPhotosViewModel() { Test = Guid.NewGuid().ToString() });

            return Json(new
            {
                Html = this.RenderPartialView("EventFeedControl", events)
            }, JsonRequestBehavior.AllowGet);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.ViewModel
{
    public class UploadPhotosViewModel
    {
        public string Test { get; set; }
    }
}

Helper class to render partial view in controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;

namespace Web.Helper
{
    public static class ControllerHelper
    {
        public static string RenderPartialView(this Controller controller, string viewName, object model)
        {
            if (string.IsNullOrEmpty(viewName))
                viewName = controller.ControllerContext.RouteData.GetRequiredString("action");

            controller.ViewData.Model = model;
            using (var sw = new StringWriter())
            {
                ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
                var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
                viewResult.View.Render(viewContext, sw);

                return sw.GetStringBuilder().ToString();
            }
        }
    }
}

Now simply add this to your page and your done.

<% Html.RenderPartial("EventFeedsControl"); %>

Lastly, Note that this runs in a jqueryUI ready asp.net mvc project. Hope this helps! Thanks.

ASP.NET MVC : Multiple file uploads using Uploadify and JQueryUI ProgressBar

I was on a curiosity adventure when I did this one, I wondered whether I could imitate facebook.com multiple photo uploading using ASP.NET MVC. The results where good enough for me to post this on my blog :]. This article would discuss how to create a multiple photo uploader using Uploadify and QueryUI ProgressBar.

First is create an ASP.NET MVC project. Then attach the JQueryUI and Uploadify objects as required. You could download them here.(JQueryUI and Uploadify).

Next is attached the objects in the Site.Master for re usability as shown below:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
    <%--Uploadify and JQUeryUI--%>
    <link href="<%= Url.Content("~/Scripts/Uploadify/uploadify.css") %>" rel="stylesheet" type="text/css" />
    <link href="<%= Url.Content("~/Scripts/JQueryUI/css/ui-lightness/jquery-ui-1.8.16.custom.css") %>" rel="stylesheet" type="text/css" />

    <script type="text/javascript" src="<%= Url.Content("~/Scripts/jquery-1.6.2.min.js") %>"></script>
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/JQueryUI/js/jquery-ui-1.8.16.custom.min.js") %>"></script>
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/Uploadify/swfobject.js") %>"></script>
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/Uploadify/jquery.uploadify.v2.1.4.min.js") %>"></script>
     <%--End Uploadify and JQUeryUI--%>
    <asp:ContentPlaceHolder ID="JavaScriptContent" runat="server" />
</head>

Next is to create a js script for the home view, I’ve seperated the js from the view to allow separation of js code from the view.

$(document).ready(function () {
    $("#multipleFiles").uploadify({
        'uploader': '/Scripts/Uploadify/uploadify.swf',
        'script': '/Home/Upload',
        'fileDataName': 'file',
        'fileDesc': 'Web Image Files (.JPG, .GIF, .PNG)',
        'fileExt': '*.jpg;*.gif;*.png',
        'buttonText': 'Upload Photos',
        'multi': true,
        'sizeLimit': 1048576,
        'simUploadLimit': 1,
        'cancelImg': '/Scripts/Uploadify/cancel.png',
        'auto': true,
        'height': 30,
        'queueID': 'fileQueue',
        'onError': function (a, b, c, d) {
            if (d.status == 404)
               alert("Could not find upload script. Use a path relative to: " + "<?= getcwd() ?>");
            else if (d.type === "HTTP")
                alert("error " + d.type + ": " + d.status);
            //else if (d.type === "File Size")
            //   alert(c.name + " " + d.type + " Limit: " + Math.round(d.info / (1024 * 1024)) + "MB");
            //else
            //    alert("error " + d.type + ": " + d.text);
        },
        'onComplete': function (event, queueId, fileObj, response, data) {
            //Do Nothing Yet
            var result = $.parseJSON(response);
            $("#progressbar").progressbar("value", result.Percentage);
        },
        'onSelectOnce': function (event, data) {
            $.ajax({
                type: 'POST',
                url: '/Home/SetUploadCount',
                data: { TotalCount: data.filesSelected },
                dataType: 'json',
                success: function (data) {
                },
                error: function (XMLHttpRequest) {
                    var errorMsg = XMLHttpRequest.statusText;
                },
                complete: function (jsonData) {
                }
            });
        }
    });

    $("#progressbar").progressbar();
});  

Then we would re-factor the Home view as shown below, Notice that I’ve created the JavaScriptContent placeholder to hold js reference for page specific js scripts. Also note that you can adjust the visibility of the Uploadify queue by adding the display none style. Don’t forget to add the js you created above on the view.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderID="JavaScriptContent" runat="server">
    <script type="text/javascript" src="<%= Url.Content("../../Scripts/home.js") %>"></script>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <div style="width: 50%;">
         <input type="file" id="multipleFiles" style="width: 50%;"/><div style="width: 77%;float:right" id="progressbar"></div>
    </div>
    <div id="fileQueue" ></div> <%--style="display:none"--%>
</asp:Content>

Now will add the controller actions used by the Uploadify, note that the method VariousQuality is commented out. I’ve just tested an Image quality compressing approach written here which I’ve used to analyze the effects of compression to an image. Also note that the “/Uploads/” path is missing in the sample solution.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing;

namespace UploadifyTest.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        private static float _uploadCount { get; set; }
        private static float _totalCount { get; set; }

        public ActionResult Index()
        {
            return View();
        }

        public JsonResult Upload(HttpPostedFileBase file)
        {
            var uploadFile = file;
            //do business logic here
            //Also try considering compressing the file -> VariousQuality(Image.FromStream(file.InputStream, true, true));

            var percentage = default(float);

            if (_totalCount > 0) 
            {
                _uploadCount += 1;
                percentage = (_uploadCount / _totalCount) * 100;
            }

            return Json(new
            {
                Percentage = percentage
            });
        }

        public JsonResult SetUploadCount(int TotalCount)
        {
            _totalCount = TotalCount;
            _uploadCount = 0;

            return Json(new
            {
                Count = TotalCount
            });
        }

        private void VariousQuality(Image original)
        {
            ImageCodecInfo jpgEncoder = null;
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == ImageFormat.Jpeg.Guid)
                {
                    jpgEncoder = codec;
                    break;
                }
            }
            
            if (jpgEncoder != null)
            {
                Encoder encoder = Encoder.Quality;
                EncoderParameters encoderParameters = new EncoderParameters(1);

                for (long quality = 10; quality <= 100; quality += 10)
                {
                    EncoderParameter encoderParameter = new EncoderParameter(encoder, quality);
                    encoderParameters.Param[0] = encoderParameter;

                    string fileOut = Path.Combine("/Uploads/", "quality_" + quality + ".jpeg");
                    FileStream ms = new FileStream(fileOut, FileMode.Create, FileAccess.Write);
                    original.Save(ms, jpgEncoder, encoderParameters);
                    ms.Flush();
                    ms.Close();
                }
            }
        }
    }
}

This should be the output:
Before Loading
While Loading
Loading Complete

If you want to download a sample project, below is a link. It is connected to skydrive so you may want to have a hotmail account. Project is created using VS2010.
https://skydrive.live.com/embedicon.aspx/Shared/UploadifyTest.zip?cid=34106e8cbacbd842&sc=documents

ASP.NET MVC, C#: Custom jqGrid object based column definitions

There are not much reference that I’ve seen with regards to customizing a jqGrid based on an object. This sample will allow users to create a customize jqGrid where the grid and column definitions are defined within the c# codebehind. The sample would require download of jqGrid version 4.1.2 and jquery-ui which is required for jqGrid 3.5 and above.

First step is to create a ASP.NET MVC Project – Let’s name it as StepByStepJQGridGuid

Then add the jqGrid and jquery-ui objects to your solution as shown below. jquery-ui objects are marked as blue.

Now we shall add references to this scripts in our page. As a standard, we would be adding the scripts references within the Site.Master, hence allowing the jqGrid scripts to be used by any page inheriting from the Site.Master. add the references within the header tag of the Site.Master page as shown below

<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
    <%-- Add Code --%>
    <link href="../../Scripts/css/ui.jqgrid.css" rel="stylesheet" type="text/css" />
    <link href="../../Scripts/css/ui.multiselect.css" rel="stylesheet" type="text/css" />
    <link href="../../Scripts/css/jquery-ui-1.8.7.css" rel="stylesheet" type="text/css" />
    <script src="../../Scripts/jquery-1.5.2.min.js" type="text/javascript"></script>
    <script src="../../Scripts/js/i18n/grid.locale-en.js" type="text/javascript"></script>
    <script src="../../Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>
     <%-- End Add Code --%>
</head>

Next is we would be injecting a jqGrid in the page. For this sample we would be modifying the index.aspx in the Home View as pre-created in a ASP.NET MVC application.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<StepByStepJQGridGuid.ViewModel.HomeViewModel>" %>
<%@ Import Namespace="StepByStepJQGridGuid.Helper" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%: Html.CreateJQGrid<StepByStepJQGridGuid.ViewModel.ProductViewModel>(Model.GridData)%>
</asp:Content>

As shown above we need a Html.CreateJQGrid helper to create the jqgrid, we also need to import the namespace of the helper class to the page (e.g. <%@ Import Namespace=”StepByStepJQGridGuid.Helper” %>). Below you’ll see that where using a grid model (GridViewModel<T>) which wraps the object that would define the grid column definitions(e.g. column width, alignment etc), the grid model itself defines the jqGrid main definitions (e.g. Id,Caption,Url,Grid Width etc).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
using StepByStepJQGridGuid.ViewModel;

namespace StepByStepJQGridGuid.Helper
{
    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString CreateJQGrid<T>(this HtmlHelper helper, GridViewModel<T> model)
        {
            var htmlBuilder = new StringBuilder();

            htmlBuilder.AppendFormat(@"<table id=""{0}"" class=""scroll"" cellpadding=""0"" cellspacing=""0""></table>", model.Id);
            htmlBuilder.AppendFormat(@"<div id=""{0}Pager"" class=""scroll"" style=""text-align:center;""></div>", model.Id);

            htmlBuilder.AppendFormat(@"<script type=""text/javascript"">");
            htmlBuilder.AppendFormat(@"$(function()");
            htmlBuilder.AppendFormat(@"{{");
            htmlBuilder.AppendFormat(@"$('#{0}').jqGrid({{", model.Id);
            htmlBuilder.AppendFormat(@"url: '{0}',", model.Url);
            htmlBuilder.AppendFormat(@"datatype: 'json',");
            htmlBuilder.AppendFormat(@"mtype: 'POST',");

            //Create Columns
            htmlBuilder.AppendFormat(@"colModel: [");
            foreach (var column in model.Columns)
            {
                htmlBuilder.AppendFormat(@"{{ name: '{0}', 
                                            index: '{0}', 
                                            width: {1}, 
                                            align: '{2}', 
                                            sortable: {3},
                                            hidden: {4}
                }},", column.Name
                    , column.Width > 0 ? column.Width : 80
                    , column.Align == null ? "left" : column.Align.ToLower()
                    , column.IsSortable.ToString().ToLower()
                    , column.IsHidden.ToString().ToLower()
                    );
            }
            htmlBuilder.AppendFormat(@"],");

            htmlBuilder.AppendFormat(@"pager: jQuery('#{0}Pager'),", model.Id);
            htmlBuilder.AppendFormat(@"rowNum: 5,");
            htmlBuilder.AppendFormat(@"rowList: [5, 10, 20, 50],");
            htmlBuilder.AppendFormat(@"viewrecords: true,");
            htmlBuilder.AppendFormat(@"imgpath: '/scripts/themes/coffee/images',");
            htmlBuilder.AppendFormat(@"caption: '{0}',", model.Caption);
            htmlBuilder.AppendFormat(@"width: {0}", model.Width);
            htmlBuilder.AppendFormat(@"}});");
            htmlBuilder.AppendFormat(@"}});");
            htmlBuilder.AppendFormat(@"</script>");


            return MvcHtmlString.Create(htmlBuilder.ToString());
        }
    }
}

Below shows the object represented within the jqGrid, it is designed to contain attributes (e.g. JQGridColumn) which would be used as the column definitions.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using StepByStepJQGridGuid.Utilities;

namespace StepByStepJQGridGuid.ViewModel
{
    public class ProductViewModel
    {
        [JQGridColumn(IsHidden = true)]
        public int ProductID { get; set; }
        [JQGridColumn(Width = 200, Align = "Center")]
        public string Name { get; set; }
        [JQGridColumn(Width = 50, Align = "Left")]
        public string ProductNumber { get; set; }
        [JQGridColumn(Width = 50, Align = "Right")]
        public decimal ListPrice { get; set; }
    }
}

Below is the attribute class, to learn more about attributes visit here.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace StepByStepJQGridGuid.Utilities
{
    [AttributeUsage(AttributeTargets.Property)]
    public class JQGridColumnAttribute : Attribute
    {
        public int Width { get; set; }
        public string Align { get; set; }
        public bool IsSortable { get; set; }
        public bool IsHidden { get; set; }
    }
}

Lastly, is the GridViewModel itself, the class uses reflection to retrieve the attributes from the object.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using StepByStepJQGridGuid.Utilities;

namespace StepByStepJQGridGuid.ViewModel
{
    public class GridViewModel<T>
    {
        public GridViewModel()
        {
            this.Data = new List<T>();
            CreateColumns();
        }

        public string Id { get; set; }
        public string Url { get; set; }
        public string Caption { get; set; }
        public int Width { get; set; }
        public List<T> Data { get; set; }
        public IEnumerable<JQGridColumn> Columns { get; set; }

        private void CreateColumns()
        {
            var modelType = typeof(T);
            var columns = new List<JQGridColumn>();

            var properties = modelType.GetProperties();
            if (properties != null)
            {
                foreach (var property in properties)
                {
                    var column = new JQGridColumn();
                    column.Name = property.Name;

                    var attrs = Attribute.GetCustomAttributes(property);
                    foreach (var attr in attrs)
                    {
                        var a = attr as JQGridColumnAttribute;
                        if (a != null)
                        {
                            column.Width = a.Width;
                            column.IsHidden = a.IsHidden;
                            column.IsSortable = a.IsSortable;
                            column.Align = a.Align;
                        }
                    }
                    columns.Add(column);
                }
            }
            Columns = columns;
        }
    }
}

Now we will proceed with modifying the Homecontroller, as a standard, we would be making the Home View strongly typed to a ViewModel (e.g. HomeViewModel). Then we would be using the ViewModel to pass data from Controller to View.

Below is the ViewModel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace StepByStepJQGridGuid.ViewModel
{
    public class HomeViewModel
    {
        public HomeViewModel() 
        {
            //Grid Definitions
            this.GridData = new GridViewModel<ProductViewModel>();
            this.GridData.Url = "/Home/GridData";
            this.GridData.Id = "ProductSearchGrid";
            this.GridData.Width = 800;
            this.GridData.Caption = "Products";
        }

        public GridViewModel<ProductViewModel> GridData { get; set; }
    }
}

Next we will modify the home controller as shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using StepByStepJQGridGuid.ViewModel;

namespace StepByStepJQGridGuid.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public HomeController() 
        {
        }

        public ActionResult Index()
        {
            var model = new HomeViewModel();

            if(Session["cacheProducts"] == null)
                Session["cacheProducts"] = CreateDummyProducts(500).OrderBy(x => x.ProductID).ToList();
            
            return View(model);
        }

        public ActionResult GridData(string sidx, string sord, int page, int rows)
        {
            var cacheProducts = Session["cacheProducts"] as List<ProductViewModel>;
            var totalRecords = cacheProducts.Count;
            var products = cacheProducts.Skip((page - 1) * rows).Take(rows);

            var pageIndex = Convert.ToInt32(page) - 1;
            var pageSize = rows;
            var totalPages = (int)Math.Ceiling(totalRecords / (float)pageSize);
 
            var jsonData = new
            {
                total = totalPages, 
                page = page,
                records = totalRecords, 
                rows = (
                    from p in products
                    select new {
                        i = p.ProductID,
                        cell = new[] { p.ProductID.ToString(), p.Name, p.ProductNumber, p.ListPrice.ToString() }
                    }).ToArray()
            };

            return Json(jsonData);
        }

        public ActionResult About()
        {
            return View();
        }

        private List<ProductViewModel> CreateDummyProducts(int count) 
        {
            var products = new List<ProductViewModel>();
            for (var i = 0; i < count; i++) 
            {
                var product = new ProductViewModel();
                product.ProductID = i + 1;
                product.Name = "Product" + i.ToString() + "_" + Guid.NewGuid().ToString();
                product.ProductNumber = "PN" + i.ToString();
                product.ListPrice = i * 21;
                products.Add(product);
            }
            return products;
        }
    }
}

To try it out yourself, download a copy of the test project below, Thanks!
https://skydrive.live.com/embedicon.aspx/Shared/StepByStepJQGridGuid.zip?cid=34106e8cbacbd842&sc=documents

Reporting Services: Creating A Dynamic Column Tablix Report using RDL Generator

One approach I’ve learned just recently in creation of Reports with Dynamic column is by using a Streamed report created programmatically using a RDL Generator class as describe in this link. This tutorial will teach you how to create a dynamic column tablix report rdlc using the RDL Generator.

First is create the RDL Generator Class, this will serve as the creator of the rdl in xml format.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace SampleRDLGenerator
{
    public class RDLGenerator
    {
        public RDLGenerator()
        {
        }

        //Enter Methods Here
    }
}

Add method to Create the document and the report base

public XmlDocument CreateDocument()
{
	XmlDocument doc = new XmlDocument();

	var sb = new StringBuilder();
	sb.Append("<report ");
	sb.AppendFormat("xmlns:rd=\"{0}\" ", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");
	sb.AppendFormat("xmlns=\"{0}\">", "http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition");
	sb.Append("</Report>");
	string xmlData = sb.ToString();
	doc.Load(new StringReader(xmlData));

	return doc;
}
public XmlElement CreateReportBase(XmlDocument doc)
{
	XmlElement report;
	XmlElement dataSources;
	XmlElement dataSource;
	XmlElement connectionProperties;

	report = (XmlElement)doc.FirstChild;
	AddElement(report, "AutoRefresh", "0");
	AddElement(report, "ConsumeContainerWhitespace", "true");

	dataSources = AddElement(report, "DataSources", null);
	dataSource = AddElement(dataSources, "DataSource", null);
	AddAttribute(dataSource, doc, "Name", "SandsReportReportModels");

	connectionProperties = AddElement(dataSource, "ConnectionProperties", null);
	AddElement(connectionProperties, "DataProvider", "System.Data.DataSet");
	AddElement(connectionProperties, "ConnectString", "/* Local Connection */");

	return report;
}

Now add method that will create the data set from a list of field names

public void CreateReportDataSet(XmlElement report, XmlDocument doc, string dataSetName, List<ReportColumn> propertyList)
{
	XmlElement dataSets;
	XmlElement dataSet;
	XmlElement fields;
	XmlElement query;

	//DataSets element
	dataSets = AddElement(report, "DataSets", null);
	dataSet = AddElement(dataSets, "DataSet", null);
	AddAttribute(dataSet, doc, "Name", dataSetName);

	fields = AddElement(dataSet, "Fields", null);

	foreach (var property in propertyList.OrderBy(x => x.Order))
	{
		CreateDataSetFields(fields, doc, property.Name);
	}

	//Query
	query = AddElement(dataSet, "Query", null);
	AddElement(query, "DataSourceName", "ReportModels");
	AddElement(query, "CommandText", "/* Local Query */");
}

public void CreateDataSetFields(XmlElement fields, XmlDocument doc, string fieldName)
{
	XmlElement field;
	field = AddElement(fields, "Field", null);
	AddAttribute(field, doc, "Name", fieldName);
	AddElement(field, "DataField", fieldName);
	AddRdElement(field, "rd:TypeName", "System.String");
}

Then create the report body

public XmlElement CreateReportBody(XmlElement report, XmlDocument doc, string dataSetName, List<ReportColumn> propertyList)
{
	XmlElement tablix;
	XmlElement tablixBody;
	XmlElement tablixColumns;
	XmlElement tablixRows;
	XmlElement tablixCellHeaders;
	XmlElement tablixCellRows;
	XmlElement reportItems;
	int columnCount = propertyList.Count;

	//Create Report Body And Tablix
	reportItems = CreateReportItems(report, doc);
	tablix = CreateTablix(reportItems, doc, dataSetName, propertyList);

	//Create Tablix Columns And Rows
	tablixBody = AddElement(tablix, "TablixBody", null);
	tablixColumns = AddElement(tablixBody, "TablixColumns", null);
	foreach (var property in propertyList.OrderBy(x => x.Order))
	{
		CreateColumn(tablixColumns, property.Width);
	}
	tablixRows = AddElement(tablixBody, "TablixRows", null);
	tablixCellHeaders = CreateRow(tablixRows);
	tablixCellRows = CreateRow(tablixRows);
	foreach (var property in propertyList.OrderBy(x => x.Order))
	{
		CreateHeaderCell(tablixCellHeaders, doc, property.Name, property.HeaderName);
		CreateRowCell(tablixCellRows, doc, property.Name);
	}

	return report;
}
public XmlElement CreateReportItems(XmlElement report, XmlDocument doc)
{
	XmlElement body;
	XmlElement reportItems;
	XmlElement page;

	page = AddElement(report, "Page", null);
	//landscape
	AddElement(page, "PageHeight", "8.5in");
	AddElement(page, "PageWidth", "11in");
	AddElement(page, "LeftMargin", "1in");
	AddElement(page, "RightMargin", "1in");
	AddElement(page, "TopMargin", "1in");
	AddElement(page, "BottomMargin", "1in");
	AddElement(page, "Style", null);

	AddElement(report, "Width", "11in");
	body = AddElement(report, "Body", null);
	AddElement(body, "Height", "1.85546in");
	reportItems = AddElement(body, "ReportItems", null);

	return reportItems;
}
public XmlElement CreateTablix(XmlElement reportItems, XmlDocument doc, string dataSetName, List<ReportColumn> propertyList)
{
	XmlElement tablix;
	XmlElement tablixColumnHierarchy;
	XmlElement tablixMembers;
	XmlElement tablixMember;
	XmlElement tablixRowHierarchy;
	XmlElement group;
	XmlElement tablixMembersNested;

	tablix = AddElement(reportItems, "Tablix", null);
	AddAttribute(tablix, doc, "Name", "Tablix1");
	AddElement(tablix, "DataSetName", dataSetName);
	AddElement(tablix, "Top", "0.5in");
	AddElement(tablix, "Left", "0.5in");
	AddElement(tablix, "Height", "0.5in");
	AddElement(tablix, "Width", "3in");

	tablixColumnHierarchy = AddElement(tablix, "TablixColumnHierarchy", null);
	tablixMembers = AddElement(tablixColumnHierarchy, "TablixMembers", null);
	foreach (var property in propertyList.OrderBy(x => x.Order))
	{
		CreateTablixMember(tablixMembers, property.Name);
	}

	tablixRowHierarchy = AddElement(tablix, "TablixRowHierarchy", null);
	tablixMembers = AddElement(tablixRowHierarchy, "TablixMembers", null);
	tablixMember = AddElement(tablixMembers, "TablixMember", null);
	AddElement(tablixMember, "KeepWithGroup", "After");
	AddElement(tablixMember, "KeepTogether", "true");

	tablixMember = AddElement(tablixMembers, "TablixMember", null);
	AddElement(tablixMember, "DataElementName", "Detail_Collection");
	AddElement(tablixMember, "DataElementOutput", "Output");
	AddElement(tablixMember, "KeepTogether", "true");

	group = AddElement(tablixMember, "Group", null);
	AddAttribute(group, doc, "Name", "Table1_Details_Group");
	AddElement(group, "DataElementName", "Detail");

	tablixMembersNested = AddElement(tablixMember, "TablixMembers", null);
	AddElement(tablixMembersNested, "TablixMember", null);

	return tablix;
}
public XmlElement CreateColumn(XmlElement tablixColumns, string width)
{
	XmlElement tablixColumn;

	tablixColumn = AddElement(tablixColumns, "TablixColumn", null);
	AddElement(tablixColumn, "Width", width);

	return tablixColumn;
}
public XmlElement CreateRow(XmlElement tablixRows)
{
	XmlElement tablixRow;
	XmlElement tablixCells;

	tablixRow = AddElement(tablixRows, "TablixRow", null);
	AddElement(tablixRow, "Height", "0.27373in");
	tablixCells = AddElement(tablixRow, "TablixCells", null);

	return tablixCells;
}
public void CreateTablixMember(XmlElement tablixMembers, string propertyname)
{
	XmlElement tablixMember;
	tablixMember = AddElement(tablixMembers, "TablixMember", null);
}

the next methods create the cells for the tablix

public void CreateRowCell(XmlElement tablixCells, XmlDocument doc, string fieldname)
{
	XmlElement tablixCell;
	XmlElement cellContents;
	XmlElement textbox;
	XmlElement paragraphs;
	XmlElement paragraph;
	XmlElement textRuns;
	XmlElement textRun;
	XmlElement style;
	XmlElement border;

	tablixCell = AddElement(tablixCells, "TablixCell", null);
	cellContents = AddElement(tablixCell, "CellContents", null);
	textbox = AddElement(cellContents, "Textbox", null);
	AddAttribute(textbox, doc, "Name", fieldname);
	AddElement(textbox, "KeepTogether", "true");
	paragraphs = AddElement(textbox, "Paragraphs", null);
	paragraph = AddElement(paragraphs, "Paragraph", null);
	textRuns = AddElement(paragraph, "TextRuns", null);
	textRun = AddElement(textRuns, "TextRun", null);
	AddElement(textRun, "Value", "=Fields!{0}.Value".Set(fieldname));

	style = AddElement(textRun, "Style", null);
	style = AddElement(textbox, "Style", null);
	border = AddElement(style, "Border", null);
	AddElement(border, "Color", "LightGrey");
	AddElement(border, "Style", "Solid");
	AddElement(style, "PaddingLeft", "2pt");
	AddElement(style, "PaddingRight", "2pt");
	AddElement(style, "PaddingTop", "2pt");
	AddElement(style, "PaddingBottom", "2pt");
}
public void CreateHeaderCell(XmlElement tablixCells, XmlDocument doc, string fieldName, string fieldHeaderName)
{
	XmlElement tablixCell;
	XmlElement cellContents;
	XmlElement textbox;
	XmlElement paragraphs;
	XmlElement paragraph;
	XmlElement textRuns;
	XmlElement textRun;
	XmlElement style;
	XmlElement border;

	tablixCell = AddElement(tablixCells, "TablixCell", null);
	cellContents = AddElement(tablixCell, "CellContents", null);
	textbox = AddElement(cellContents, "Textbox", null);
	AddAttribute(textbox, doc, "Name", "Header{0}".Set(fieldName));
	AddElement(textbox, "KeepTogether", "true");
	paragraphs = AddElement(textbox, "Paragraphs", null);
	paragraph = AddElement(paragraphs, "Paragraph", null);
	textRuns = AddElement(paragraph, "TextRuns", null);
	textRun = AddElement(textRuns, "TextRun", null);
	AddElement(textRun, "Value", "{0}".Set(fieldHeaderName));

	style = AddElement(textRun, "Style", null);
	style = AddElement(textbox, "Style", null);
	border = AddElement(style, "Border", null);
	AddElement(border, "Color", "LightGrey");
	AddElement(border, "Style", "Solid");
	AddElement(style, "PaddingLeft", "2pt");
	AddElement(style, "PaddingRight", "2pt");
	AddElement(style, "PaddingTop", "2pt");
	AddElement(style, "PaddingBottom", "2pt");
	AddElement(style, "BackgroundColor", "PaleTurquoise");
}

the next methods are utility methods used for creating the report xml

public XmlElement AddElement(XmlElement parent, string name, string value)
{
	XmlElement newelement = parent.OwnerDocument.CreateElement(name,
		"http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition");
	parent.AppendChild(newelement);
	if (value != null) newelement.InnerText = value;
	return newelement;
}
public XmlElement AddRdElement(XmlElement parent, string name, string value)
{
	XmlElement newelement = parent.OwnerDocument.CreateElement(name,
		"http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");
	parent.AppendChild(newelement);
	if (value != null) newelement.InnerText = value;
	return newelement;
}
public XmlAttribute AddAttribute(XmlElement parentElement, XmlDocument doc, string attributeName, string attributeValue)
{
	XmlAttribute attr = parentElement.Attributes.Append(doc.CreateAttribute(attributeName));
	attr.Value = attributeValue;

	return attr;
}

Now to use this RDLGenerator Class

public void CreateDynamicColumnRDL(string datasetName, IEnumerable<object> objectList)
{
	var rdlGenerator = new RDLGenerator();

	//Retrive field/property name list from objectList
	//this could be done through reflection that i would discuss in another topic
	var fieldList = ConvertHelper.RetrivePropertyNameList(objectList);

	var doc = rdlGenerator.CreateDocument();
	var report = rdlGenerator.CreateReportBase(doc);
	rdlGenerator.CreateReportDataSet(report, doc, datasetName, fieldList);
	rdlGenerator.CreateReportBody(report, doc, datasetName, fieldList);

	//Convert to Stream
	byte[] myByteArray = System.Text.Encoding.UTF8.GetBytes(doc.OuterXml);
	MemoryStream ms = new MemoryStream(myByteArray);

	//Supply Stream to ReportViewer
	ReportViewer1.LocalReport.LoadReportDefinition(ms);
	//Supply DataSource
	ReportViewer1.LocalReport.DataSources.Add(new ReportDataSource(datasetName, objectList));
	//Refresh Report Viewer
	ReportViewer1.LocalReport.Refresh();
}

Here is the structure for the report column

    public class ReportColumn
    {
        public string Name { get; set; }
        public string HeaderName { get; set; }
        public string Width { get; set; }
        public int Order { get; set; }
        public string FieldType { get; set; }
    }

Here is a snapshot of code within the ConvertHelper List. Basically it retrive a list of ReportColumn to publish to the rdl generator, the code below has other dependecies such as retriving the report column definitions “_reportDefinition” which is not covered anymore in this post. to completely cuztomize this for your own use, you would need knowledge in .Net reflection. but the idea simply is convert the list of objects with attributes into a list of reportcolumn to be passed to the rdl generator.

“Disclaimer: Convert Helper below is just a sample, you’ll need to implement your own reflection approach in retriving the list of reportcolumn based on your object.”

 public static List<ReportColumn> RetrivePropertyNameList(string dataSetname, IEnumerable<object> Data)
        {
            try
            {
                var model = Data.FirstOrDefault();
                if (model != null)
                {
                    var modelType = model.GetType();
                    var propertyInfo = modelType.GetProperties();
                    var propertyList = new List<ReportColumn>();
                    var propertyCount = propertyInfo.Count();
                    var reportDefinitionColumns = _reportDefinition.ReportColumns;

                    var orderCount = 0;
                    foreach (var property in propertyInfo)
                    {
                        var reportColumn = new ReportColumn();
                        var columnName = property.Name;

                        var columnInDefinition = reportDefinitionColumns.Where(x => x.Id == columnName).FirstOrDefault();

                        if (columnInDefinition != null)
                        {
                            if (orderCount != 0 && columnInDefinition.Order > orderCount)
                            {
                                orderCount += 1;
                                reportColumn.Order = orderCount;
                            }
                            else 
                            {
                                reportColumn.Order = columnInDefinition.Order;
                            }

                            reportColumn.Width = columnInDefinition.Width;
                            
                            reportColumn.HeaderName = columnInDefinition.Header;
                            reportColumn.Name = columnInDefinition.Id;
                            reportColumn.FieldType = columnInDefinition.FieldType ?? Constants.ReportFieldType.String;

                            propertyList.Add(reportColumn);
                        }
                        else 
                        {
                            columnInDefinition = reportDefinitionColumns.Where(x => columnName.Contains(x.Id)).FirstOrDefault();

                            if (columnInDefinition != null) 
                            {
                                if (orderCount == 0)
                                {
                                    orderCount = columnInDefinition.Order;
                                    reportColumn.Order = columnInDefinition.Order;
                                }
                                else 
                                {
                                    orderCount += 1;
                                    reportColumn.Order = orderCount;
                                }

                                reportColumn.Width = columnInDefinition.Width;

                                var toReplace = "{0}_".Set(columnInDefinition.Id);
                                reportColumn.HeaderName = columnName.Replace(toReplace, "").Replace("_"," ");
                                reportColumn.Name = columnName;
                                reportColumn.FieldType = columnInDefinition.FieldType ?? Constants.ReportFieldType.String;

                                propertyList.Add(reportColumn);
                            }
                        }
                    }

                    return propertyList;
                }
            }
            catch (Exception exception)
            {
                Console.WriteLine("An error occurred: " + exception.Message);
            }

            return default(List<ReportColumn>);
        }

Conclusion?

Just simply copy all methods to RDLGenerator create the whole class.

Lastly to note was this is created as a generic report which would display data from a gridview,
to customize the code would require familiarizartion on the xml schema of the sql rdl itself which I’ve done through opening the rdl file in xml editor view. anyway, hope this helps anyone! thanks!

2012-04-02 – Added the report column object for reference. Thanks to 4son!
2012-04-28 – Added a sample on retrieving the report column list from an object through reflection.

Repository Pattern – Mocking with Entity Framework 4.0: A Generic Approach

Today I will present a mocking approach I have applied for Entity Framework 4.0. Unlike other Repository Pattern, The approach I’ve created is more generic in terms that you wouldn’t need to create specific concrete class that would implement IRepository Interface. For more details about Repository and Unit of work pattern please refer to this.

Why use Repository Pattern?
The reason is simple, It gives the ability to mock an ObjectContext, where in our example, is entity framework 4.0

First off, we need to define the IRepository interface. The example below will allow user the basic CRUDE operations interface.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Model.Model.Contracts
{
    public interface IRepository
    {
        IQueryable<TEntity> GetEntitySet<TEntity>() where TEntity : class;
        int SaveChanges();
        int SaveChangesPersist();
        void AcceptAllChanges();
        void AddObject<TEntity>(TEntity entity) where TEntity : class;
        object GetObjectByKey(string entityKey);
    }
}

Next is we need to construct the base repository to be used which would implement the IRepository pattern. This base repository class will handle all Repository Concrete Implementation instead of the usual creation of Repository concrete classes for each Data Entity, therefor would help create a more leaner approach on repository pattern.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects;
using Model.Model.Contracts;
using System.Xml.Serialization;
using System.Data;
using System.IO;

namespace Model.Model.Shared
{
    public class RepositoryBase : IRepository
    {
        ObjectContext _dtx;
        public RepositoryBase(ObjectContext dtx)
        {
            _dtx = dtx;
            _dtx.CommandTimeout = 1800;
        }

        public IQueryable<TEntity> GetEntitySet<TEntity>() where TEntity : class
        {
            return _dtx.CreateObjectSet<TEntity>();
        }

        public int SaveChanges()
        {
            return _dtx.SaveChanges();
        }

        public int SaveChangesPersist()
        {
            return _dtx.SaveChanges(false);
        }

        public void AcceptAllChanges()
        {
            _dtx.AcceptAllChanges();
        }

        public void AddObject<TEntity>(TEntity entity) where TEntity : class
        {
            var objectSet = _dtx.CreateObjectSet<TEntity>();

            if (objectSet != null)
            {
                objectSet.AddObject(entity);
            }
            else
            {
                throw new Exception("EntitySet is not found.");
            }
        }

        public object GetObjectByKey(string entityKeyStr)
        {
            var serializer = new XmlSerializer(typeof(EntityKey));
            var reader = new StringReader(entityKeyStr);
            var entityKey = serializer.Deserialize(reader) as EntityKey;

            if (entityKey != null)
            {
                return _dtx.GetObjectByKey(entityKey);
            }

            return default(object);
        }
    }
}

Now to use this is simple, I’ve created a sample class below that would retrieve data from a AdventureWorks created EDMX.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Model.Model.Contracts;
using Model.Model.Shared;
using Model.Utilities.Extensions;

namespace Model.Model.AdventureWorks
{
    public class ProductModel : IProductModel
    {
        IRepository _dtx;
        public ProductModel()
        {
            _dtx = new RepositoryBase(new AdventureWorksEntities());
        }
        public ProductModel(IRepository dtx)
        {
            _dtx = dtx;
        }

        public IEnumerable<Product> GetProductList(int pageNumber, int pageSize, out int totalRecords)
        {
            var products = from p in _dtx.GetEntitySet<Product>()
                           select p;

            totalRecords = products.Count();
            products = products.OrderBy(x => x.ProductID).Page(pageNumber, pageSize);

            return products.ToList();
        }
    }

   public interface IProductModel
   {
        IEnumerable<Product> GetProductList(int pageNumber, int pageSize, out int totalRecords);
   }
}

Now we can freely create Mock Repositories for our testing. Following example uses a mocking framework called Moq. This code demontrates how to create a Mock object from the IRepository interface.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SampleMVCApplication.Controllers;
using SampleMVCApplication.Tests.Helpers;
using Model.Model.Contracts;
using System.Linq;
using Model;
using System.Collections.Generic;

namespace SampleMVCApplication.Tests.Controllers
{
    [TestClass]
    public class AdventureWorksControllerTest
    {
        static Mock<IRepository> _mockRepository = new Mock<IRepository>();
        static Mock<IProductModel> _mockModel = new Mock<IProductModel>();

        [TestInitialize]
        public void InitializeTest()
        {
            _mockModel.SetupAllProperties();
            _mockRepository.SetupAllProperties();
        }

        [TestMethod]
        public void InvokeGridData()
        {
            var totalRecords = 0;
            var pageSize = 5;
            var pageNum = 1;

            var controller = new AdventureWorksController(_mockModel.Object);
            _mockModel.Setup(x => x.GetProductList(pageNum, pageSize, out totalRecords)).Returns(FakeProducts());
            var result = controller.GridData(It.IsAny<string>(), It.IsAny<string>(), pageNum, pageSize);

            Assert.IsNotNull(result);
        }

        private static IQueryable<Product> FakeProducts()
        {
            var productList = new List<Product>();
            productList.Add(new Product() { ProductID = 1, Name = "testName", ListPrice = 10, ProductNumber = "Test101" });
            return productList.AsQueryable();
        }
    }
}

Conclusion?
The example above illustrate’s how easy it is to create a Mockable Entity Framework for C#. Thanks for reading. Hope it helps!