Reporting in MVC using an IReportRepository - asp.net-mvc

I am working on an existing system and need to add a report via a stored procedure.
I just want to sort out some errors first.
In IReportingRepository.cs I have:
public interface IReportingRepository
{
IEnumerable<LastSundayDate<DateTime>> GetTradeMeKPISearches();
}
the first error is on the line above the LastSundayDate> part and says:
The non-generic type 'TradeUK.Entities.Reporting.LastSundayDate' cannot be used with type arguments
I have a class LastSundayDate.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TradeUK.Entities.Reporting
{
public class LastSundayDate
{
public virtual DateTime Date { get; set; }
}
}
In ReportingServies.cs I have:
public IEnumerable<LastSundayDate> GetTradeUKKPISearches()
{
return ReportingRepository.GetTradeUKKPISearches();
}
and then in ReportingRepository.cs I have:
public IEnumerable<LastSundayDate<DateTime>> GetTradeMeKPISearches()
{
try
{
using (SqlConnection conn = new SqlConnection(TradeUKModelContainer.CONNECTIONSTRING))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "[Reporting].[GetTradeMEKPISearches]";
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
da.Fill(dt);
List<LastSundayDate<DateTime>> results = new List<LastSundayDate<DateTime>>();
foreach (DataRow dr in dt.Rows)
{
string title = dr["Title"].ToString();
int count = Convert.ToInt32(dr["Total"]);
results.Add(new LastSundayDate<>());
}
}
}
}
}
catch (Exception ex)
{
throw new Exception("ReportingRepository GetTradeUKKPISearches: Error", ex);
}
}
why can i not have the type as DateTime?
thanks

Your class
public class LastSundayDate
{
public virtual DateTime Date { get; set; }
}
is not generic, so you cannot use it as LastSundayDate<DateTime>.
Why don't you write
public interface IReportingRepository
{
IEnumerable<LastSundayDate> GetTradeMeKPISearches();
}

Related

Stored procedure ADO.NET .NET Core Web API

I am building a Web API in ASP.NET Core, I am using stored procedures to be able to handle more complex queries, which with the Entity Framework is too complicated for me, I am using ADO.NET to make this connection.
I have managed to connect to a stored procedure and use the get and post methods, the point is that I don't know how to do it in order to call the other stored procedures and map a route to interact via get or post in the same project. I have only been able to do one, and I don't think it would be more convenient to create a Web API for each function that complies with a stored procedure.
My project is made up of three folders called Controller, Data, Models.
Within Models is the Value class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNETCore_StoredProcs.Models
{
public class Value
{
public int Id { get; set; }
public int Value1 { get; set; }
public string Value2 { get; set; }
}
}
Data folder has a class called ValueRepository
using ASPNETCore_StoredProcs.Models;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNETCore_StoredProcs.Data
{
public class ValuesRepository
{
private readonly string _connectionString;
public ValuesRepository(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("defaultConnection");
}
public async Task<List<Value>> GetAll()
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetAllValues", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var response = new List<Value>();
await sql.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
response.Add(MapToValue(reader));
}
}
return response;
}
}
}
private Value MapToValue(SqlDataReader reader)
{
return new Value()
{
Id = (int)reader["Id"],
Value1 = (int)reader["Value1"],
Value2 = reader["Value2"].ToString()
};
}
public async Task<Value> GetById(int Id)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetValueById", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id", Id));
Value response = null;
await sql.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
response = MapToValue(reader);
}
}
return response;
}
}
}
public async Task Insert(Value value)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("InsertValue", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#value1", value.Value1));
cmd.Parameters.Add(new SqlParameter("#value2", value.Value2));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
public async Task DeleteById(int Id)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("DeleteValue", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id", Id));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
}
}
and finally I have a controller class called ValuesController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCore_StoredProcs.Data;
using ASPNETCore_StoredProcs.Models;
using Microsoft.AspNetCore.Mvc;
namespace ASPNETCore_StoredProcs.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ValuesRepository _repository;
public ValuesController(ValuesRepository repository)
{
this._repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
// GET api/values
[HttpGet]
public async Task<ActionResult<IEnumerable<Value>>> Get()
{
return await _repository.GetAll();
}
// GET api/values/5
[HttpGet("{id}")]
public async Task<ActionResult<Value>> Get(int id)
{
var response = await _repository.GetById(id);
if (response == null) { return NotFound(); }
return response;
}
// POST api/values
[HttpPost]
public async Task Post([FromBody] Value value)
{
await _repository.Insert(value);
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public async Task Delete(int id)
{
await _repository.DeleteById(id);
}
}
}
This is my startup class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCore_StoredProcs.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace ASPNETCore_StoredProcs
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ValuesRepository>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
I appreciate if you can help me or if it is how I think I should create an API for each procedure that is performed thanks

Syncfusion Server-Side event is not passing data

I have a Asp.Net MVC project created from Syncfusion ASP.New MVC (Essential JS 2) VS template that is using Syncfusion's Data Grid. I can get the CrudUpdate event set in CrudUrl to fire at the server, however the value returned to CrudUpdate is empty. action parameter seems correctly set.
If I cast the value as Object, I get back a not-null, but VS cannot interrogate it. My guess is some weirdness in the way the value is cast or returned.
Has anyone got a complete working sample of the Syncfusion grid using the CrudUrl method with MVC (not asp). I am also guessing I may have some dependency issue.
View
#Html.EJS().Grid("CrudUrl").DataSource(dataManager => { dataManager.Url("/TestGrid2/UrlDatasource").CrudUrl("/TestGrid2/CrudUpdate").Adaptor("UrlAdaptor"); }).Columns(col =>
{
col.Field("RowKey").IsPrimaryKey(true).Add();
col.Field("PartitionKey").Add();
col.Field("sourceResourceId").Add();
col.Field("imagesLocation").Add();
col.Field("imagesResourceGroup").Add();
col.Field("imagePrefix").Add();
col.Field("imageVersion").Add();
}).AllowPaging().Toolbar(new List<string>() { "Search", "Add", "Edit", "Delete", "Update", "Cancel" }).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true); }).Render()
Controller
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Script.Services;
using System.Web.Services;
using DB;
//using Microsoft.AspNetCore.Mvc;
using Syncfusion.EJ2.Base;
namespace VMSSManagmentConsole.Controllers
{
public class TestGrid2Controller : Controller
{
private ModelContainer db = new ModelContainer();
public ActionResult TestGrid2()
{
//var items = db.ManagementItems.ToList();
//ViewBag.dataSource = items;
return View();
}
public ActionResult UrlDatasource([FromBody]DataManagerRequest dm)
{
IEnumerable DataSource = db.ManagementItems.ToList();
DataOperations operation = new DataOperations();
int count = DataSource.Cast<ManagementItem>().Count();
if (dm.Skip != 0)
{
DataSource = operation.PerformSkip(DataSource, dm.Skip); //Paging
}
if (dm.Take != 0)
{
DataSource = operation.PerformTake(DataSource, dm.Take);
}
var result = (ActionResult)(dm.RequiresCounts ? Json(new { result = DataSource, count = count }) : Json(DataSource));
return result;
}
public ActionResult CrudUpdate([FromBody]ICRUDModel<ManagementItem> value, string action)
{
//if (value.action == "update")
//{
// var ord = value.value;
// ManagementItem val = db.ManagementItems.Where(or => or.RowKey == ord.RowKey).FirstOrDefault();
// val.imagePrefix = ord.imagePrefix;
// val.imagesLocation = ord.imagesLocation;
// val.imagesResourceGroup = ord.imagesResourceGroup;
// val.imageVersion = ord.imageVersion;
// val.sourceResourceId = ord.sourceResourceId;
//}
//else if (value.action == "insert")
//{
// db.ManagementItems.Add(value.value);
//}
//else if (value.action == "remove")
//{
// db.ManagementItems.Remove(db.ManagementItems.Where(or => or.RowKey == value.key.ToString()).FirstOrDefault());
// return Json(value);
//}
//return Json(value.value);
return null;
}
public class ICRUDModel<T> where T : class
{
public string action { get; set; }
public string table { get; set; }
public string keyColumn { get; set; }
public object key { get; set; }
public T value { get; set; }
public List<T> added { get; set; }
public List<T> changed { get; set; }
public List<T> deleted { get; set; }
public IDictionary<string, object> #params { get; set; }
}
}
}
Use the DataGrid scaffold wizard. On the third page select DataSourceType = "Remote Data". A page and controller will be created with the correct code.
For your reference, we have created a sample and perform CRUD actions. Please refer the attached sample for more information.
Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/GridEJ2Mvc-914352281
The reported problem occurred when model mismatched. For Example, when we specify the number column field(EmployeeID) in grid but while inserting you did not specify the value for that column then it shows value as null in CrudUpdate, for this scenario you need to specify the nullable value for that field in model class as follows.
public class OrdersDetails
{
public OrdersDetails(int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, DateTime ShippedDate, string ShipAddress)
{
this.OrderID = OrderID;
this.CustomerID = CustomerId;
this.EmployeeID = EmployeeId;
. . . . .
}
public static List<OrdersDetails> GetAllRecords()
{
if (order.Count() == 0)
{
int code = 10000;
for (int i = 1; i < 10; i++)
{
order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6"));
. . . . .
}
}
return order;
}
public int? OrderID { get; set; }
public string CustomerID { get; set; }
public int? EmployeeID { get; set; } // it accept null value
. . . . .
}
}
If you still face the problem then share more details or below information that will helpful for us to validate further and provide a better solution as soon as possible.
• Did the problem occurred for both update and insert?
• Share package version details.
Regards,
Thavasianand S.

How to populate google charts using SQL Server Database in MVC Razor

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;
}

How can I incorporate this Castle Windsor DI code into my Controller and Repository code?

Note: I can't bountify this question yet (it's too new), but I will reward a good answer with 50 points, and a great answer with 100 (when possible).
I need to incorporate DI into my Web API project. I currently have the expected Model and Controller folders/classes, along with corresponding Repository classes.
That seemed to work well for awhile, but now I need to use DI with the Controllers so that I can pass an Interface type to the Controllers' constructor.
I'm struggling with just how to implement this; that is, how to incorporate the DI "extravaganza" into my existing Model/Controller/Repository structure. I have example DI code, but I don't know just how it should be applied to my project.
Perhaps some code is in order to try to make this clear. I will show a simple sample of what I've got, followed by the DI code I'd like to somehow incorporate into it / with it.
Here is the existing Model/Controller/Repository code:
MODEL
public class Department
{
public int Id { get; set; }
public int AccountId { get; set; }
public string Name { get; set; }
}
CONTROLLER
public class DepartmentsController : ApiController
{
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
public int GetCountOfDepartmentRecords()
{
return _deptsRepository.Get();
}
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch)
{
return _deptsRepository.Get(ID, CountToFetch);
}
public void PostDepartment(int accountid, string name)
{
_deptsRepository.PostDepartment(accountid, name);
}
public HttpResponseMessage Post(Department department)
{
// Based on code 2/3 down http://www.codeproject.com/Articles/344078/ASP-NET-WebAPI-Getting-Started-with-MVC4-and-WebAP?msg=4727042#xx4727042xx
department = _deptsRepository.Add(department);
var response = Request.CreateResponse<Department>(HttpStatusCode.Created, department);
string uri = Url.Route(null, new { id = department.Id });
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
}
REPOSITORY
public class DepartmentRepository : IDepartmentRepository
{
private readonly List<Department> departments = new List<Department>();
public DepartmentRepository()
{
using (var conn = new OleDbConnection(
#"Provider=Microsoft.ACE.OLEDB.12.0;User ID=BlaBlaBla...
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT td_department_accounts.dept_no,
IIF(ISNULL(t_accounts.name),'No Name provided',t_accounts.name) AS name
FROM t_accounts INNER JOIN td_department_accounts ON
t_accounts.account_no = td_department_accounts.account_no ORDER BY
td_department_accounts.dept_no";
cmd.CommandType = CommandType.Text;
conn.Open();
int i = 1;
using (OleDbDataReader oleDbD8aReader = cmd.ExecuteReader())
{
while (oleDbD8aReader != null && oleDbD8aReader.Read())
{
int deptNum = oleDbD8aReader.GetInt16(0);
string deptName = oleDbD8aReader.GetString(1);
Add(new Department { Id = i, AccountId = deptNum, Name,
deptName });
i++;
}
}
}
}
}
public int Get()
{
return departments.Count;
}
private Department Get(int ID) // called by Delete()
{
return departments.First(d => d.Id == ID);
}
public IEnumerable<Department> Get(int ID, int CountToFetch)
{
return departments.Where(i => i.Id > ID).Take(CountToFetch);
}
public Department Add(Department dept)
{
if (dept == null)
{
throw new ArgumentNullException("Department arg was null");
}
// This is called internally, so need to disregard Id vals that already exist
if (dept.Id <= 0)
{
int maxId = departments.Max(d => d.Id);
dept.Id = maxId + 1;
}
if (departments != null) departments.Add(dept);
return dept;
}
public void PostDepartment(int accountid, string name)
{
int maxId = departments.Max(d => d.Id);
Department dept = new Department();
dept.Id = maxId + 1;
dept.AccountId = accountid;
dept.Name = name;
departments.Add(dept);
}
public void Post(Department department)
{
int maxId = departments.Max(d => d.Id);
department.Id = maxId + 1;
departments.Add(department);
}
public void Put(Department department)
{
int index = departments.ToList().FindIndex(p => p.Id == department.Id);
departments[index] = department;
}
public void Put(int id, Department department)
{
int index = departments.ToList().FindIndex(p => p.Id == id);
departments[index] = department;
}
public void Delete(int id)
{
Department dept = Get(id);
departments.Remove(dept);
}
And now here is the DI code that I want to incorporate
Classes in the DIInstallers folder:
IDepartmentProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HandheldServer.DIInstallers
{
public interface IDepartmentProvider
{
// These are the methods that are in the sample example IAuthProvider interface; I don't know what I need yet, though...
//bool Authenticate(string username, string password, bool createPersistentCookie);
//void SignOut();
}
}
DepartmentProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HandheldServer.DIInstallers
{
public class DepartmentProvider : IDepartmentProvider
{
// TODO: Implement methods in IDepartmentProvider, once they have been added
}
}
DepartmentProviderInstaller.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
namespace HandheldServer.DIInstallers
{
public class DepartmentProviderInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn(typeof(IDepartmentProvider))
.WithServiceAllInterfaces());
// If I declare/implement more interface types (other than IDepartmentProvider), I assume there would be another container.Register() call for each of them?
}
}
}
Classes in the DIPlumbing folder:
WindsorCompositionRoot.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Castle.Windsor;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
namespace HandheldServer.DIPlumbing
{
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer container;
public WindsorCompositionRoot(IWindsorContainer container)
{
this.container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)this.container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
}
}
WindsorControllerFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;
namespace HandheldServer.DIPlumbing
{
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)kernel.Resolve(controllerType);
}
}
}
The Global.asax.cs file
using System;
using System.Reflection;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Castle.Windsor;
using Castle.Windsor.Installer;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.Http.Dispatcher;
using HandheldServer.DIPlumbing;
namespace HandheldServer
{
public class WebApiApplication : System.Web.HttpApplication
{
private static IWindsorContainer container;
protected void Application_Start()
{
BootstrapContainer();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
// Code that runs when an unhandled error occurs
void Application_Error(object sender, EventArgs e)
{
// Get the exception object.
Exception exc = Server.GetLastError();
log.Error(exc.Message);
// Clear the error from the server
Server.ClearError();
}
private static void BootstrapContainer()
{
container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot(container));
}
protected void Application_End()
{
container.Dispose();
}
}
}
So, I think I've basically got the code I need, but how to fold the DI code into my previous (Model/Controller/Repository) code is the part that has me stumped.
You can simply use WebApiContrib.IoC.CastleWindsor (Nuget).
This test should give you an idea of how to use it.

ViewModel in MVC correct way of accessing data

Can anyone please tell me whether this is the correct way of creating a viewmodel. I'm using Ninject and the only way I can get the view model to work is with the code below.
Also I cannot seem to be able to pass the data from the viewmodel to the controller unless I create a 2nd interface.
The code below does work, but reading all the examples I have seen I seem to be duplicating a lot off code from my domain layer.
---------------------Code Data access layer------
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
namespace Web.Domain.SearchEngine
{
public class DisplaySearchResults
{
public string Title { get; set; }
public string Description { get; set; }
public string URL { get; set; }
}
public class GetSearchResults : IGetSearchResults
{
private string dbConn;
public GetSearchResults()
{
dbConn = ConfigurationManager.ConnectionStrings["Search"].ConnectionString;
}
public IEnumerable<DisplaySearchResults> SearchResults(string q, string option, int pagenumber)
{
List<DisplaySearchResults> Data = new List<DisplaySearchResults>();
string spName = "dbo.FTS_On_at_Websites";
using (SqlConnection cn = new SqlConnection(dbConn))
{
using (SqlCommand cmd = new SqlCommand(spName, cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#strSearchPhrase", SqlDbType.VarChar, 100));
cmd.Parameters.Add(new SqlParameter("#SearchMode", SqlDbType.Int, 4));
cmd.Parameters.Add(new SqlParameter("#intPageNumber", SqlDbType.Int));
cmd.Parameters.Add(new SqlParameter("#intRecordsPerPage", SqlDbType.Int));
cmd.Parameters.Add(new SqlParameter("#intTotalRecordsReturned", SqlDbType.Int));
cmd.Parameters["#strSearchPhrase"].Value = q;
cmd.Parameters["#SearchMode"].Value = 1;
cmd.Parameters["#intPageNumber"].Value = pagenumber;
cmd.Parameters["#intRecordsPerPage"].Value = 10;
cmd.Parameters["#intTotalRecordsReturned"].Value = 10;
cn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.Default))
{
if (rdr.HasRows)
{
while (rdr.Read())
{
Data.Add(new DisplaySearchResults
{
Title = (string)rdr["PageTitle"],
Description = (string)rdr["PageParagraph"],
URL = (string)rdr["PageURL"]
});
}
}
return Data;
}
}
}
}
}
}
-------------Code ViewModel--------------------
using Microsoft.Security.Application;
using System.Collections.Generic;
using System.Linq;
using Web.Domain.SearchEngine;
namespace Web.UI.ModelHelpers.Search
{
public class DisplaySearchResultsViewModel
{
public string Title { get; set; }
public string Description { get; set; }
public string URL { get; set; }
}
public class GetSearchResultsViewModel : IGetSearchResultsViewModel
{
private readonly IGetSearchResults _IGSR;
public GetSearchResultsViewModel(IGetSearchResults IGSR)
{
_IGSR = IGSR;
}
public IEnumerable<DisplaySearchResultsViewModel> SearchResultsViewModel(string q, string option, int pagenumber)
{
var searchResults = _IGSR.SearchResults(q, option, pagenumber).AsEnumerable();
List<DisplaySearchResultsViewModel> GetData = new List<DisplaySearchResultsViewModel>();
foreach (var details in searchResults.AsEnumerable())
{
GetData.Add(new DisplaySearchResultsViewModel()
{
Title = Sanitizer.GetSafeHtmlFragment(details.Title),
Description = Sanitizer.GetSafeHtmlFragment(details.Description).ToLower(),
URL = Sanitizer.GetSafeHtmlFragment(details.URL),
});
}
return GetData;
}
}
}
In controller I have
var DisplaySearchResults = _IGSR.SearchResultsViewModel(cleanText, "1", 1);
No, this is not the correct way to build a view model. A view model should not contain any data access logic in it. That's the responsibility of the model.
What you should do instead is use Ninject to inject the IGetSearchResults instance into your controller instead of having your GetSearchResultsViewModel view model take it as constructor dependency. Actually you do not need this GetSearchResultsViewModel at all. You already have the correct view model called DisplaySearchResultsViewModel. Then it's the responsibility of your controller to use your data access layer and build this view model.
For example:
public class SomeController : Controller
{
private readonly IGetSearchResults repository;
public SomeController(IGetSearchResults repository)
{
this.repository = repository;
}
public ActionResult SomeAction(string q, string option, int pagenumber)
{
// query your data access layer and build the view model that you will
// pass to the view
IEnumerable<DisplaySearchResultsViewModel> model = this.repository
.SearchResults(q, option, pagenumber)
.AsEnumerable()
.Select(details => new DisplaySearchResultsViewModel
{
Title = Sanitizer.GetSafeHtmlFragment(details.Title),
Description = Sanitizer.GetSafeHtmlFragment(details.Description).ToLower(),
URL = Sanitizer.GetSafeHtmlFragment(details.URL)
})
.ToList();
return View(model);
}
}

Resources