ViewModel in MVC correct way of accessing data - asp.net-mvc

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

Related

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.

BreezeSharp Attach Property key not found

I'm implementing an application with Breezesharp. I ran into a issue when insert the entity in the EntityManager. The error is:
There are no KeyProperties yet defined on EntityType: 'TransportReceipt:#Business.DomainModels'
I already faced this error with my first entity type "Customer" and implement a mismatching approach as suggested here. In that case I made the get operation against my WebApi with success. But now I'm creating the TransportReceipt entity inside my application.
Mapping mismatch fix
public static class ExtendMap
{
private static bool? executed;
public static void Execute(MetadataStore metadataStore) {
if (ExtendMap.executed == true)
{
return;
}
var customerBuilder = new EntityTypeBuilder<Customer>(metadataStore);
customerBuilder.DataProperty(t => t.id).IsPartOfKey().IsAutoIncrementing();
var transportReceiptBuilder = new EntityTypeBuilder<TransportReceipt>(metadataStore);
transportReceiptBuilder.DataProperty(t => t.id).IsPartOfKey().IsAutoIncrementing();
var transportReceiptAttachmentBuilder = new EntityTypeBuilder<TransportReceiptAttachment>(metadataStore);
transportReceiptAttachmentBuilder.DataProperty(t => t.id).IsPartOfKey().IsAutoIncrementing();
var uploadedFileBuilder = new EntityTypeBuilder<UploadedFile>(metadataStore);
uploadedFileBuilder.DataProperty(t => t.id).IsPartOfKey().IsAutoIncrementing();
ExtendMap.executed = true;
}
}
My base dataservice core code
public abstract class SimpleBaseDataService
{
public static string Metadata { get; protected set; }
public static MetadataStore MetadataStore { get; protected set; }
public string EntityName { get; protected set; }
public string EntityResourceName { get; protected set; }
public EntityManager EntityManager { get; set; }
public string DefaultTargetMethod { get; protected set; }
static SimpleBaseDataService()
{
try
{
var metadata = GetMetadata();
metadata.Wait();
Metadata = metadata.Result;
MetadataStore = BuildMetadataStore();
}
catch (Exception ex)
{
var b = 0;
}
}
public SimpleBaseDataService(Type entityType, string resourceName, string targetMethod = null)
{
var modelType = typeof(Customer);
Configuration.Instance.ProbeAssemblies(ConstantsFactory.BusinessAssembly);
try
{
this.EntityName = entityType.FullName;
this.EntityResourceName = resourceName;
this.DefaultTargetMethod = (string.IsNullOrWhiteSpace(targetMethod) ? "GetAllMobile" : targetMethod);
var dataService = new DataService($"{ConstantsFactory.Get.BreezeHostUrl}{this.EntityResourceName}", new CustomHttpClient());
dataService.HasServerMetadata = false;
this.EntityManager = new EntityManager(dataService, SimpleBaseDataService.MetadataStore);
this.EntityManager.MetadataStore.AllowedMetadataMismatchTypes = MetadataMismatchTypes.AllAllowable;
// Attach an anonymous handler to the MetadataMismatch event
this.EntityManager.MetadataStore.MetadataMismatch += (s, e) =>
{
// Log the mismatch
var message = string.Format("{0} : Type = {1}, Property = {2}, Allow = {3}",
e.MetadataMismatchType, e.StructuralTypeName, e.PropertyName, e.Allow);
// Disallow missing navigation properties on the TodoItem entity type
if (e.MetadataMismatchType == MetadataMismatchTypes.MissingCLRNavigationProperty &&
e.StructuralTypeName.StartsWith("TodoItem"))
{
e.Allow = false;
}
};
}
catch (Exception ex)
{
var b = 0;
}
}
}
This is who I'm trying to add the new entity
//DataService snippet
public void AttachEntity(T entity)
{
this.EntityManager.AttachEntity(entity, EntityState.Added);
}
//Business
this.TransportReceipt = new TransportReceipt { id = Guid.NewGuid(), date = DateTime.Now, customerId = Customer.id/*, customer = this.Customer*/ };
this.Attachments = new List<TransportReceiptAttachment>();
this.TransportReceipt.attachments = this.Attachments;
TransportReceiptDataService.AttachEntity(this.TransportReceipt);
When I try to add add the entity to the EntityManager, I can see the custom mapping for all my entity classes.
So my question is what I'm doing wrong.
Ok. That was weird.
I changed the mapping for a new fake int property and works. I'll test the entire save flow soon and I'll share the result here.
Update
I moved on and start removing Breezesharp. The Breezesharp project is no up-to-date and doesn't have good integration with Xamarin. I'll appreciate any comment with your experience.

Castle.Windsor - How to implement TypedFactoryFacility

Recently, I developed a component , using factory pattern. However, I did a research. on how to improve it using TypedFactoryFacility, since we are using Castle.WIndsor.
Can you please provide a simple complete example? I have read few of them but still can't really fully understand . SO far, my code looks like that :
public class DynamoStoreService : IDynamoStoreService
{
private IDynamoStoreFactory _dynamoStoreFactory;
public DynamoStoreService(IDynamoStoreFactory dynamoStoreFactory)
{
_dynamoStoreFactory=dynamoStoreFactory;
}
public IDynamoStore GetProductDataDynamoStore(string storageAccount)
{
return _dynamoStoreFactory.Create(storageAccount);
}
}
public class DynamoStoreFactory : IDynamoStoreFactory
{
private IStorageAccountSelector _storageAccountSelector;
public DynamoStoreFactory(IStorageAccountSelector storageAccountSelector)
{
_storageAccountSelector = storageAccountSelector;
}
public IDynamoStore Create(string storageAccount)
{
return new AzureKeyValueStore(_storageAccountSelector.GetCredentials(storageAccount).StorageAccount, "pointerfiles");
}
}
public class StorageAccountSelector : IStorageAccountSelector
{
private readonly IConfigurationSettings _settings;
public StorageAccountSelector(IConfigurationSettings settings)
{
_settings = settings;
}
BlobCredentials IStorageAccountSelector.GetCredentials(string storageAccount)
{
return new BlobCredentials()
{
Container = string.Empty,
StorageAccount = GetStorageAccount(storageAccount)
};
}
private string GetStorageAccount(string storageAccount)
{
switch (storageAccount)
{
case "CustomerPolarisingCategoryBlobStorageAccountKey":
return _settings.CustomerPolarisingCategoryBlobStorageAccount;
case "CustomerPolarisingSegmentBlobStorageAccountKey":
return _settings.CustomerPolarisingSegmentBlobStorageAccount;
case "P2ProductSimilarityBlobStorageAccountKey":
return _settings.P2ProductSimilarityBlobStorageAccount;
case "ProductPolarisingCategoryBlobStorageAccountKey":
return _settings.ProductPolarisingCategoryBlobStorageAccount;
case "ProductPolarisingSegmentBlobStorageAccountKey":
return _settings.ProductPolarisingSegmentBlobStorageAccount;
case "SignalBlobStorageAccountKey":
return _settings.SignalBlobStorageAccount;
}
return string.Empty;
}
}
}
So basically, the IDynamostore , whenvever called, we need to be able to pass a different connection string. I have figured out the above design.. could this be improved using TypedFactoryFacility?
Thanks
Maybe the code below can give you an idea about how to use the TypedFactoryFacility. If you have studied it and have questions about it, please let me know.
Kind regards,
Marwijn.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Castle.Facilities.TypedFactory;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
namespace ConsoleApplication3
{
public class TypedFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
private readonly StorageAccountSelector _storageAccountSelector;
public TypedFactoryComponentSelector(StorageAccountSelector storageAccountSelector)
{
_storageAccountSelector = storageAccountSelector;
}
protected override System.Collections.IDictionary GetArguments(MethodInfo method, object[] arguments)
{
var dictionary = new Dictionary<string, object>();
dictionary.Add("mappedStorageAccount", _storageAccountSelector.GetCredentials((string)arguments[0]).StorageAccount);
dictionary.Add("files", "pointerfiles");
return dictionary;
}
}
public interface IDynamoStore
{
}
public class AzureKeyValueStore : IDynamoStore
{
public AzureKeyValueStore(string mappedStorageAccount, string files)
{
Console.WriteLine(mappedStorageAccount);
Console.WriteLine(files);
}
}
public class BlobCredentials
{
public string Container { get; set; }
public string StorageAccount { get; set; }
}
public interface IDynamoStoreFactory
{
IDynamoStore Create(string storageAccount);
}
public class StorageAccountSelector
{
public BlobCredentials GetCredentials(string storageAccount)
{
return new BlobCredentials()
{
Container = string.Empty,
StorageAccount = GetStorageAccount(storageAccount)
};
}
public string GetStorageAccount(string storageAccount)
{
return storageAccount + "Mapped";
return string.Empty;
}
}
class Program
{
static void Main(string[] args)
{
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component.For<IDynamoStoreFactory>().AsFactory(new TypedFactoryComponentSelector(new StorageAccountSelector())),
Component.For<IDynamoStore>().ImplementedBy<AzureKeyValueStore>()
);
var factory = container.Resolve<IDynamoStoreFactory>();
factory.Create("storageAccount");
}
}
}

Reporting in MVC using an IReportRepository

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

Mocking a Customer SessionHandler Object in ASP.NET MVC for Unittests with Rhino Mocks

I currently use the following approach to create a strongly typed object representing session variables.
public abstract class SessionController : Controller
{
private const string SESSION_NAME = "UserSession";
public SessionData SessionData
{
get
{
SessionData sessionData = (SessionData)Session[SESSION_NAME];
if (sessionData != null)
{
return sessionData;
}
else
{
sessionData = new SessionData();
Session[SESSION_NAME] = sessionData;
return sessionData;
}
}
set
{
Session[SESSION_NAME] = value;
}
}
}
SessionData is a simple object like for example
[Serializable]
public class SessionData
{
public String SessionId { get; set; }
public Int64 UserId { get; set; }
public String NameOfUser { get; set; }
}
When creating a new Controller I derivate it from the SessionController so that I have strongley typed access to my SessionData. For example
public CityController : SessionController
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
ViewData.Model = _cityService.GetAll(SessionData.UserId);
return View("Index");
}
}
So, I am struggling at the moment to get this approached covered by a unittest. A shortened version of what I have tried is the following snippet
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
_cityService = MockRepository.GenerateStub<ICityService>();
_sesssionData = new SessionData { UserId = 1, SessionId = "1" };
// First Approach
controller = new CityController(_cityService);
controller.Expect(p => p.SessionData).Return(_sesssionData);
// Second Approach
cctx = MockRepository.GenerateStub<ControllerContext>();
cctx.Expect(p=>p.HttpContext.Session["UserSession"] as SessionData).Return(_sesssionData);
controller.ControllerContext = cctx;
}
Has anyone a tip on how to get this problem solved?
If you make your SessionData property virtual then your first approach could work:
// Arrange
var mocks = new MockRepository();
var cityService = MockRepository.GenerateStub<ICityService>();
var sesssionData = new SessionData { UserId = 1, SessionId = "1" };
var controller = mocks.PartialMock<CityController>(cityService);
controller.Expect(c => c.SessionData).Return(sessionData);
controller.Replay();
// Act
controller.Index();
//Assert
...
IMO this approach is not very good because the SUT (Subject Under Test => in this case CityController) is being mocked with PartialMock.

Resources