I am trying to create a pie chart using SyncFusion bind in SQL Server in ASP.NET MVC, however the chart doesn't display, no error message found when I rebuild the code and it runs but no chart display. I wondered what could the cause of the chart doesn't load
I tried this code to connect the Syncfusion pie chart to SQL Server
string command2 = "SELECT * FROM [myTable] WHERE Item_ID < 10";
SqlCommand cmd1 = new SqlCommand(command2, con);
adapter.SelectCommand = cmd1;
adapter.Fill(dataset);
for (var i = 0; i < dataset.Tables[0].Rows.Count; i++)
{
int x1 = (int)Convert.ToInt32(dataset.Tables[0].Rows[i]["this.Item_ID"]);
int y1 = (int)Convert.ToInt32(dataset.Tables[0].Rows[i]["Item_Score"]);
data.Add(new ChartSqlData(x1, y1));
}
CSHTML view:
#(Html.EJS().AccumulationChart("container")
.Series(sr =>
{
sr.Type(Syncfusion.EJ2.Charts.AccumulationType.Pie)
.XName("Item_ID")
.YName("Item_Score")
.Name("Item_ID")
.Explode(true)
.DataLabel(dl => dl.Visible(true).Name("Item_ID").Position(Syncfusion.EJ2.Charts.AccumulationLabelPosition.Outside).ConnectorStyle(cs => cs.Type(Syncfusion.EJ2.Charts.ConnectorType.Line).Length("5 %")).Font(ft => ft.Size("14px")))
.Animation(animate => animate.Enable(true))
.Radius("70%")
.StartAngle(0)
.EndAngle(360)
.InnerRadius("0%")
.GroupTo("9")
.GroupMode(Syncfusion.EJ2.Charts.GroupModes.Point)
.DataSource(ViewBag.dataSource).Add();
})
Controller:
List<ChartSqlData> data = new List<ChartSqlData>();
string connectionString = null;
SqlDataAdapter adapter = new SqlDataAdapter();
DataSet dataset = new DataSet();
connectionString = #"Data Source=LAPTOP-V3QJAMBF\SQLEXPRESS;Initial Catalog=My_Database;Integrated Security=True;";
SqlConnection con = new SqlConnection(connectionString);
con.Open();
string command2 = "SELECT * FROM [myTable] WHERE Item_ID < 10";
SqlCommand cmd1 = new SqlCommand(command2, con);
adapter.SelectCommand = cmd1;
adapter.Fill(dataset);
for (var i = 0; i < dataset.Tables[0].Rows.Count; i++)
{
int x1 = (int)Convert.ToInt32(dataset.Tables[0].Rows[i]["this.Item_ID"]);
int y1 = (int)Convert.ToInt32(dataset.Tables[0].Rows[i]["Item_Score"]);
data.Add(new ChartSqlData(x1, y1));
}
ViewBag.dataSource = data;
return View();
[Serializable]
public class ChartSqlData
{
public ChartSqlData(int xvalue, int yvalue1)
{
this.Item_ID = xvalue;
this.Item_Score = yvalue1;
}
public int Item_ID { get; set; }
public int Item_Score { get; set; }
}
This is because of Render method is not yet called at an end of control creation. I have added that and prepared sample based on your need. Please check the below snippet and sample.
#(Html.EJS().AccumulationChart("container").Render())
Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/PieDB1343192459.zip
Related
I have a large number of stored procedures to work with and I have to work with Entity Framework.
I got for example this controller where I'm just calling the database to show my table:
public class CarguioController : Controller
{
public ActionResult Index(int? page)
{
string cs = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (SqlConnection conn = new SqlConnection(cs))
{
// establece conneciĆ³n
SqlParameter param1 = new SqlParameter();
param1.ParameterName = "#MODO";
param1.SqlDbType = SqlDbType.Int;
param1.Value = 2;
SqlCommand cmdProcedure = new SqlCommand(#"Almacen.[PRC_Carguio]", conn);
cmdProcedure.Parameters.Add(param1);
conn.Open();
cmdProcedure.CommandType = CommandType.StoredProcedure;
SqlDataReader dr = cmdProcedure.ExecuteReader();
List<CarguioViewModel> lst = new List<CarguioViewModel>();
int pageNumber = page ?? 1;
int pageSize = 8;
if (dr.HasRows)
{
while (dr.Read())
{
lst.Add(new CarguioViewModel
{
Carguio_ID = dr.GetInt32(0),
Vehiculos_ID = dr.GetInt32(1),
ManifiestoCarga_ID = dr.GetInt32(2),
Guia_ID = dr.GetInt32(3),
Programaciones_ID = dr.GetInt32(4),
Numero = dr.GetInt32(5),
NroMobil = dr.GetString(6),
Fecha = dr.GetDateTime(7),
Usuarios_ID = dr.GetInt32(8),
Sucursales_IS = dr.GetInt32(9)
});
//display retrieved record
}
return View(lst.ToPagedList(pageNumber, pageSize));
}
else
{
Console.WriteLine("No data found.");
}
dr.Close();
conn.Close();
}
return View();
}
}
As you can see, I have to connect with the SQL Server database many times. Maybe you have done a similar job with ASP.NET MVC projects or have any idea to refactor my code?
I have more than 30 tables and everyone has more a Crud and other functions.
I've been searching for this but there is just the same example.
string cs = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
You can get create in General Utility class to read the connection, so that It is stay in one place in the code and read connection value from the Genral Utility class wherever you need it.
void Main()
{
string cn = GeneralUtility.getConnectionString();
}
public class GeneralUtility
{
public static string getConnectionString()
{
string cs = "";
try
{
cs = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
}
catch (Exception ex)
{
throw new Exception("Connection String Error " + ex.Message.ToString());
}
return cs;
}
}
I added a new element called ADO.Net Entity Data Model, where I retrieve all my Stored Procedures, It is helpful
I added a new element called ADO.Net Entity Data Model
Well, now my code is shorter than before:
public ActionResult Index(int? page)
{
List<CarguioModel> lst = new List<CarguioModel>();
int pageNumber = page ?? 1;
int pageSize = 8;
using (MarviBKPEntities prcAlm = new MarviBKPEntities())
{
List<PRC_Carguio_Result> prc = prcAlm.PRC_Carguio(2, null, null, null, null, null, null, null, null, null, null).ToList();
return View(prc.ToPagedList(pageNumber, pageSize));
}
return View();
}
Whta do you think? Could it cause some bad performance?
I wish to replace the hard coded data with SQL Server Database.
However, I'm stucked as I am still new to this.. I just tried the Google Chart and its working with hard-coded values, please guide me step-by-step to change the values to data from my database.
If theres any informations you need, please let me know. I'll try to provide them. Thanks for the help in advance guys! ):
Code for my Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ChartInMvcApplication.Models
{
public class ProductModel
{
public string YearTitle { get; set; }
public string SaleTitle { get; set; }
public string PurchaseTitle { get; set; }
public Product ProductData { get; set; }
}
public class Product
{
public string Year { get; set; }
public string Purchase { get; set; }
public string Sale { get; set; }
}
}
Code for my Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ChartInMvcApplication.Models;
namespace ChartInMvcApplication.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
ProductModel objProductModel = new ProductModel();
objProductModel.ProductData = new Product();
objProductModel.ProductData = GetChartData();
objProductModel.YearTitle = "Year";
objProductModel.SaleTitle = "Sale";
objProductModel.PurchaseTitle = "Purchase";
return View(objProductModel);
}
/// <summary>
/// Code to get the data which we will pass to chart
/// </summary>
/// <returns></returns>
public Product GetChartData()
{
Product objproduct = new Product();
/*Get the data from databse and prepare the chart record data in string form.*/
objproduct.Year = "2009,2010,2011,2012,2013,2014";
objproduct.Sale = "2000,1000,3000,1500,2300,500";
objproduct.Purchase = "2100,1400,2900,2400,2300,1500";
return objproduct;
}
}
}
Code for my View:
#model ChartInMvcApplication.Models.ProductModel
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(drawChart);
function drawChart() {
// Create and populate the data table.
var years = [#Model.ProductData.Year];
var sales = [#Model.ProductData.Sale];
var Purchase = [#Model.ProductData.Purchase];
var data = new google.visualization.DataTable();
data.addColumn('string', '#Model.YearTitle');
data.addColumn('number', '#Model.SaleTitle');
data.addColumn('number', '#Model.PurchaseTitle');
for (i = 0; i < years.length; i++) {
data.addRow([years[i].toString(), sales[i], Purchase[i]]);
}
var options = {
title: 'Sale and Purchase Compare',
hAxis: { title: '#Model.YearTitle', titleTextStyle: { color: 'red'} }
};
var chart = newgoogle.visualization.ColumnChart(document.getElementById('chartdiv'));
chart.draw(data, options);
}
</script>
<div id="chartdiv" style="width: 500px; height: 300px;">
</div>
You have to add data to each product object.
public Product GetChartData()
{
Product objproduct = new Product();
//Get Data for years as a string list.
List<string> years = Database.GetYears();
foreach(var year in years)
GetChartDataForYear(year, ref objproduct);
return objproduct;
}
public void GetChartDataForYear(string year, Product objproduct out)
{
if(!string.IsNullorEmpty(objproduct.Year))
objproduct.Year += (",");
if(!string.IsNullorEmpty(objproduct.Sale))
objproduct.Sale += (",");
if(!string.IsNullorEmpty(objproduct.Purchase ))
objproduct.Purchase += (",");
objproduct.Year += year.ToString();
objproduct.Sale += GetSale(int year);
objproduct.Purchase += GetPurchase(int year);
}
public string GetSale(string year)
{
// To get from DB.
return "";
}
public string GetPurchase(string year)
{
// To get from DB.
return "";
}
The best way is to bring the values from the database and bind them into a list, then do a foreach to populate your chart.
In your model create 3 functions like this :
public List<string> getYears()
{
List<string> years = new List<string>();
string connectionString = ConfigurationManager.AppSettings["StringConnection"].ToString();
using (SqlConnection cn = new SqlConnection(connectionString))
{
cn.Open();
SqlCommand sqlCommand = new SqlCommand("SELECT year FROM tableTest", cn);
SqlDataReader reader = sqlCommand.ExecuteReader();
while (reader.Read())
{
years.Add(reader["year"].ToString());
}
cn.Close();
}
return years;
}
public List<string> getSale()
{
List<string> sales = new List<string>();
List<string> years = getYears();
foreach (var year in years)
{
string connectionString = ConfigurationManager.AppSettings["StringConnection"].ToString();
using (SqlConnection cn = new SqlConnection(connectionString))
{
cn.Open();
SqlCommand sqlCommand = new SqlCommand("SELECT sale FROM tableTest where year = '" + year + "'", cn);
SqlDataReader reader = sqlCommand.ExecuteReader();
while (reader.Read())
{
sales.Add(reader["sale"].ToString());
}
cn.Close();
}
}
return sales;
}
public List<string> getPurchase()
{
List<string> purchases = new List<string>();
List<string> years = getYears();
foreach (var year in years)
{
string connectionString = ConfigurationManager.AppSettings["StringConnection"].ToString();
using (SqlConnection cn = new SqlConnection(connectionString))
{
cn.Open();
SqlCommand sqlCommand = new SqlCommand("SELECT purchase FROM tableTest where year = '" + year + "'", cn);
SqlDataReader reader = sqlCommand.ExecuteReader();
while (reader.Read())
{
purchases.Add(reader["purchase"].ToString());
}
cn.Close();
}
}
return purchases;
}
Add a function in your Model to change Lists to strings:
public string listToString(List<string> list)
{
string output = "";
foreach(var item in list)
{
output = (String.IsNullOrEmpty(output)) ? item : output + "," + item;
}
return output;
}
In your controller the function will be :
public Product GetChartData()
{
ProductModel prod = new ProductModel();
Product objproduct = new Product();
/*Get the data from databse and prepare the chart record data in string form.*/
objproduct.Year = prod.listToString(prod.getYears());
objproduct.Sale = prod.listToString(prod.getSale());
objproduct.Purchase = prod.listToString(prod.getPurchase());
return objproduct;
}
While i build the Project, It have a error like this:
Server Error in '/' Application.
The model item passed into the dictionary is of type
'System.Data.DataTable', but this dictionary requires a model item of
type 'System.Collections.Generic.IList`1[TLayout.Models.DemoTable]'.
This is my Controller
public ActionResult Index()
{
var dm = new DemoTable();
string connstring = "Server=localhost;Port=5432;User Id=postgres;Password=123456;Database=test";
NpgsqlConnection conn = new NpgsqlConnection(connstring);
conn.Open();
string sql = "select * from demo";
NpgsqlDataAdapter da = new NpgsqlDataAdapter(sql, conn);
ds.Reset();
da.Fill(ds);
dt = ds.Tables[0];
var demoid = dm.demoid.ToString();
var demoname = dm.demoname;
for (int i = 0; i < dt.Rows.Count; i++)
{
List<DataTable> dtb = new List<DataTable>();
demoid = dt.Rows[i]["demoid"].ToString();
demoname = dt.Rows[i]["demoname"].ToString();
dtb.Add(dt);
}
return View(dt);
}
This is my View, to show data to layout:
foreach (var item in Model)
{
fields.Add(Html.X().ModelField().Mapping(#item.demoid.ToString()).Name("grid-alarm"));
fields.Add(Html.X().ModelField().Mapping(#item.demoname.ToString()).Name("grid-check"));
}
var list = dt.AsEnumerable()
.Where(row => (int)row["demoid"] > 5)
.Select(row => new
{
demoid = Convert.ToInt32(row["demoid"]),
demoname = row["demoname"] != null ?
row["demoname"].ToString() :
String.Empty
}).ToList();
Or you can define class:
public class myClass
{
public int demoid;
public string demoname;
}
and then:
List<myClass> list = dt.AsEnumerable()
.Where(row => (int)row["demoid"] > 5)
.Select(row => new myClass
{
demoid = Convert.ToInt32(row["demoid"]),
demoname = row["demoname"] != null ?
row["demoname"].ToString() :
String.Empty
}).ToList<myClass>();
I am sending a standard Sql select statement to my Sql box via the SqlDataAdapter, then populating a DataSet object.
I can access the rows in the resulting DataSet, but how can I convert the DataSet into a List which can be returned to the MVC View. i.e. I'm assuming a List object is the best way to handle this.
Here's my controller c# code:
public class QAController : Controller
{
private readonly static string connString = ConfigurationManager.ConnectionStrings["RegrDBConnection"].ToString();
private readonly static SqlConnection sqlConn = new SqlConnection(connString);
private readonly static SqlCommand sqlComm = new SqlCommand();
public ActionResult Index()
{
DbRegressionExec();
return View();
}
public static void DbRegressionExec()
{
// SELECT TABLE CONTENTS FROM SQL !!
RegressDB_TableList regresDB = new RegressDB_TableList();
string sqlStr = "select * from [RegressionResults].[dbo].[Diff_MasterList] order by TableName";
// POPULATE DATASET OBJECT
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(sqlStr, sqlConn);
da.SelectCommand.CommandType = CommandType.Text;
sqlConn.Open();
try
{
da.Fill(ds, "RegresDB");
}
catch (Exception e)
{
throw;
}
finally
{
sqlConn.Close();
}
// I can iterate thru rows here, but HOW DO CONVERT TO A LIST OBJECT ????
int numRows = ds.Tables["RegresDB"].Rows.Count;
for (int i = 0; i < numRows; i++)
{
string tblName = ds.Tables["RegresDB"].Rows[i].Field<string>("TableName");
}
//List<RegressDB_TableList> masterList = regresDB.RegresTableList.ToList(); //not working !!
//var masterList = regresDB.TableName.ToList(); //
}
}
and a simple class I may need to make this happen:
namespace RegressionMvc.Models
{
public class RegresDB_TableName
{
public string TableName { get; set; }
}
public class RegressDB_TableList
{
public List<RegresDB_TableName> RegresTableList { get; set; }
}
}
In the end, I'm trying to figure out the best way to handle DataSet results from Sql Server and how to make them back to an MVC View.
I can probably go with jQuery and Json, meaning just convert the data fields to Json and return to JQuery, but I'm sure there are several ways to handle Sql based result sets.
Thanks in advance for your advice....
Best,
Bob
In your controller put the code like this
[HttpGet]
public ActionResult View(Modelclass viewmodel)
{
List<Modelclass> employees = new List<Modelclass>();
DataSet ds = viewmodel.GetAllAuthors();
var empList = ds.Tables[0].AsEnumerable().Select(dataRow => new Modelclass{
AuthorId = dataRow.Field<int>("AuthorId"),
Fname = dataRow.Field<string>("FName"),
Lname = dataRow.Field<string>("Lname")
});
var list = empList.ToList();
return View(list);
}
And in view
#{
var gd = new WebGrid(Model, canPage: true, rowsPerPage: 5, selectionFieldName: "selectedRow",ajaxUpdateContainerId: "gridContent");
gd.Pager(WebGridPagerModes.NextPrevious);}
#gd.GetHtml(tableStyle: "table",
columns: gd.Columns(
gd.Column("AuthorId", "AuthorId"),
gd.Column("Fname", " Fname"),
gd.Column("Lname", "Lname", style: "description")
))
Short answer
Directly answering your question:
var tableList = new List<RegresDB_TableName>();
int numRows = ds.Tables["RegresDB"].Rows.Count;
for (int i = 0; i < numRows; i++)
{
string tblName = ds.Tables["RegresDB"].Rows[i].Field<string>("TableName");
tableList.Add(new RegresDB_TableName() { TableName = tblName };
}
return View(tableList);
Long answer (that's actually shorter)
Try out dapper-dot-net.
Your code could change to something like:
string sqlStr = "SELECT * FROM [RegressionResults].[dbo].[Diff_MasterList] ORDER BY TableName";
return sqlConn.Query<RegresDB_TableName>(sqlStr);
If you're stuck with using DAO, I would suggest not using a DataSet and instead use a strongly typed class with the speed of SqlDataReader.GetValues() method. It's more work, but it has to be done somewhere if you want strongly typed classes which I would highly recommend.
public class Person
{
public Person(Object[] values]
{
this.FirstName = (string)values[0];
this.LastName = (string)values[1];
this.Birthday = (DateTime)values[2];
this.HasFavoriteColor = (bool)values[3];
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
public DateTime Birthday { get; private set; }
public bool HasFavoriteColor { get; private set; }
}
public static void DbRegressionExec()
{
List<Person> viewModel = new List<Person>();
// SELECT TABLE CONTENTS FROM SQL !!
RegressDB_TableList regresDB = new RegressDB_TableList();
string sqlStr = "select
FirstName
,LastName
,Birthday
,HasFavoriteColor
from [RegressionResults].[dbo].[Diff_MasterList]
order by TableName";
// POPULATE VIEWMODEL OBJECT
sqlConn.Open();
try
{
using (SqlCommand com = new SqlCommand(sqlStr, sqlConn))
{
using (SqlDbReader reader = com.ExecuteReader())
{
while(reader.Read())
{
viewModel.Add(new Person(com.GetValues()));
}
}
}
}
catch (Exception e)
{
throw;
}
finally
{
sqlConn.Close();
}
return this.View(viewModel);
}
Controller code
//pQ is your query you have created
//P4DAL is the key name for connection string
DataSet ds = pQ.Execute(System.Configuration.ConfigurationManager.ConnectionStrings["Platform4"].ConnectionString);
//ds will be used below
//create your own view model according to what you want in your view
//VMData is my view model
var _buildList = new List<VMData>();
{
foreach (DataRow _row in ds.Tables[0].Rows)
{
_buildList.Add(new VMData
{
//chose what you want from the dataset results and assign it your view model fields
clientID = Convert.ToInt16(_row[1]),
ClientName = _row[3].ToString(),
clientPhone = _row[4].ToString(),
bcName = _row[8].ToString(),
cityName = _row[5].ToString(),
provName = _row[6].ToString(),
});
}
}
//you will use this in your view
ViewData["MyData"] = _buildList;
View
#if (ViewData["MyData"] != null)
{
var data = (List<VMData>)ViewData["MyData"];
<div class="table-responsive">
<table class="display table" id="Results">
<thead>
<tr>
<td>Name</td>
<td>Telephone</td>
<td>Category </td>
<td>City </td>
<td>Province </td>
</tr>
</thead>
<tbody>
#foreach (var item in data)
{
<tr>
<td>#Html.ActionLink(item.ClientName, "_Display", new { id = item.clientID }, new { target = "_blank" })</td>
<td>#item.clientPhone</td>
<td>#item.bcName</td>
<td>#item.cityName</td>
<td>#item.provName</td>
</tr>
}
</tbody>
</table>
</div>
}
I have been having a tough time getting an attendance table for a classroom to be displayed in an appropriate fashion. I have an Attendance table in my database that I can get to display quite easily with the data from just one classroom, but it displays in a way that looks like how it would look if you browsed to the attendance table directly in the database.
It is probably easier for me to explain by showing my work:
I have the following classes:
The actual classroom class, or Course:
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int AttendanceDate { get; set;}
public virtual ICollection<Enrollment> Enrollments { get; set; } // allows Students to be enrolled in a Course
etc. . .
}
My Students:
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; } // allows Student to be enrolled in a Course
etc. . .
}
The entity that ties the Students to the Courses:
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
Attendance data:
public class Attendance
{
public int AttendanceID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public int AttendanceDay { get; set; } // used to set how many days people are supposed to attend this Course (each course has a different length, some are 10 day courses, some are 3, etc.)
public bool Present { get; set; } // absent or present (set to absent by default)
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
The flow of my project has Instructors creating a Course for Students to sign up. When a Student signs up for a Course, all the necessary Attendance data is input into the Attendance database table with default values (absent every day):
for (int i = 0; i < course.AttendingDays; i++)
{
Attendance newAttendance = new Attendance
{
CourseID = course.CourseID,
StudentID = thisStudent.StudentID,
AttendanceDay = i + 1,
Present = false
};
db.Attendance.Add(newAttendance);
db.Save();
}
So I have a database table with a bunch of attendance data and I cannot get it to display correctly on the screen, sorted similar to this:
Attendance Day 1 | 2 | 3 | 4 | 5 |
Student1 absent absent absent absent absent
Student2 absent absent absent absent absent
Student3 absent absent absent absent absent
Hopefully you can read that. . . I assume it is a pretty standard layout for an attendance table.
I have tried sorting the data with Group By:
var model = from s in db.Attendance
where s.CourseID == 4
group s.AttendanceDay by s.StudentID into t
select new
{
StudentID = t.Key,
Days = t.OrderBy(x => x)
};
return View(model);
Which returns an IEnumerable of anonymous type (If I understand correctly), but I cannot seem to get this data to do anything. If I use 'simpler' table generators (and don't try to Group By the AttendanceDay number) I get the attendance table data just fine, but it is sorted just like it is when you view the actual database table for Attendance, not very useful if you want an Instructor to read and edit the information.
I am thinking I need an appropriate ViewModel to adjust the incoming attendance data in the IEnumerable of anonymous type format, followed by a view that appropriately displays that ViewModel. . . but I am not sure how I would handle that process.
Any help would be appreciated. Thank you.
Update:
I am beginning to think I need to take advantage of a "cross-tab report" / "pivoting" and use something like this: http://linqlib.codeplex.com/wikipage?title=Pivot&referringTitle=Home
Any pointers?
Update 2:
Almost completely solved using the below accepted answer, and here is my current controller:
// Generates list of Attendances specifically for current Course
var attendanceItems = db.Attendance.Where(s => s.CourseID == id);
List<Attendance> attendanceItemsList = attendanceItems.ToList();
// End of generating list of Attendances
// CURRENT PROBLEM AREA - INCOMPLETE
var student = attendanceItemsList.Select(a => a.Student).Distinct()/*.OrderBy(a => a)*/; // This works for adding one student, Cannot use OrderBy in its current state - how can I properly order by name? (right now it will order by id I believe)
List<Student> StudentList = student.ToList();;
//
// Generates list of AttendingDays specifically for current Course
Course course = db.Courses.FirstOrDefault(p => p.CourseID == id);
List<int> attDayList = new List<int>();
for (int i = 0; i < course.AttendingDays; i++)
{
attDayList.Add(i + 1);
};
// End of generating list of AttendingDays
AttendanceReportViewModel model = new AttendanceReportViewModel
{
AttendanceDays = attDayList,
Students = StudentList,
Attendances = attendanceItemsList,
};
return View(model);
I think you should look into creating a ViewModel that contains a list of days, a list of students, and a list of attendance entries, then simply iterate over the students and within it over the attendance days and then display the attendance record for each.
So the ViewModel would look something like this:
public class AttendanceReportViewModel
{
public List<int> AttendanceDays { get; set; }
public List<Student> Students { get; set; }
public List<Attendance> Attendances { get; set; }
public string IsPresent(Student student, int attendanceDay)
{
return Attendances.Single(a => a.StudentID == student.ID && a.AttendanceDay == attendanceDay).Present ? "present" : "absent";
}
}
Make sure to sort all items the way you would like them to appear, but this is simple OrderBy stuff. Also make sure to only load Attendance for the course in question and add any course data to the viewModel you want to show.
Then in your view iterate like so:
<table>
<thead>
<tr>
<th>Attendance Day</th>
#foreach (var attendanceDay in Model.AttendanceDays)
{
<th>#attendanceDay</th>
}
</tr>
<thead>
<tbody>
#foreach (var student in Model.Students)
{
<tr>
<td>#student.Name</td>
#foreach (var attendanceDay in Model.AttendanceDays)
{
<td>#Model.IsPresent(student, attendanceDay)</td>
}
</tr>
}
</tbody>
}
Just whacked this together for you, so not even sure if my exact code compiles, but I hope it gives you a good starting point. Obviously some styling to the table, etc. should be applied.
EDIT 1:
To load data into the view model you can just do this in the controller (ultimately this should technically speaking be in repositories and maybe even a service layer)
var viewModel = new AttendanceReportViewModel();
viewModel.AttendanceDays = db.Attendance.Select(a => a.AttendanceDay).Distinct().OrderBy(a => a)
...
I should also have noted that of course you could also only have loaded the attendance data only into the view model and then produced the AttendanceDays and Students properties in the ViewModel from this data. I avoided this for now as you would most likely want Student data as well.
EDIT 2
For students it is just more of the same you want all students enrolled in the class, but since you have initialized attendance data for all days up front you can just do this:
var viewModel = new AttendanceReportViewModel();
viewModel.AttendanceDays = db.Attendance.Select(a => a.AttendanceDay).Distinct().OrderBy(a => a);
viewModel.Students = db.Attendance.Select(a => a.Student).Distinct().OrderBy(s => s.Name);
Alternatively you can just load this information from the Enrollment table like so:
viewModel.Students = db.Enrollment.Where(e => e.CourseID == courseID).Select(e => e.Student).OrderBy(s => s.Name);
Please note that I presume that there is a unique constraint between course and student id on the enrollment table and that courseID is what you produce the attendance report for. Also you have Course in your Enrollment table mapped to Student above, which I would assume is incorrect.
CREATE PROCEDURE [dbo].[SP_S_GetDaywise_EmpTotalDuties_AttendanceRegister]
(
#Month int=null,
#Year int=null,
#SCode bigint=null
)
AS
/**Creating TempAllAttendance**/
CREATE TABLE #TempAllAttendance
(RecId bigint IDENTITY(1,1),EmpCode BIGINT,Duties FLOAT,AttDay INT, DESIGNATION NVARCHAR(250))
/**Creating TempAllAttendance**/
/**Creating SumAttendance**/
CREATE TABLE #SumAttendance
(Total FLOAT)
/**Creating SumAttendance**/
DECLARE #TotalDuties FLOAT=null
BEGIN
SET #TotalDuties=(SELECT SUM(ISNULL(Duties, 0)) AS Total FROM tbl_Attendance WHERE ([Month] = #Month) AND ([Year] = #Year) AND (SCode = #SCode))
INSERT INTO #SumAttendance
(Total)VALUES(#TotalDuties)
INSERT INTO #TempAllAttendance(EmpCode,Duties,AttDay,DESIGNATION)
(
SELECT EmpCode, SUM(ISNULL(Duty1, 0)) AS Duties, DAY(AttendanceDate) AS AttDay,DESIGNATION
FROM tbl_Attendance
WHERE ([Month] = #Month) AND ([Year] = #Year) AND (SCode = #Code)
GROUP BY EmpCode, AttendanceDate,DESIGNATION
)
/**This is used to get date wise & Swise Total duty When Records Not Saved In tbl_SiteWiseEmployee_TotalAttendance**/
/**This is used to get date wise & Sise Total duty When Records Saved In tbl_SiteWiseEmployee_TotalAttendance**/
/**This is used to get date wise & Swise Total duty**/
SELECT * FROM #TempAllAttendance
/**This is used to get date wise & Sitewise Total duty**/
/**This is used to get date wise Total duty**/
SELECT EmpCode,SUM(ISNULL(Duties, 0)) AS Duties,AttDay,DESIGNATION FROM #TempAllAttendance
GROUP BY EmpCode, AttDay,EmpCode,DESIGNATION
/**This is used to get Employee SumAttendance**/
SELECT SUM(Duties) AS Duties,DAY(AttendanceDate) AS AttDay FROM tbl_Attendance
WHERE (SiteCode = #SCode) AND ([Month] = #Month) AND ([Year] = #Year) GROUP BY AttendanceDate
/**This is used to get Employee SumAttendance**/
SELECT * FROM #SumAttendance
/**This is used to get date wise & Sitewise Total duty AS P**/
--SELECT RecId,EmpCode,'P' AS Duties,AttDay FROM #TempAllAttendance
SELECT EmpCode,'P' AS Duties,AttDay FROM #TempAllAttendance
GROUP BY EmpCode, AttDay
/**This is used to get date wise & Swise Total duty AS P**/
DROP TABLE #SumAttendance
DROP TABLE #TempAllAttendance
END
private void STotalEmployeeAttendenceReportAsDuty()
{
#region For Displaying page Header region
lblMonthId.Value = Request.QueryString["Month"];
lblMonthName.Value = Request.QueryString["MonthName"];
lblYear.Value = Request.QueryString["year"];
lblSCode.Value = Request.QueryString["SCode"];
lblgvSName.Value = Request.QueryString["SName"];
lblMonth.InnerText = lblMonthName.Value;
lblYears.InnerText = lblYear.Value;
lblSName.InnerText = lblgvSiName.Value + "-" + lblSCode.Value;
#endregion
#region Get SWise Employee Detail
siAttndncPL.SiteCode = lblSiteCode.Value;
sAttndncPL.Month = lblMonthId.Value;
siAttndncPL.Year = lblYear.Value;
DataTable EmpDt = siteAttndncBL.GetEmployeeNameWithTotalDutiesBySit(siteAttndncPL);
#endregion
#region Making TempTable.
DataTable dt = new DataTable();
DataColumn dc = new DataColumn();
dc.ColumnName = "EmpCode";
dt.Columns.Add(dc);
dc = new DataColumn();
dc.ColumnName = "EmpName";
dt.Columns.Add(dc);
dc = new DataColumn();
dc.ColumnName = "Designation";
dt.Columns.Add(dc);
/**Some Table**/
DataTable dtdayDuties = new DataTable();
DataTable dtEmpDayDuties = new DataTable();
DataTable dtEmpDutiesAsP = new DataTable();
DataTable dtSumDuty = new DataTable();
/**Some Region**/
#endregion
#region Get No Of Days In Month..
int DaysCount = DateTime.DaysInMonth(Convert.ToInt32(Request.QueryString["Year"]), Convert.ToInt32(Request.QueryString["Month"]));
#endregion
#region This will dispaly date value on grid header
for (int i = 1; i <= 9; i++)
{
dc = new DataColumn();
dc.ColumnName = "0" + i.ToString();
dt.Columns.Add(dc);
}
for (int i = 10; i <= DaysCount; i++)
{
dc = new DataColumn();
dc.ColumnName = i.ToString();
dt.Columns.Add(dc);
}
dc = new DataColumn();
dc.ColumnName = "Total";
dt.Columns.Add(dc);
#endregion
#region /*Adding Site Name Row in Grid View*/
for (int j = 0; j < EmpDt.Rows.Count + 1; j++)
{
DataRow dtrow = dt.NewRow();
if (j < EmpDt.Rows.Count)
{
dtrow[0] = EmpDt.Rows[j][0];/**this Row Cells EmpCode**/
string EmpCode = EmpDt.Rows[j][0].ToString();
// lblEmpCode = EmpDt.Rows[j][0].ToString();
dtrow[1] = EmpDt.Rows[j][1];/**this Row Cells EmpName**/
dtrow[2] = EmpDt.Rows[j][2];/**this Row Cells DESsgName**/
dtrow[DaysCount + 3] = EmpDt.Rows[j][3];/**this Row Cells Duties **/
#region Get EmployeeWise Total Duties.
siteAttndncPL.SiteCode = lblSiteCode.Value;
siteAttndncPL.Month = lblMonthId.Value;
siteAttndncPL.Year = lblYear.Value;
DataSet ds = siteAttndncBL.GetEmployeeDayWiseAttendenceBySCode_WOPHD(siteAttndncPL);
dtEmpDutiesAsP = ds.Tables["tbl_GetEmpTotalAttendanceOfDayAsP"];/*getting Site Duties as P*/
dtEmpDayDuties = ds.Tables["tbl_GetEmpTotalAttendanceOfDay"];
dtdayDuties = ds.Tables["tbl_SumOfAllEmpDayWiseDuties"];
dtSumDuty = ds.Tables["tbl_TotalSumOfAllEmpAttendance"];
TotalSumDuties = Convert.ToDouble(dtSumDuty.Rows[0]["Total"].ToString());
#endregion
}
if (j == EmpDt.Rows.Count)
{
#region Count Total Duties.
dtrow[2] = "Total Duties";
dtrow[DaysCount + 3] = TotalSumDuties;/**Sum Of All Main Attendance**/
#endregion
}
dt.Rows.Add(dtrow);
}
#endregion
/**Fill Day Wise Attendance In Gridview**/
#region Day Wise Attendance fill In Gridview..
for (int i = 0; i < dt.Rows.Count; i++)
{
if (i < dt.Rows.Count - 1)
{
for (int j = 3; j < dt.Columns.Count; j++)
{
foreach (DataRow dtrows in dtEmpDayDuties.Rows)
{
/*Matching Emp Code*/
if (dt.Rows[i][0].ToString() == dtrows[0].ToString())
{
/*Matching Emp Category */
if (dt.Rows[i][2].ToString() == dtrows[3].ToString())
{
TextBox txtDuty = new TextBox();
txtDuty.Text = dt.Columns[j].ColumnName.ToString();
txtDuty.Text = dtrows[2].ToString();
/*Matching Date*/
if (Convert.ToString(j - 2) == dtrows[2].ToString())
{
dt.Rows[i][j] = dtrows[1].ToString(); /*Filling Days wise duty*/
}
}
/*Matching Emp Category */
}
}
}
}
if (i == dt.Rows.Count - 1)
{
for (int j = 3; j < dt.Columns.Count; j++)
{
foreach (DataRow dtrows in dtdayDuties.Rows)
{
TextBox txtDuty = new TextBox();
txtDuty.Text = dt.Columns[j].ColumnName.ToString();
txtDuty.Text = dtrows[1].ToString();
if (Convert.ToString(j - 2) == dtrows[1].ToString())
{
dt.Rows[i][j] = dtrows[0].ToString();
}
}
}
}
}
#endregion
/**Fill Day Wise Attendance In Gridview**/
#region Binding Grid
grdSWiseEmpAttendance.DataSource = dt;
grdSWiseEmpAttendance.DataBind();
#endregion Binding Grid
ViewState["tbldata"] = dt;/*used For Saving Employee Monthly Attendance*/
Session["reportdata"] = dt;/*used For Printing Employee Monthly Attendance*/
lblNoOfEmp.InnerText = (grdSWiseEmpAttendance.Rows.Count - 1).ToString();
}
void Page_Init(object obj, EventArgs e)
{
int days = DateTime.DaysInMonth(Convert.ToInt32(Request.QueryString["Year"]), Convert.ToInt32(Request.QueryString["Month"]));
int dtinc = 0;
for (int rowcnt = 0; rowcnt < days + 4; rowcnt++)
{
if (rowcnt == 0)
{
BoundField nameColumn = new BoundField();
nameColumn.DataField = "EmpCode";
nameColumn.HeaderText = "EmpCode";
grdSiteWiseEmpAttendance.Columns.Add(nameColumn);
}
else if (rowcnt == 1)
{
BoundField nameColumn = new BoundField();
nameColumn.DataField = "EmpName";
nameColumn.HeaderText = "EmpName";
grdSiteWiseEmpAttendance.Columns.Add(nameColumn);
}
else if (rowcnt == 2)
{
BoundField nameColumn = new BoundField();
nameColumn.DataField = "DESIGNATION";
nameColumn.HeaderText = "DESIGNATION";
grdSiteWiseEmpAttendance.Columns.Add(nameColumn);
}
else if (rowcnt == days + 3)
{
BoundField nameColumn = new BoundField();
nameColumn.DataField = "Total";
nameColumn.HeaderText = "Total";
grdSiteWiseEmpAttendance.Columns.Add(nameColumn);
nameColumn.ItemStyle.HorizontalAlign = HorizontalAlign.Center;
}
else
{
dtinc = dtinc + 1;
string formet = "00";
TemplateField bfield = new TemplateField();
/**Initalize the DataField value**/
bfield.HeaderTemplate = new GridViewTemplate2(ListItemType.Header, Convert.ToString(dtinc.ToString(formet)));
/**Initialize the HeaderText field value**/
bfield.ItemTemplate = new GridViewTemplate2(ListItemType.Item, Convert.ToString(dtinc.ToString(formet)));
/**Add the newly created bound field to the GridView**/
grdSEmpAttendance.Columns.Add(bfield);
}
}
}
<div class="row">
<div class="col-lg-12">
<div class="table-responsive">
<asp:GridView ID="grdSiEmpAttendance" CssClass="table table-small-font table-bordered table-striped" Font-Size="Smaller" EmptyDataRowStyle-ForeColor="#cc0000" HeaderStyle-Font-Size="10" HeaderStyle-Font-Names="Arial" HeaderStyle-Font-Italic="true" runat="server" AutoGenerateColumns="false"
BackColor="#f0f5f5" HeaderStyle-ForeColor="#990000">
<Columns>
</Columns>
<HeaderStyle HorizontalAlign="Justify" VerticalAlign="Top"
Font-Bold="true" />
<RowStyle Font-Size="Small" Height="1" ForeColor="#000000" Font-Italic="true" />
</asp:GridView>
</div>
</div>
</div>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI;
public class GridViewTemplate2 : ITemplate
{
/**A variable to hold the type of ListItemType**/
ListItemType _templateType;
/**A variable to hold the column name**/
string _columnName;
/**Constructor where we define the template type and column Name**/
public GridViewTemplate2(ListItemType type, string colname)
{
_templateType = type; /*Stores the template type*/
_columnName = colname;/*Stores the column name*/
}
void ITemplate.InstantiateIn(System.Web.UI.Control container)
{
switch (_templateType)
{
#region For Use TemplateFields.
case ListItemType.Header:
/*Create a New Label control and add it to the container*/
Label lbl = new Label();/*Allocates the new label object*/
lbl.Text = _columnName;/*Assigns the name of the column in the lable*/
lbl.Font.Name = "Calibri";
//lbl.CssClass = "form-label";
//lbl.Font.Bold = true;
container.Controls.Add(lbl);/*Adds the New created label control to the container*/
break;
case ListItemType.Item:
/**Creates a New Text box Control And Add It to the container**/
TextBox tb1 = new TextBox();/*Allocates the new text box object*/
tb1.DataBinding += new EventHandler(tb1_DataBinding);/*Attaches the data binding event*/
tb1.CssClass = "vertical-text";
string bxid = "txt" + _columnName;
tb1.ID = bxid.Trim().ToString();
tb1.Width = new Unit(35, UnitType.Pixel);
tb1.Height = new Unit(15, UnitType.Pixel);
//tb1.Style.Add("font-size", "8pt;");
tb1.Font.Name = "Calibri";
tb1.Columns = 3; /**Creates a column with size 4**/
container.Controls.Add(tb1);/**Adds the New created textbox to the container**/
break;
case ListItemType.EditItem:
break;
case ListItemType.Footer:
CheckBox chkColumn = new CheckBox();
chkColumn.ID = "Chk" + _columnName;
container.Controls.Add(chkColumn);
break;
#endregion
}
}
/// <summary>
/// This is the event, which will be raised when the binding happens.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
///
void tb1_DataBinding(object sender, EventArgs e)
{
#region For Use DataBinding Fields..
TextBox txtdata = (TextBox)sender;
GridViewRow container = (GridViewRow)txtdata.NamingContainer;
object dataValue = DataBinder.Eval(container.DataItem, _columnName);
if (dataValue != DBNull.Value)
{
if (dataValue.ToString().Trim() == "0".ToString() || dataValue.ToString().Trim()=="")
{
txtdata.Text = "";
//txtdata.BackColor = System.Drawing.Color.BlanchedAlmond;
//txtdata.ReadOnly = true;
txtdata.Width = 35;
txtdata.Height = 25;
txtdata.CssClass = "form-control input-sm m-bot15";
txtdata.Font.Size = 8;
}
if (dataValue.ToString().Trim() == "777" || dataValue.ToString().Trim() == "L")
{
txtdata.Text = "L";
txtdata.BackColor = System.Drawing.Color.SandyBrown;
//txtdata.ReadOnly = true;
txtdata.Width = 35;
txtdata.Height = 25;
txtdata.CssClass = "form-control input-sm m-bot15";
txtdata.Font.Size = 7;
}
if (dataValue.ToString().Trim() == "888" || dataValue.ToString().Trim() == "WO")
{
txtdata.Text = "WO";
txtdata.BackColor = System.Drawing.Color.LightGreen;
//txtdata.ReadOnly = true;
txtdata.Width = 36;
txtdata.Height = 25;
txtdata.CssClass = "form-control input-sm m-bot15";
txtdata.Font.Size = 7;
}
if (dataValue.ToString().Trim() == "999" || dataValue.ToString().Trim() == "PH")
{
txtdata.Text = "PH";
txtdata.BackColor = System.Drawing.Color.Cornsilk;
//txtdata.ReadOnly = true;
txtdata.Width = 35;
txtdata.Height = 25;
txtdata.CssClass = "form-control input-sm m-bot15";
txtdata.Font.Size = 7;
}
if (dataValue.ToString().Trim() == "1111" || dataValue.ToString().Trim() == "CL")
{
txtdata.Text = "CL";
txtdata.BackColor = System.Drawing.Color.Bisque;
//txtdata.ReadOnly = true;
txtdata.Width = 35;
txtdata.Height = 25;
txtdata.CssClass = "form-control input-sm m-bot15";
txtdata.Font.Size = 7;
}
if ((dataValue.ToString().Trim() != "1111") && (dataValue.ToString().Trim() != "CL") && (dataValue.ToString().Trim() != "2997") && (dataValue.ToString().Trim() != "HD") && (dataValue.ToString().Trim() != "777") && (dataValue.ToString().Trim() != "L") && (dataValue.ToString().Trim() != "888") && (dataValue.ToString().Trim() != "W/O") (dataValue.ToString().Trim() != "") && (dataValue.ToString().Trim() != "1")&&
{
txtdata.ReadOnly = false;
txtdata.BackColor = System.Drawing.Color.LightGoldenrodYellow;
txtdata.Text = dataValue.ToString();
txtdata.Width = 35;
txtdata.Height = 25;
txtdata.CssClass = "form-control input-sm m-bot15";
txtdata.Font.Size = 7;
txtdata.Font.Bold = true;
}
if ((dataValue.ToString().Trim() != "1111") && (dataValue.ToString().Trim() != "CL") && (dataValue.ToString().Trim() != "2997") && (dataValue.ToString().Trim() != "HD") && (dataValue.ToString().Trim() != "777") && (dataValue.ToString().Trim() != "L") && (dataValue.ToString().Trim() != "888") && (dataValue.ToString().Trim() != "WO") && (dataValue.ToString().Trim() != "1776") && (dataValue.ToString().Trim() != "") && (dataValue.ToString().Trim() != "3000") && (dataValue.ToString().Trim() != "T") && (dataValue.ToString().Trim() != "1000") &&
{
txtdata.ReadOnly = false;
txtdata.Text = dataValue.ToString();
txtdata.Width = 35;
txtdata.Height = 25;
txtdata.CssClass = "form-control input-sm m-bot15";
txtdata.Font.Size = 7;
txtdata.Font.Bold = true;
}
//else
//{
// txtdata.ReadOnly = false;
// txtdata.Text = dataValue.ToString();
// txtdata.Width = 35;
// txtdata.Height = 25;
// txtdata.CssClass = "form-control input-sm m-bot15";
// txtdata.Font.Size = 8;
// txtdata.Font.Bold = true;
//}
}
else
{
txtdata.Text ="";
txtdata.Width = 35;
txtdata.Height = 25;
txtdata.CssClass = "form-control input-sm m-bot15";
txtdata.Font.Size = 8;
txtdata.Font.Bold = true;
}
#endregion
}
}