Skip to main content Skip to footer

Migrating LightSwitch To HTML5: A look at ASP.NET Core Scaffolders and Code Generators

Microsoft recently announced that Visual Studio 2015 would be the last version to support LightSwitch and advised developers not to start new development using this platform. This has left many LOB developers looking for a stable, well-supported platform to replace LightSwitch, and moving from LightSwitch to HTML5 is one of the most suitable paths.

In this two-part series, we'll look at how to create a simple data-driven app in HTML5 using Angular and ASP.NET MVC. In this blog, we'll focus on ASP.NET Core.

Angular vs. ASP.NET MVC: Benefits of Migrating LightSwitch Apps

A client-side technology such as Angular is a natural choice, as it offers:

  • Structure to JavaScript
  • Templating
  • Two-way data-binding
  • Great for SPA (Single-Page Applications)
  • Modular development

For someone coming from LightSwitch background, though, it's still missing some features. LightSwitch offered designers and screens to quickly create LOB applications, and while Angular is easy, it presents a learning curve for someone coming from server side C#, and it involves writing some code for simple apps.

Server side ASP.NET has come a long way. It provides easy-to-use tools, and builds scalable business applications. Feature highlights include:

  • Scaffolding, which helps to generate screens based on the model
  • A strong HTML5 editor, which makes writing HTML code a breeze
  • TagHelpers in ASP.NET Core, which you can think of as XAML on the web
  • TypeScript for client-side scripting
  • Entity Framework, which helps with quick model generation

Create a Simple App in ASP.NET Core

Let's start with a simple app in ASP.NET Core. This walkthrough uses VS2015.

Our requirements include:

  • A screen that displays product information inside a grid
  • User can edit, delete, page and filter the grid data
  • A grid that has conditional formatting for a certain threshold of stock

We'll use the following ComponentOne Studio ASP.NET MVC Edition features and products:

Step 1: Create the Project

  • In Visual Studio, Click on File > New Project.
  • Under Web Node, select "C1 ASP.NET Core MVC Web Application" template.
  • Give a name to the project and click OK.
  • In the Project wizard, check "Enable client IntelliSense" and check "Add TS file"

Wizard Wizard

  • Click OK to complete the project creation.

Step 2: Add Licensing

Click on Tools > GrapeCity License Manager to open the the GrapeCity License Manager Add-In. If you're not already logged in, you'll need to sign in with the GrapeCity credentials:

SignIn SignIn

Next, Click on the "Generate App(runtime) License" button:

license

In the next window, use the dropdown to select a registered C1 license key or Eval and hit "Generate App(runtime) License" button.

Generate License Generate License

This generates an XML file with a unique GUID and adds it to the project.

Step 3: Add Data

Data models can be added to the project in various ways; you can create POCO classes or use Entity Framework. In this example, we'l be using the new Entity Framework Core 1.0, which directly interacts with an existing database. Entity Framework can quickly scaffold models out of database using the "Scaffold-DbContext" command via NuGet Package Manager Console.

  • Go to tools > NuGet Package Manager > NuGet Package Manager Console
  • Type the following command:

      Scaffold-DbContext "Data Source=.\\SQLEXPRESS;Database=Northwind;Integrated Security=True;Connect Timeout=30" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models  

Note: This example is using SQLExpress, so you'll need to update the Data Source according to the server you're using. This generates the Models for our Northwind database:

Generated Model Generated Model

Step 4: Add Dependency for Data

Remove the following code from NorthwindContext.cs under Models folder:


  //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
  {  
  //   #warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.  
  optionsBuilder.UseSqlServer(@"Data Source=.\\SQLEXPRESS;Database=Northwind;Integrated Security=True;Connect Timeout=30");  
  }  

Add the following code to NorthwindContext.cs


  public NorthwindContext(DbContextOptions  
<NorthwindContext> options)  
            : base(options)  
        { }  

Open Startup.cs and add dependency for NorthwindContext in the ConfigureServices method:



 var con = @"Server=.\\SQLEXPRESS;Database=Northwind;Integrated Security=True;Connect Timeout=30";  
            services.AddDbContext<NorthwindContext>(option => option.UseSqlServer(con));  

We're done configuring data. Next we'll see how to use scaffolding to quickly generate Screens, Views and related Controller code.

Step 5: Add Screen/View

  • Right-click Controllers folder > Select New Scaffolded Item
  • From the Add Scaffolder window, select C1Scaffolder. Click Add.
  • The C1Scaffolder offers various options: FlexGrid, Input, FlexChart. Select FlexGrid and hit next.

MVC Control Scaffolders MVC Control Scaffolders

The MVC FlexGrid window opens.

On the General tab:

  • Name the Controller as ProductsController
  • Leave the View name as Index
  • For the Model class, select Products from the dropdown
  • For the DataContext, select NorthwindContext from the dropdown
  • Set Id as fg

FlexGrid Scaffolder FlexGrid Scaffolder

Go to the Columns tab:

  • Uncheck Autogenerate Columns.
  • Remove SupplierID and OrderDetail columns using the remove button. Displaying IDs for CategoryID may not be helpful to user, so instead we should display CategoryName for CategoryID. This can be configured easily in this tab.

  • Go to the properties for CategoryID column

  • Expand DataMap property
  • Select "Categories" from Source dropdown
  • Select CategoryID from SelectedValuePath dropdown
  • Select CategoryName from DisplayValuePath dropdown
  • Set "Header" property of the column to "Category"

DataMap DataMap

Go to the Editing Tab:

  • Check Allow Edit.
  • Check Allow Delete.

Go to grouping Tab:

  • Check CategoryID under Group Descriptions.

Go to Filtering Tab:

  • Check Allow Filtering.

Go to Paging Tab:

  • Check Allow Paging.
  • Set Page Size to 20.

Go to ClientEvents Tab:

  • Check FormatItem

Got HtmlAttributes Tab.

  • Set height to 540

Click Add Button. This generates the ProductsController.cs and Index.cshtml(under Views/Products) file. The Product controller class will have the code for CRUD operations:


  public partial class ProductsController : Controller  
  {  
  private C1MvcApplication1.Models.NorthwindContext db;  

  public ProductsController(C1MvcApplication1.Models.NorthwindContext context)  
  {  
  db = context;  
  }  

  public ActionResult Index()  
  {  
  var model = db.Products;  
  return View(model);  
  }  

  public ActionResult FlexGrid_Update([C1JsonRequest]CollectionViewEditRequest </p>  
<C1MvcApplication1.Models.Products> requestData)  
        {  
            return Update(requestData, db.Products);  
        }  

        private ActionResult Update<T>(CollectionViewEditRequest<T> requestData, DbSet<T> data) where T : class  
        {  
            return this.C1Json(CollectionViewHelper.Edit<T>(requestData, item =>  
            {  
                string error = string.Empty;  
                bool success = true;  
                try  
                {  
                    db.Entry(item as object).State = EntityState.Modified;  
                    db.SaveChanges();  
                }  
                catch (Exception e)  
                {  
                    error = GetExceptionMessage(e);  
                    success = false;  
                }  
                return new CollectionViewItemResult<T>  
                {  
                    Error = error,  
                    Success = success,  
                    Data = item  
                };  
            }, () => data.ToList<T>()));  
        }  

        public ActionResult FlexGrid_Delete([C1JsonRequest]CollectionViewEditRequest<C1MvcApplication1.Models.Products> requestData)  
        {  
            return Delete(requestData, db.Products, item => new object[] {  });  
        }  

        private ActionResult Delete<T>(CollectionViewEditRequest<T> requestData, DbSet<T> data, Func<T, object[]> getKeys) where T : class  
        {  
            return this.C1Json(CollectionViewHelper.Edit<T>(requestData, item =>  
            {  
                string error = string.Empty;  
                bool success = true;  
                try  
                {  
                    T resultItem = null;  
                    foreach (var i in data)  
                    {  
                        if (string.Equals(ArrayToString(getKeys(i)), ArrayToString(getKeys(item))))  
                        {  
                            resultItem = i;  
                            break;  
                        }  
                    }  

                    data.Remove(resultItem);  
                    db.SaveChanges();  
                }  
                catch (Exception e)  
                {  
                    error = GetExceptionMessage(e);  
                    success = false;  
                }  
                return new CollectionViewItemResult<T>  
                {  
                    Error = error,  
                    Success = success,  
                    Data = item  
                };  
            }, () => data.ToList<T>()));  
        }  

        /// <summary>  
        /// In order to get the real exception message.  
        /// </summary>  
        private static SqlException GetSqlException(Exception e)  
        {  
            while(e != null && !(e is SqlException))  
            {  
                e = e.InnerException;  
            }  
            return e as SqlException;  
        }  

        // Get the real exception message  
        internal static string GetExceptionMessage(Exception e)  
        {  
            var msg = e.Message;  
            var sqlException = GetSqlException(e);  
            if (sqlException != null)  
            {  
                msg = sqlException.Message;  
            }  
            return msg;  
        }  

        /// <summary>  
        /// Get string from object array.  
        /// </summary>  
        /// <param name="array">the object array</param>  
        /// <returns>the string value</returns>  
        private static string ArrayToString(object[] array)  
        {  
            if (array != null)  
            {  
                return string.Join(",", array.Cast<object>().Select(a => a.ToString()));  
            }  
            return "";  
        }  
    }  

The Index.cshtml will have the generated code for FlexGrid with all the properties already set.


@using C1.Web.Mvc  
@using C1.Web.Mvc.Grid  
@addTagHelper *, C1.AspNetCore.Mvc  
@model IEnumerable<C1MvcApplication1.Models.Products>  

    <script type="text/javascript">  

        function formatItem(sender, e) {  
            // Implement the event handler for formatItem.  
        }  

    </script>  

<c1-flex-grid id="fg" auto-generate-columns="false" allow-delete="true" height="540px" format-item="formatItem">  
    <c1-items-source source-collection="Model" update-action-url="@(Url.Action("FlexGrid_Update"))"  
                     delete-action-url="@(Url.Action("FlexGrid_Delete"))" page-size="7" group-by="CategoryId"></c1-items-source>  
    <c1-flex-grid-column binding="ProductId"></c1-flex-grid-column>  
    <c1-flex-grid-column binding="ProductName"></c1-flex-grid-column>  
    <c1-flex-grid-column binding="CategoryId">  
        <c1-data-map display-member-path="CategoryName" selected-value-path="CategoryId">  
            <c1-items-source  
                             source-collection="(IEnumerable<C1MvcApplication1.Models.Categories>)ViewBag.DataMap\_Categories\_CategoryName_CategoryId"></c1-items-source>  
        </c1-data-map>  
    </c1-flex-grid-column>  
    <c1-flex-grid-column binding="QuantityPerUnit"></c1-flex-grid-column>  
    <c1-flex-grid-column binding="UnitPrice"></c1-flex-grid-column>  
    <c1-flex-grid-column binding="UnitsInStock"></c1-flex-grid-column>  
    <c1-flex-grid-column binding="UnitsOnOrder"></c1-flex-grid-column>  
    <c1-flex-grid-column binding="ReorderLevel"></c1-flex-grid-column>  
    <c1-flex-grid-column binding="Discontinued"></c1-flex-grid-column>  
    <c1-flex-grid-filter default-filter-type="Both"></c1-flex-grid-filter>  
</c1-flex-grid>  
<c1-pager owner="fg"></c1-pager>  

Note that the generated view code uses Tag Helpers, which look similar to XAML or Angular code. If you run the sample now, you get a fully functional grid with support for:

  • CRUD
  • Grouping
  • Filtering
  • Paging
  • Column ordering\resizing
  • Excel-like Editing

All of this functionality without having to write a single line of code!

Products Products

We've met all the requirements except one: we need to apply conditional formatting on "UnitsInStock" column so that products below certain threshold are shown in red.

We'll apply conditional formatting using script. We can use either JavaScript or TypeScript; IntelliSense is provided for both. Since TypeScript is strongly typed it suits MVC controls. On the index page remove the following JavaScript code:




        function formatItem(sender, e) {  
            // Implement the event handler for formatItem.  
        }  



Open the app.ts file from wwwroot->js TypeScript file TypeScript file

Add the following formatItem function to this TypeScript file:


  function formatItem(s: wijmo.grid.FlexGrid, e: wijmo.grid.FormatItemEventArgs) {  
  if (s.cells.cellType == wijmo.grid.CellType.Cell && e.panel.columns[e.col].binding == "UnitsInStock") {  
  var data = e.panel.getCellData(e.row, e.col, false);  
  if (data < 25)  
  e.cell.style.color = "red";  
  }  
  }  

Add a reference of the app.ts file to the Index.cshtml. Run the app and navigate to Products:

Conditional Formatting Conditional Formatting

We've displayed products data without writing any code, and we had to write a small script to customize the display.

Reporting

FlexGrid supports exporting to Excel both on client and server side (on server side it makes use of Web APIs). You could also design reports using the standalone FlexReport Designer app and display them using MVC FlexViewer and FlexReport Web API. The built-in item template configures the FlexViewer, and Web API removes the necessity to write lot of code so you can concentrate on business requirements.

Note on the C1Scaffolder: The scaffolders are available for:

  • FlexGrid
  • Input
  • FlexChart
  • FlexPie
  • FlexSheet

The Scaffolder require Entity Framework 6.0 or Entity Framework Core 1.0; for more details, please refer to our documentation.

Download ComponentOne Studio

MESCIUS inc.

comments powered by Disqus