This project is read-only.

Contents

Gain Coding Speed with Nido (FREE) Framework .NET/ C#

 

Note: You may refer to Introductory Video Script here


Coding Guidelines

Select/ Search 

protected override void LoadData()
{
    SetPageNumberPanel();
    GenericResponse<IEnumerable<Student>> response = handlerStudent
        .Include("StudentCourses.Course").Include("HomeAddress").Include("OfficeAddress").GetAllGeneric(x => x.IsActive == true);

    if (response.Successful)
    {
        this.PageNmbers.Text = PageNumberPanel;
        BindedObject = response.Result.OrderBy(x => x.Age).Skip(CurrentPage * PAGE_SIZE)
            .Take(PAGE_SIZE);
        this.GridView1.DataSource = BindedObject;
        this.GridView1.DataBind();
    }
    else
        this.DisplayError(response);
}

Update (This is fairly a complex update)

private void UpdateRecord2(Student student)
{
    student.Age = 308;
    student.Description = "Editted on " + DateTime.Now;

    student.StudentCourses.FirstOrDefault().Course.Period = 308;
    student.StudentCourses.FirstOrDefault().Course.Name = "Eddited on " + DateTime.Now;

    // Remove will delete all the records including the onces that are not loaded this time
    // Remove a set of given elements will only delete them
    student.StudentCourses.Clear();// Remove(student.StudentCourses.LastOrDefault());

    Course course = new Course();
    course.EndDate = DateTime.Now;
    course.IsActive = false;
    course.IsWeekEnd = true;
    course.Name = "Newly added course on " + DateTime.Now;
    course.Period = 10;
    course.StartDate = DateTime.Now;

    StudentCourse stdCourse = new StudentCourse();
    stdCourse.StudentId = student.StudentId;
    stdCourse.CourseId = course.CourseId;
    stdCourse.Course = course;

    student.StudentCourses.Add(stdCourse);

    StudentHandler stdentHandler = new StudentHandler();

    GenericResponse<Student> response = stdentHandler
        .UpdateGraphGeneric(student
        , map => map.OwnedCollection(p => p.StudentCourses
            , with => with.OwnedEntity(p => p.Course)));

    // When the object is not attached errors comes then go with this option
    // student.Age = 120;
    // student.Description = "Editted on " + DateTime.Now;
    // GenericResponse<Student> response = handlerStudent.AttachChild(course).AttachChild(stdCourse).UpdateGeneric(student);

    // Want to update only the paramerters that were newly updated and you just want other properties untouched
    // Then go with this option, Once you pass the primary key the system think that you want it to find the
    // record from the DB and do only the update.
    // GenericResponse<Student> response = handlerStudent.UpdateGeneric(student, student.StudentId);

    this.DisplayError(response);
}

// OR

// update all tasks with status of 1 to status of 2
taskHanlder.UpdateGeneric(
t => t.StatusId == 1,
t2 => new Task {StatusId = 2});

// OR

// example of using an IQueryable as the filter for the update
userHandler.UpdateGeneric(u => u.FirstName == "firstname", u => new User {FirstName = "newfirstname"});
 

Delete

handlerStudent.DeleteGeneric(entityObject);

// OR 

handlerStudent.DeleteGeneric(12);

// OR

handlerStudent.DeleteGeneric(x=>x.StudentId>10);

Add

handlerStudent.AddGeneric(entityObject);

Calling a Stored Procedure

myDbContext db = new myDbContext(); 

return db.ExecuteStoredProcedure("StoGRNLine_I_PRNum @GRNNo,@PurOrdNo,@PrdOrdNo,@ItemCode,@SuppRefNo,@RecQty,@PRNum"new SqlParameter("@GRNNo", grnNo), new SqlParameter("@PurOrdNo", purOrdNo), new SqlParameter("@PrdOrdNo", prdOrdNo), new SqlParameter("@ItemCode", itemCode), new SqlParameter("@SuppRefNo", suppRefNo), new SqlParameter("@RecQty", recQty), new SqlParameter("@PRNum", prNum));

 

Additionally we have integrated EntityFramework Extension to this now. The Handlers are now empowered with more methods. In order to see the details of the newly available methods and their usage please refer the sample code uploaded with the source code. If you need further reading please go to...


What is the overview?

Nido framework is a generic framework that help you reduce the amount of coding you write. It also gives you the much needed architectural discipline to your project. If you take entity framework, you will see developers does write different codes on it, but managing them could be a night mere. We have identified the best techniques or method of doing something and have standardized it in to this framework called Nido. That is one aspect of Nido. Then you have all these abstract features that you get starting from testing, logging and tracking are all part of this framework. So at the end you get an environment that help you mature your capabilities while gaining experience.


How to setup the project?

Before you read this documentation, you need to look at the sample project source code and create your project as per the guidelines given in the demo video. Then we also encourage you to understand the sample project as well. Additionally you need to have the following expertise to develop application using Nido.

  • Application development using .Net Framework
  • Asp.Net Web Development
  • Entity Framework Code First technique and related development

With having all these prerequisites, you may proceed to read this documentation :-).


How to develop the database tables?

There are few Database guidelines that you need to adhere if you are to easily use Nido.

  1. Give meaningful names for tables 
  2. Use a primary key for all the tables. The primary key should have the table name followed by the suffix 'Id' (e.g. primary key = TableNameId). This is applicable for composite tables as well.
  3. 'Name' column would come handy if you are to display table records in a drop-down box.
  4. Foreign key name of a table also may use table name followed by the suffix 'Id' 
  5. Use as many as Views for yourself to simplify the SQL statement, model classes for views needed to be created manually. They can be created just like you create them in EF.
  6. Use Stored Procedures as much as possible to avoid unwanted database round trips.

How to develop the DB class or Database Context class?

The DB context class is the virtual form of the database. It is your data access layer too. Since it is a simple class, we decided to merge the data access layer with the business logic layer. So when developing with Nido you will not have a separate project for your data access layer.

Note: If you are on of those developers who use to write everything in ASP.NET file, then you may even wonder what am I talking here.

In general separation of concern is important due to few main reasons. That will directly or indirectly allow you to have

  • better security
  • scale-ability
  • ability to write code faster
  • code consistency
  • better control 
  • etc

So this is how the DB context class going to look like when you develop your application on Nido. So let's look at the sample "DbContext" class given below..

using System.Data.Entity;
using Nido.Common.BackEnd;
using DemoTest.Bll.Models;

namespace DemoTest.Bll.DB
{
    public class DemoDBContext : BaseObjectConext
    {
        public DbSet<Student> Students { get; set; }
        public DbSet<Course> Courses { get; set; }
        public DbSet<StudentCourse> StudentCourses { get; set; }
        public DbSet<Address> Addresses { get; set; }
        public DbSet<Data> Datas { get; set; }
    }
}

As you can see, this is pretty much like developing the DB context class using Entity Framework Code First technique. The only difference here is that, you would have a different base class, and that is instead of "DbContext" or "ObjectContext" you have with code first we will have the "BaseObjectContext" as the super class here. That is the only difference and all others are pretty much the same..


How to develop the Model or Business Object class?

Model class is what carries data from the database to the front end user interface. Let's see what we have in it..

One of the difference in coding on Nido is that we have merged DTOs (Data Transfer Objects) with BO (Business Objects) to reduce the amount of codes you have to write. In it both data logic as well as the business logic are managed.

If you need little more flexibility and need to manage business logic and data logic in two separate places, then it is up to you to decide how you want to extend this design to handle your requirements. One of the common approach would be to have two separate objects one directly mapping with the database table and another directly mapping with the user interface. In such case it is also a good option to introduce something like auto-mapper to map business object data to the data object and vise-versa.

Let's see what we have here..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nido.Common.BackEnd;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace DemoTest.Bll.Models
{
    /// <summary>
    /// Created by MAS IT
    /// </summary>
    public class Course : BaseObject
    {
        public Course()
            // Change this parameter to change the DisplayName 
            // (this name is used in all system messages)
            // property of this object
            : base("Course") { }

        #region Public Properties
        [Key]
        [Required]
        [DisplayName("Course Id")]
        public int CourseId { get; set; }

        [Required]
        [DisplayName("Name")]
        public string Name { get; set; }

        [Required]
        [DisplayName("Start Date")]
        public DateTime StartDate { get; set; }

        [Required]
        [DisplayName("End Date")]
        public DateTime EndDate { get; set; }

        [Required]
        [DisplayName("Period")]
        public int Period { get; set; }

        [Required]
        [DisplayName("Is Week End")]
        public bool IsWeekEnd { get; set; }

        [Required]
        [DisplayName("Is Active")]
        public bool IsActive { get; set; }

        [DisplayName("Student Courses")]
        public ICollection<StudentCourse> StudentCourses { get; set; }
        [DisplayName("Datas")]
        public ICollection<Data> Datas { get; set; }
        #endregion

        #region Not Mapped Properties

        /// <summary>
        /// Implement this to prevent or allow the object to be
        /// deleted through the base handler.
        /// You need to consider all dependant objects before 
        /// allowing a object to be deleted.
        /// Therefore please implement this correctly/ accordingly.
        /// </summary>
        [NotMapped]
        public override bool CanDelete
        {
            // TODO: Impletement this 
            // Example: return ((StudentCourses != null) 
            // && (StudentCourses.Count > 0)) ? false : true;
            get
            {
                if (_canDelete.HasValue)
                    return _canDelete.Value;
                else
                    return !(((StudentCourses != null)
                     && (StudentCourses.Count > 0)) || ((Datas != null)
                     && (Datas.Count > 0)));
            }
            set { _canDelete = value; }
        }
        private bool? _canDelete;

        [NotMapped]
        public override string DeleteError
        {
            get
            {
                return "<b>" + ((StudentCourses != null) ?
                    StudentCourses.Count.ToString() :
                    "Indecisive # of") + "</b> StudentCourse(s) are using this 'Course'<br/>"
                     + "<b>" + ((Datas != null) ?
                     Datas.Count.ToString()
                     : "Indecisive # of") + "</b> Data(s) are using this 'Course'<br/>";
            }
        }

        [NotMapped]
        public override string DeleteErrorText
        {
            get
            {
                return ((StudentCourses != null) ?
                    StudentCourses.Count.ToString() : "Indecisive # of")
                     + " StudentCourse(s), " + ((Datas != null) ?
                     Datas.Count.ToString() : "Indecisive # of")
                     + " Data(s) are using this 'Course'";
            }
        }

        /// <summary>
        /// This property set the value of the items of the combo box that created 
        /// deriving the BaseCombo. You may change this as needed.
        /// </summary>
        [NotMapped]
        public override string Value
        {
            get { return Convert.ToString(CourseId); }
            set { CourseId = Convert.ToInt32(value); }
        }

        /// <summary>
        /// This property set the display name of the items of the combo box that created 
        /// deriving the BaseCombo. You may modify or delete this as required.
        /// </summary>
        [NotMapped]
        public override string Text
        {
            get { return Name; }
            set { Name = value; }
        }

        #endregion
    }
}

This is the class that directly map to your database table. All the fields of the table named 'Course' need to have defined here. If you need any additional fields, then you may define them here as well but you need to add them with the '[NotMapped]' attribute flag.

Unlike it was with Entity Framework code first technique or "POCO" objects, the Nido Framework needed you to create model classes by deriving it from the "BaseObject", which is coming from the Nido framework.

One important point to remember is, how to name the primary key of your database table. For Nido, it is recommended to maintain special naming convention when naming the primary key. That is, the name of the primary key of any table has to have the naming convention like "<table name>Id". This will make the system auto recognize your primary key, hence you don't necessarily have to have the "[Key]" attribute defined (even though it is flagged in the sample code). This feature is available not only in Nido, it is same for Entity-Framework code first technique too.

In order to understand the various option available, you just have to study how "POCO" objects are created to support entity framework code first technique. Then follow the same pattern here too. That would help you develop the model classes very easily.

Additionally you will have "t4template" that generate model classes automatically for you too. Please see the section "How to use 't4template'" to have more details on that.


How to develop the Handler class?

The Handler classes help you manipulate model classes. This is the class where you do all CRUD (Create, Read, Update and Delete) operations related to each model class. You may do them for individually or with all the related objects together. Therefore we urge you to create separate Handler class for each model. So let's show you a handler created for our Course object below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nido.Common.BackEnd;
using DemoTest.Bll.Models;
using DemoTest.Bll.DB;
using System.Data.Entity;

namespace DemoTest.Bll.Handlers
{
    public class CourseHandler : HandlerBase<Course, DemoDBContext>
    {
        protected override Type LogPrefix
        {
            get { return this.GetType(); }
        }
    }
}

As you can see, there is nothing much here, but the important thing is the separation of concerns. That is, you need to have a separate handler for each model object. This Handler class is where you write all the operations related to the noted model. As you will learn later, bulk of the things you needed here are already being written inside that "HandlerBase". 

The base handler class allow you to override pretty much all the base methods, and among them the following two important overrides help you inject validation logic for both Add and Update. You can use these two methods to do object validations before adding as well as updating..

public override bool ValidateForAdd(Course entity, out string[] errors)
{
    return base.ValidateForAdd(entity, out errors);
}

public override bool ValidateForUpdate(Course entity, out string[] errors)
{
    return base.ValidateForUpdate(entity, out errors);
}

The main goal here is to have the code consistency. It does not matter who writes the code, we encourage you to write that in such a predictable manner so that any developer who understand the coding pattern (with Nido) can manage the code. There by we reduce individual dependencies of the code.

In summary, when you use Nido, it is not your brain but the standard framework that solve the problem for you. 


How to use 't4template'?

If you download the sample source code and open it, you will see a project with the name "DemoTest.Bll". In it you will find a directory called "T4Templates" where you will find three separate t4 templates together with a one ttinclude file. Get them to your business logic layer project and configure them with your database. Update the "Connection String" (look at the t4 template code and update the var with the name connectionString) as require before selecting to "run custom tools" to generate the classes for you.

Please note that this is not the finalized t4 template and you may use this as an assistance to generate your business logic. But if it gives you errors then you may need to look at the sample project and other video available here to resolve them. In addition to that you may also post questions under discussion tab here which I will reply as soon as I can...


How to configure with the database?

The Nido framework is tested with MS SQL Server 2008, but it should work with all MS SQL versions and any other database that Entity Framework 5.0 works with. In order to configure Nido framework with the database you need to have an entry in to your Web.Config file as given below.

<connectionStrings>
    <add name="DemoDBContext" connectionString="data source=MAS-LT-ICT011;initial catalog=School;user id=smstest;password=welcome@123;multipleactiveresultsets=True; timeout=500" providerName="System.Data.SqlClient" />
</connectionStrings>

Note: Here the "DemoDBContext" is the name of the DB Context class. This is the same configuration you do in entity framework code first technique.


How to configure for exception logging?

This uses famous "log4net" for exception logging. It has two level of exception logging, it log all the errors as well as all the operation (as Info logs). You need to add couple of line of codes to configure Exception Logging for your application and set the threshold. You may find details about configuring your application with "log4net"on the internet as well. So here I am just giving you a sample Web.Config file, please look at it and apply required configuration to your application.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, 
    log4net" />
  </configSections>
<log4net>
    <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
      <file value=".\\Errors\\Exception.log" />
      <appendToFile value="true" />
      <maximumFileSize value="256KB" />
      <maxSizeRollBackups value="10" />
      <!--DEBUG < INFO < WARN < ERROR < FATAL-->
      <param name="Threshold" value="INFO" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%d [%t] %-5p %c - %m%n" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="RollingFile" />
</root> </log4net>

How to enable the Audit Trail option?

Audit trail store all operations in a database table. That help you keep track of all the modifications you do to the database. You just have to follow the steps given below to configure that.

Step 1. Enable Tracking via adding the appSettings <add key="EnableTracking" value="true"/>
Step 2. Create the table to insert tracking details using the following SQL Command

/****** Object: Table [dbo].[AuditTrail] Script Date: 12/13/2013 08:12:22 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[AuditTrail](
[AuditTrailId] [int] IDENTITY(1,1) NOT NULL,
[SystemName] [varchar](50) NULL,
[RevisionStamp] [datetime] NULL,
[TableName] [varchar](50) NULL,
[TablePrimaryId] [varchar](50) NULL,
[UserName] [varchar](50) NULL,
[Actions] [varchar](50) NULL,
[OldData] [xml] NULL,
[NewData] [xml] NULL,
[ChangedColumns] [varchar](1000) NULL,
CONSTRAINT [PK_DBAudit] PRIMARY KEY CLUSTERED
(
[AuditTrailId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

Step 3. Add a new session variable named "UserName". You may need to set the actual logged in user of the application. I am sorry I have not done this implementation for Windows Form yet. I really need to see how this would behave with a Windows Form based application. 

Step 4. Add a new Session variable named "SystemName". This is the actual name of your application.

Step 5. Add 'Auditable' attribute right at the top part of the model class to indicate that the model needed to be audited. Then mark each property with the same attribute to indicate the audit-able properties (commit version 29369 and higher) too.

Step 6. Once you do all that, you are ready to go with "Audit Trail" now..

Audit trail has options to optimize auditing work. 'Nido' does not track changes to all the table records in the database. Tracking all the records could make the system use more processor power in tracking records than doing other important business related functions. On the same node, it may force the tracking module to track records that are not needed to track as well. In this sense 'Nido' uses a special attribute called [Auditable(true)] to mark the records that needed to be tracked. You can mark important records that needed to be tracked with that attribute. As changes occurs to those records, the system will track changes and add entries details to a table named 'AuditTrail'.

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

 


How to Enable "Testing" Mode?

This is one of those very useful setting. This allows you to test applications in production environment with production data but without changing the production data.

In our environments, it is often report bugs that are not repeatable in development environment. Some of these bugs are so unique that they occurs only in production environment and only with a particular set of data. Once the data is being changed the the error is also gone. Therefore to track those errors we came up with this option for Nido.

Once you add a simple appSetting to your web.config file the application create a instance of the database in system memory where it keep track of all the update for the current instance of the application. It work just like the updates were done to the database but in actual terms it does update only the in memory version of the database. Upon restarting the application the system revert back the to original database allowing you to restart the testing without worrying about resetting your database to its initial form.

In order to get this working you just have to add the following configuration to your web.config or app.config file.

<add key="EnableTesting" value="false"/>

 


How to get additional Features?

If you need any more features than what is provided by default via the Handler Classes then you may write them inside the Handler classes itself. In order to do that create new methods for your custom method inside the Handler class. As you may aware..

- You can access the DB context object via '_context' property

- You can access the current object set via '_entitySet' property or 'GetObjectSet()' method

We encourage you to navigate and see the 'HandlerBase' to study writing new methods using a similar coding pattern.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nido.Common.BackEnd;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using DemoTest.Bll.Models;
using DemoTest.Bll.DB;

namespace DemoTest.Bll.Handlers
{
    /// <summary>
    /// Handler method for Address Table. You will have one handler for each table.
    /// </summary>
    /// <remarks>
    /// You may write any additional data handling operations
    /// related to Address table in here.
    /// </remarks>
    public partial class AddressHandler : HandlerBase<Address, SchoolDBContext>
    {
        protected override Type LogPrefix
        {
            get { return this.GetType(); }
        }

        /// <summary>
        /// Created by RocketFramework
        /// </summary>
        /// <remarks>
        /// This is a sample method.
        /// You may modify this method as needed.
        ///
        /// If you want to do complex data loading, you can write a
        /// SP that accept multiple parameters and then call multiple tables,
        /// views through it to select the particular record set you want.
        ///
        /// Important: You have to create a 'Model' object to directly match the receiving object.
        ///
        /// Then through a SP call you may load the records.
        /// </remarks>
        public GenericResponse<IEnumerable<Address>> CustomGetMethod()
        {
            try
            {
                // Call the SP or any other custom SQL query with or wihout parameters
                // This is good if the provided methods are not sifficient to select the records
                //
                // The example below explain how to call a SP with or without parameters
                //
                // return db.ExecuteStoredProcedure<FinanceStatusView>("PRHeader_S_FinancePRStatus @BudgetStatusId,@PRStatusId",
                // new SqlParameter("@BudgetStatusId", 1), new SqlParameter("@PRStatusId", prStatusId));
                //
                // return db.ExecuteStoredProcedure<FinanceStatusView>("PRHeader_S_FinancePRStatus");
                //
                IEnumerable<Address> s = this._context.ExecuteStoredProcedure<Address>("Select * from Student", null);
                // return UpdateSuccessResponse(<Updated Address>);
                // return DeleteSuccessResponse(<Deleted Address>);
                return SelectSuccessResponse(s);
            }
            catch (Exception e)
            {
                return HandleException<IEnumerable<Address>>(e);
            }
        }
    }
}

Note: If you feel the new methods that you are writing are 'COMMON' enough to include in the Nido 'HandlerBase', then don't hesitate to contact us.

How to use Property Encryption?

'Nido' framework provide facilities to easily encrypt sensitive data before storing them in the database. In order to use this feature you need to do two things.

1. You can flag model class properties with a special attribute with the name 'Encrypted' to enable this feature.
2. Then most of the functions automatically pick the tag and do the needful modification to the model. This means 'Nido' framework to go through all the properties that has the 'Encrypted' attribute and call for encryption or decryption before/ after saving/ retrieving them to/ from the database.
 
This is how you need to flag a property for encryption
public class Student : BaseObject
{
[Encrypted]
public string Name { get; set; }
}

Once you do that, when store the student model object into the database, it calls the method with the name 'EncryptRecoords' to actually encrypt the records.

studentObj.EncryptRecords(); // You don't have to call this

Going along the same way, when loading a record back from the database, it calls to decry-pt the record back to readable format.

studentObj.DecryptRecords(); // You don't have to call this

However (as of now) there are special scenarios where you have to call the encryption and decryption method.

If you are storing a record with its child records to the database, then you have to call encryption method for all of those child objects (not the primary object). Then again if you are retrieving records from the database with their child (related tables) objects, then you got to manually do their decryption as well.

Last edited Oct 3, 2014 at 5:33 AM by RocketFramework, version 70