2. Web Programming Principles
1. Do not get far more than what we need
2. Database connection/query is expensive (Try to work all in one go as much as we can)
3. D-R-Y (Don’t Repeat Yourself)
3. Issues
• Single function has been used for multiple purposes
• GetProjectByID (projectId)
• Project info
• Org info
• Related Licenses
• Project viewpoints, Terrain Services, Reference Structures, PDF files
• Project overlays
• Project layouts
• Turbines
• Turbine spec
• Turbine model
• Nearby org cumulative projects
• Nearby national cumulative projects
• National cumulative turbines
• Snapshots
• ….
public string GetProjectName(int projectId)
{
var project = GetProjectByID(projectId);
return project.Name;
}
public int GetNumberOfViewpoints(int projectId)
{
var project = GetProjectByID(projectId);
return project.Viewpoints.Count();
}
Violate principle #1
4. Issues – It’s even worse
* Entity Framework SQL Query Generation
* N+1 problem (common mistake)
public BusinessObjects.Project GetProjectByID(int projectId)
{
using (var db = ProjectsEntities1.Create())
{
BusinessObjects.Project projectObj = null;
var projectData = db.Projects.Where(n => n.UID == projectId).SingleOrDefault();
if (projectData != null)
{
projectObj = tblProject.CreateObject(projectData);
foreach (var viewPointData in projectData.ViewPoints)
{
var viewPointObj = tblViewPoint.CreateObject(viewPointData);
projectObj.ViewPoints.Add(viewPointObj);
}
}
return projectObj;
}
}
“Behind the scene”
1 query to retrieve project info
N queries to retrieve N viewpoints of project
Total 1+N queries just to retrieve a simple data
Given we have M projects, it would take M*(1+N)
amount of queries to get data from database!
Violate principle #2
Lazy loading
6. Best Practices (option 1.)
public class ProjectBasic
{
public ProjectBasic(ProjectBasic projectBasic) : this()
{
Utils.CopyProperties<ProjectBasic>(projectBasic, this);
}
public int UID { get; set; }
public int OrganizationUID { get; set; }
public string Name { get; set; }
public System.DateTime DateAdded { get; set; }
}
public class ProjectWithViewPoint : ProjectBasic
{
//Basic data
public ProjectWithViewPoint(ProjectBasic projectBasic)
: base(projectBasic) {}
//Additional data
public List<BusinessObjects.ViewPoint> ViewPoints { get; set; }
}
public static BusinessObjects.ProjectBasic GetProjectByID(int projectId, ProjectsEntities1 db)
{
BusinessObjects.ProjectBasic projectObjBasic = null;
var projectData = db.Projects.Where(n => n.UID == projectId).SingleOrDefault();
if (projectData != null)
projectObjBasic = tblProject.CreateObjectBasic(projectData);
return projectObjBasic;
}
public BusinessObjects.ProjectWithViewPoint GetProjectWithViewPoints(int projectId)
{
using (var db = ProjectsEntities1.Create())
{
BusinessObjects.ProjectWithViewPoint projectObj = null;
BusinessObjects.ProjectBasic projectBasicObj = GetProjectByID(projectId, db);
if (projectBasicObj != null)
{
//Create new from basic data (with auto-copied constructor)
projectObj = new BusinessObjects.ProjectWithViewPoint(projectBasicObj);
//Query additional data we need in one go under the same connection DB
var dataViewPoints = db.ViewPoints.Where(n => n.ProjectUID == projectId);
foreach (var dataViewPoint in dataViewPoints)
{
var viewPointObj = tblViewPoint.CreateObject(dataViewPoint);
projectObj.ViewPoints.Add(viewPointObj);
}
}
return projectObj;
}
}
7. Best Practices (option 2.) using System.Data.Entity;
public BusinessObjects.ProjectWithViewPoint GetProjectWithViewPoints(int projectId)
{
using (var db = ProjectsEntities1.Create())
{
db.Configuration.LazyLoadingEnabled = false;
BusinessObjects.ProjectWithViewPoint projectObj = null;
var projectData = db.Projects
.Include(n => n.ViewPoints)
.Where(n => n.UID == projectId).SingleOrDefault();
if (projectData != null)
{
var projectBasic = tblProject.CreateObjectBasic(projectData);
//Create new from basic data (with auto-copied constructor)
projectObj = new BusinessObjects.ProjectWithViewPoint(projectBasic);
//Query additional data with include enabled
foreach (var dataViewPoint in projectData.ViewPoints)
{
var viewPointObj = tblViewPoint.CreateObject(dataViewPoint);
projectObj.ViewPoints.Add(viewPointObj);
}
}
return projectObj;
}
}
public class ProjectBasic
{
public ProjectBasic(ProjectBasic projectBasic) : this()
{
Utils.CopyProperties<ProjectBasic>(projectBasic, this);
}
public int UID { get; set; }
public int OrganizationUID { get; set; }
public string Name { get; set; }
public System.DateTime DateAdded { get; set; }
}
public class ProjectWithViewPoint : ProjectBasic
{
//Basic data
public ProjectWithViewPoint(ProjectBasic projectBasic)
: base(projectBasic) {}
//Additional data
public List<BusinessObjects.ViewPoint> ViewPoints { get; set; }
}
8. Best Practices (cont.)
public static BusinessObjects.ProjectBasic CreateObjectBasic(Project projectData)
{
BusinessObjects.ProjectBasic retsult = new ProjectBasic();
result.UID = projectData.UID;
result.OrganisationUID = projectData.OrganisationUID;
result.Name = projectData.Name;
result.DateAdded = projectData.DateAdded;
return result;
}
public static void CopyProperties<T>(T fromObj, T toObj)
{
var propertiesInfo = typeof(T).GetProperties();
var toObjType = toObj.GetType();
foreach (var propertyInfo in propertiesInfo)
{
var valueToSet = propertyInfo.GetValue(fromObj, null);
toObjType.GetProperty(propertyInfo.Name).SetValue(toObj, valueToSet, null);
}
}
9. Best Practices (cont.)
* CreateObject function just simply to create BusinessObject from DataObject (That’s all! Do
NOT query or load related objects)
* Construct a full business object by:
◦ Based on basic data object
◦ Query related objects separately in one go as much as we can (within a same connection)
◦ Construct a full object by merging related objects
◦ Or Use “Include” keyword (System.Data.Entity) – Carefully to use this!
* Split data access function into DB-reuse and Non-DB-reuse.
10. Conclusions
1. Do not get far more than what we need
2. Database connection/query is expensive
Query data at one time and arrange data in memory instead of working directly in database
3. D-R-Y (Don’t Repeat Yourself)
4. Following best practices!