Testing JSON Results from MVC 4 C# Controller - c#

So I have a controller that returns json to my views that I need to test. I have tried using reflection with a dynamic data type to access a sub property of a list but I keep getting something similar to 'unable to cast' errors. Basically I have a list within a list that I want to access and verify things about but I can't access it. Has anyone tested json returned from their controller before in MVC4 and have advice?
Code:
// arrange
var repositoryMock = new Mock<IEdwConsoleRepository>();
var date = -1;
var fromDate = DateTime.Today.AddDays(date);
EtlTableHistory[] tableHistories =
{
new EtlTableHistory
{
Table = new Table(),
RelatedStatus = new BatchStatus(),
BatchId = 1
}
};
EtlBatchHistory[] batchHistories = { new EtlBatchHistory
{
Status = new BatchStatus(),
TableHistories = tableHistories
} };
repositoryMock.Setup(repository => repository.GetBatchHistories(fromDate)).Returns((IEnumerable<EtlBatchHistory>)batchHistories);
var controller = new EdwController(new Mock<IEdwSecurityService>().Object, repositoryMock.Object);
// act
ActionResult result = controller.BatchHistories(1);
// assert
Assert.IsInstanceOfType(result, typeof(JsonResult), "Result type was incorrect");
var jsonResult = (JsonResult)result;
var resultData = (dynamic)jsonResult.Data;
var returnedHistories = resultData.GetType().GetProperty("batchHistories").GetValue(resultData, null);
var returnedTableHistoriesType = returnedHistories.GetType();
Assert.AreEqual(1, returnedTableHistoriesType.GetProperty("Count").GetValue(returnedHistories, null), "Wrong number of logs in result data");

Here's an example:
Controller:
[HttpPost]
public JsonResult AddNewImage(string buildingID, string desc)
{
ReturnArgs r = new ReturnArgs();
if (Request.Files.Count == 0)
{
r.Status = 102;
r.Message = "Oops! That image did not seem to make it!";
return Json(r);
}
if (!repo.BuildingExists(buildingID))
{
r.Status = 101;
r.Message = "Oops! That building can't be found!";
return Json(r);
}
SaveImages(buildingID, desc);
r.Status = 200;
r.Message = repo.GetBuildingByID(buildingID).images.Last().ImageID;
return Json(r);
}
public class ReturnArgs
{
public int Status { get; set; }
public string Message { get; set; }
}
Test:
[TestMethod]
public void AddNewImage_Returns_Error_On_No_File()
{
// Arrange
ExtendedBuilding bld = repo.GetBuildings()[0];
string ID = bld.Id;
var fakeContext = new Mock<HttpContextBase>();
var fakeRequest = new Mock<HttpRequestBase>();
fakeContext.Setup(cont => cont.Request).Returns(fakeRequest.Object);
fakeRequest.Setup(req => req.Files.Count).Returns(0);
BuildingController noFileController = new BuildingController(repo);
noFileController.ControllerContext = new ControllerContext(fakeContext.Object, new System.Web.Routing.RouteData(), noFileController);
// Act
var result = noFileController.AddNewImage(ID, "empty");
ReturnArgs data = (ReturnArgs)(result as JsonResult).Data;
// Assert
Assert.IsTrue(data.Status == 102);
}
In your example it looks to me that the problem is here:
var resultData = (dynamic)jsonResult.Data;
var returnedHistories = resultData.GetType().GetProperty("batchHistories").GetValue(resultData, null);
The resultData object would be the exact type of the object that you returned in your action. So if you did something like:
List<String> list = repo.GetList();
return Json(list);
Then your resultData would be of type:
List<String>
Try making sure that you are returning your Object using the Json(obj) function.

You can deserialize your Json into a dynamic object and then ask for the property you want
Example:
dynamic obj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
var propertyValue = obj.MyProperty; //Ask for the right property
You can add Json serializer from Nuget Json.Net package.

Related

how can I reduce 2 select to 1 select linq to gain performance?

my question is simple but I got stuck with something. Can you tell me how can I reduce 2 select into 1 select LINQ in c#? I am using CloudNative.CloudEvents NuGet package for cloud-native events.
var orderEvents = input
.Select(_ => new OrderDocument(_.Id, _.ToString()).ToOrderEvent())
.Select(_ =>
new CloudEvent()
{
Type = _.EventType,
Subject = _.Subject,
Source = _.Source,
Data = _
});
input is a parameter from cosmosDbTrigger it`s type : IReadOnlyList
OrderDocument.cs
public class OrderDocument
{
public string Id { get; private set; }
public string Json { get; private set; }
public OrderDocument(string id, string json)
{
Id = id;
Json = json;
}
public OrderEvent ToOrderEvent() => OrderEventHelper.ToOrderEvent(Json);
}
OrderEventHelper.cs
public static OrderEvent ToOrderEvent(string json)
{
ArgumentHelper.ThrowIfNullOrEmpty(json);
var orderEvent = JsonConvert.DeserializeObject<OrderEvent>(json);
var eventDefinition = OrderEvents.EventDefinitions.SingleOrDefault(_ => _.EventType == orderEvent.EventType);
return eventDefinition == null
? orderEvent
: new OrderEvent(
orderEvent.Id,
orderEvent.Source,
orderEvent.EventType,
orderEvent.Subject,
orderEvent.DataContentType,
orderEvent.DataSchema,
orderEvent.Timestamp,
JsonConvert.DeserializeObject(orderEvent.Payload.ToString(), eventDefinition.PayloadType),
orderEvent.TraceId);
}
linq extensions are basically for loops in the background. If you want to perform multiple actions against a list, perhaps making your own simple for loop where you can manage that yourself would work.
Your code:
var orderEvents = input
.Select(_ => new OrderDocument(_.Id, _.ToString()).ToOrderEvent())
.Select(_ =>
new CloudEvent()
{
Type = _.EventType,
Subject = _.Subject,
Source = _.Source,
Data = _
});
could be changed to:
// our result set, rather than the one returned from linq Select
var results = new List<CloudEvent>();
foreach(var x in input){
// create the order event here
var temporaryOrderEvent = new OrderDocument(x.Id, x.ToString()).ToOrderEvent();
// add the Cloud event to our result set
results.Add(new CloudEvent()
{
Type = temporaryOrderEvent .EventType,
Subject = temporaryOrderEvent .Subject,
Source = temporaryOrderEvent .Source,
Data = temporaryOrderEvent
});
}
where you then have a result list to work with.
If you wanted to keep it all in linq, you could instead perform all of your logic in the first Select, and ensure that it returns a CloudEvent. Notice here that you can employ the use of curly brackets in the linq statement to evaluate a function rather than a single variable value:
var orderEvents = input
.Select(x =>
{
// create the order event here
var temporaryOrderEvent = new OrderDocument(x.Id, x.ToString()).ToOrderEvent();
// return the Cloud event here
return new CloudEvent()
{
Type = temporaryOrderEvent .EventType,
Subject = temporaryOrderEvent .Subject,
Source = temporaryOrderEvent .Source,
Data = temporaryOrderEvent
};
});
How about putting conversion to OrderEvent and using ToCloudEvent in the same Select?
var orderEvents = input
.Select(_ => new OrderDocument(_.Id, _.ToString()).ToOrderEvent().ToCloudEvent())
public class OrderEvent
{
public CloudEvent ToCloudEvent()
{
new CloudEvent()
{
Type = this.EventType,
Subject = this.Subject,
Source = this.Source,
Data = this
};
}
}

C# Solr query - how to only get future dates?

I have a search method that queries Solr for event items. I need to modify it to only get events where the date has not already passed (i.e. Where(x => x.EventDate.Date >= DateTime.Now.Date), but I'm not sure how to add this because I'm not very familiar with Solr. Here's my search function:
public SearchQueryResults Search(string keywords, int page,int perPage, List<Guid> contentTypeFilters, List<Guid> otherFilters, ISortBuilder<SearchResultItem> sortBuilder)
{
var searchFilters = new List<IPredicateBuilder<SearchResultItem>>()
{
new IsSearchablePredicateBuilder()
};
if (contentTypeFilters.Any())
{
var contentTypePredicateBuilder = new ContentTypePredicateBuilder();
contentTypePredicateBuilder.ContentTypes = contentTypeFilters;
searchFilters.Add(contentTypePredicateBuilder);
}
if (otherFilters.Any())
{
var tagFilterBuilder = new TagsAndPredicateBuilder(otherFilters,_sitecoreContext);
searchFilters.Add(tagFilterBuilder);
}
if (string.IsNullOrWhiteSpace(keywords))
{
keywords = "";
}
SearchRequest searchRequest = new SearchRequest();
var queryParams = new Dictionary<string, string>() { };
queryParams.Add("q", keywords);
searchRequest.QueryParameters = queryParams;
searchRequest.SortBy = "";
searchRequest.SortOrder = "";
SearchQuery<SearchResultItem> queryArguments = new SearchQuery<SearchResultItem>();
queryArguments.FilterBuilders = searchFilters;
queryArguments.Page = page;
queryArguments.PerPage = perPage;
queryArguments.FacetsBuilder = new SearchFacetBuilder<SearchResultItem>();
queryArguments.SearchRequest = searchRequest;
queryArguments.IndexName = _indexName;
if (string.IsNullOrWhiteSpace(keywords))
{
queryArguments.QueryBuilders =new List<IPredicateBuilder<SearchResultItem>>();
}
else
{
queryArguments.QueryBuilders = new[] { new KeywordPredicateBuilder<SearchResultItem>(new[] { keywords }) };
}
queryArguments.SortBuilder = sortBuilder;
try
{
var results = _searchManager.GetResults<SearchResultItem>(queryArguments);
SearchQueryResults queryResults = new SearchQueryResults();
queryResults.ResultItems = results.Results;
queryResults.CurrentPage = page;
queryResults.TotalResults = Int32.Parse(results.TotalResults.ToString());
queryResults.TotalPages = (queryResults.TotalResults + perPage - 1) / perPage; ;
return queryResults;
}
catch (Exception exc)
{
Sitecore.Diagnostics.Log.Error("Error with FilteredSearch, could be a loss of connection to the SOLR server: " + exc.Message, this);
return null;
}
}
and here is how it's being called:
Results = _searchService.Search(searchTerm, CurrentPage - 1, 10, contentTypes, searchFilters,
new GenericSortBuilder<SearchResultItem>(q => q.OrderByDescending(r => r.SearchDate)));
How do I add in date filtering so that it only returns items where the date is in the future?
I would add filter query to the list of existing ones filtering the date field. On the documentation page, I was able to find information about fluent API, which could help here
Query.Field("date").From(DateTime.Now)
I'm not C# developer, that this code could have some mistakes, but I think the main idea is clear what needs to be done.

Adding attributes for faceting with Algolia C#

I have an attribute that I'd like to add for faceting: ApprovalFL. When I add the facet in the Algolia dashboard, it works fine until I have to reindex which ends up deleting my facets in the dashboard for some reason. I was thinking, maybe adding the attribute in my code would resolve that. I found the documentation about this step here: https://www.algolia.com/doc/api-reference/api-parameters/attributesForFaceting/
Here's the C# example they provide:
index.SetSettings(
JObject.Parse(#"{""attributesForFaceting"":[""author"",""filterOnly(category)"",""searchable(publisher)""]}")
);
If I understand correctly, this code needs to go in my backend reindexing code (found in my AdminController.cs):
public async Task<ActionResult> ReIndexData()
{
var algoliaArtistModels = Tools.BuildAlgoliaArtistModels(EntityDataAccess.GetAllAccountInfoes());
var algoliaUnaffiliatedArtistModels = Tools.BuildAlgoliaArtistModels(EntityDataAccess.GetAllUnaffiliatedAccountInfo());
var algoliaSongModels = Tools.BuildAlgoliaSongModels(EntityDataAccess.GetAllAcceptedSongs());
var artistIndexHelper = HttpContext.Application.Get("ArtistIndexHelper") as IndexHelper<ArtistAlgoliaModel>;
var unaffiliatedArtistIndexHelper = HttpContext.Application.Get("UnaffiliatedArtist") as IndexHelper<ArtistAlgoliaModel>;
var songIndexHelper = HttpContext.Application.Get("SongIndexHelper") as IndexHelper<SongAlgoliaModel>;
await artistIndexHelper.OverwriteIndexAsync(algoliaArtistModels);
await unaffiliatedArtistIndexHelper.OverwriteIndexAsync(algoliaUnaffiliatedArtistModels);
await songIndexHelper.OverwriteIndexAsync(algoliaSongModels);
return View("AlgoliaReIndexData");
}
However, I don't think I put index.setSettings in this block of code, what is the best way to set this attribute for faceting in my backend code? The ApprovalFL attribute is stored in my Song indices.
Possibly it should go somewhere here in my Tools.cs?
public static SongAlgoliaModel BuildAlgoliaSongModel(Song song)
{
var model = new SongAlgoliaModel();
var album = EntityDataAccess.GetAlbumByID(song.AlbumID);
model.AccountImageURL = album.AccountInfo.ImageURL;
model.AccountInfoID = album.AccountInfoID;
model.AccountType = album.AccountInfo.CreatorFL == true ? "Creator" : "Artist";
model.AlbumID = song.AlbumID;
model.AlbumName = album.AlbumName;
model.ApprovalFL = song.ApprovalFL;
model.Artist = album.AccountInfo.DisplayName;
model.BPM = song.BPM;
model.Duration = song.Duration;
model.FeaturedArtist = song.Artist;
model.FreeFL = album.FreeFL;
model.ImageURL = album.ImageURL;
model.iTunesURL = album.iTunesURL;
model.LabelName = album.LabelName;
model.LicenseFL = album.LicenseFL;
model.SongID = song.SongID;
model.Title = song.Title;
model.UploadDate = song.UploadDate;
model.URL = song.URL;
model.UserID = album.AccountInfo.UserID;
return model;
}
public static List<SongAlgoliaModel> BuildAlgoliaSongModels(AccountInfo accountInfo)
{
var list = new List<SongAlgoliaModel>();
var songs = EntityDataAccess.GetSongsByUserID(accountInfo.UserID).Where(x => x.ApprovalFL == true).ToList();
foreach(var item in songs)
{
var model = new SongAlgoliaModel();
model.AccountImageURL = item.Album.AccountInfo.ImageURL;
model.AccountInfoID = item.Album.AccountInfoID;
model.AccountType = item.Album.AccountInfo.CreatorFL == true ? "Creator" : "Artist";
model.AlbumID = item.AlbumID;
model.AlbumName = item.Album.AlbumName;
model.ApprovalFL = item.ApprovalFL;
model.Artist = item.Album.AccountInfo.DisplayName;
model.BPM = item.BPM;
model.Duration = item.Duration;
model.FeaturedArtist = item.Artist;
model.FreeFL = item.Album.FreeFL;
model.ImageURL = item.Album.ImageURL;
model.iTunesURL = item.Album.iTunesURL;
model.LabelName = item.Album.LabelName;
model.LicenseFL = item.Album.LicenseFL;
model.SongID = item.SongID;
model.Title = item.Title;
model.UploadDate = item.UploadDate;
model.URL = item.URL;
model.UserID = item.Album.AccountInfo.UserID;
list.Add(model);
}
return list;
}
OK for those using C# .NET it's as simple as adding it to your Global.asax.cs for example for me:
var songIndexHelper = new IndexHelper<SongAlgoliaModel>(algoliaClient, "Song", "SongID");
songIndexHelper.SetSettings(JObject.Parse(#"{""attributesForFaceting"":[""ApprovalFL""]}"));
Why do you have to re-index by the way? Best practice is to set up your index only initially.

Call method from repository in controller (ASP.NET MVC)

I have a repository, where I have this method for getting some data. Here is the code:
public List<HeatmapViewModel> GetStops()
{
using (var ctx = new GoogleMapTutorialEntities())
{
List<HeatmapViewModel> items = new List<HeatmapViewModel>();
#region firstitem_calculation
var firstitem = ctx.Loggings.Where(x => x.Datatype == 2).AsEnumerable().Select(
x => new Logging
{
Longitude2 = x.Longitude2,
Latitude2 = x.Latitude2,
CurDateTime = x.CurDateTime
}).FirstOrDefault();
var midnight = new DateTime(firstitem.CurDateTime.Year, firstitem.CurDateTime.Month, firstitem.CurDateTime.Day, 00, 00, 00);
TimeSpan difference = (firstitem.CurDateTime - midnight);
var difference_after_midnight = (int) difference.TotalMinutes;
items.Add( new HeatmapViewModel
{
FirstStartDifference = difference_after_midnight
});
#endregion
return items;
}
}
I need to call this method in controller, in this method:
public JsonResult GetStops()
{
}
How I can do this?
YourRepositoryName repo = new YourRepositoryName();
var _data = repo.GetStops();
public JsonResult GetStops()
{
var repo = new TheRepository();
var listOfHeatMapVm = repo.GetStops();
//Convert the list of HeatMapVm to Json result here.
return Json(listOfHeatMapVm);
}

Linq to Json MVC 5 not formatting as expected

might seem really simple however I can't get my json to be correctly formatted from my MVC 5 controller.
My output is showing as;
[
{
"result":{
"account_color":"B43104",
"account_desc":"Welcome to XYZ",
"account_name":"XYZ",
"account_zone":1
}
},
{
"result":{
"account_color":"FF0000",
"account_desc":"Test Company",
"account_name":"Test",
"account_zone":2
}
}
]
So as you can see the level above result does not show any text so it seems like the 'result' section is getting added to a blank node
My controller is;
public IEnumerable<dynamic> Get()
{
return db.tblAccounts.Select(o => new
{
result = new
{
account_name = o.account_name,
account_desc = o.account_desc,
// account_image = Url.Content("~/images/") + String.Format("account_{0}.png", o.id),
account_color = o.account_color,
}
});
}
My json formatter is;
config.Formatters.Clear();
//config.Formatters.Add(new XmlMediaTypeFormatter());
var json = new JsonMediaTypeFormatter();
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;
config.Formatters.Add(json);
so it just appears that the 'result' is at a sub level it needs to be a level higher, any help would be great.
Try changing your code to look like this:
public IEnumerable<dynamic> Get()
{
return db.tblAccounts.Select(o =>
new
{
account_name = o.account_name,
account_desc = o.account_desc,
// account_image = Url.Content("~/images/") + String.Format("account_{0}.png", o.id),
account_color = o.account_color,
}
);
}
This will produce an array of results instead of the nested JSON that you provided. you don't actually need the result node label do you?
EDIT:
based on your response is this what you are looking for?
public dynamic Get()
{
var accountsNode = new {
accounts = db.tblAccounts.Select(o =>
new
{
account_name = o.account_name,
account_desc = o.account_desc,
// account_image = Url.Content("~/images/") + String.Format("account_{0}.png", o.id),
account_color = o.account_color,
}
).ToList()
};
return accountsNode;
}

Categories

Resources