asp.net web api list of list pass into json ajax - asp.net

I have web api controller where I try to do something like this:
public IHttpActionResult Get()
{
var Rooms = db.Rooms.Select(r => new {
Id = r.Id,
Name = r.Name,
ChairNum = r.СhairNum,
Requests = r.Requests.ToList()
}).ToList();
return Ok(new { results = Rooms });
}
In Model I have this:
public partial class Room
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Room()
{
this.Requests = new HashSet<Request>();
}
public int Id { get; set; }
public string Name { get; set; }
public int СhairNum { get; set; }
public bool IsProjector { get; set; }
public bool IsBoard { get; set; }
public string Description { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Request> Requests { get; set; }
}
I tried to pass this data in service. But I have serialization error, when I call http://localhost:99999/api/Rest.
<ExceptionMessage>
The "ObjectContent`1" type could not serialize the response text for the content type "application / json; charset = utf-8".
</ ExceptionMessage>
What is the best way to pass the data like in model into json?

It helped to put in WebApiConfig:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);

Related

How to send a collection of files + JSON data to the client side

In my ASP.NET Core Web API I have an entity Idea:
public class Idea
{
public int Id { get; set; }
public string Name { get; set; }
public string OwnerId { get; set; }
public User Owner { get; set; }
public string Description { get; set; }
public int? MainPhotoId { get; set; }
public Photo MainPhoto { get; set; }
public int? MainVideoId { get; set; }
public Video MainVideo { get; set; }
public ICollection<Photo> Photos { get; set; }
public ICollection<Video> Videos { get; set; }
public ICollection<Audio> Audios { get; set; }
public ICollection<Document> DocumentsAboutTheIdea { get; set; }
public DateTime DateOfPublishing { get; set; }
}
public class Photo
{
public int Id { get; set; }
public string Url { get; set; }
}
(the different media-type classes are equivalent)
When the client makes a Post request for creating the Idea he sends the information about it along with all the media-files (I am using IFormFile and IFormFileCollection) and while saving them on the server I set the Url property to match their location. But in the Get request I want to send the files (not Urls).
Here is the Get action which now returns only one file (mainPhoto) without any JSON and without any other media-files:
[HttpGet("{id}", Name = "Get")]
public async Task<IActionResult> Get(int id)
{
var query = await unitOfWork.IdeaRepository.GetByIdAsync(id, includeProperties: "Owner,MainPhoto,MainVideo,Photos,Videos,Audios,DocumentsAboutTheIdea");
if (query != null)
{
string webRootPath = hostingEnvironment.WebRootPath;
var path = string.Concat(webRootPath, query.MainPhoto.Url);
var memory = new MemoryStream();
using (var stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, GetContentType(path), Path.GetFileName(path));
}
return NotFound();
}
So in a Get request I want to send to the client side (an Angular app) some of the information about the Idea in a JSON-format ALONG WITH different (NOT ONE) media-files associated with it. My goal is to make the client side get it all so that it can then show them on the page of the info about the Idea. But I cannot figure out the approach for this. Is there a way to achieve this? Or there is another (better) way of interacting with the client side to transfer all the required data? I tried to find information on this topic but couldn't find any.

Entity Framework Core Query Specific Model both directions

Let me preface this question with, I am VERY new to ASP.NET Core/EF Core.
My model look like this:
namespace MyProject.Models
{
public class DeviceContext : DbContext
{
public DeviceContext(DbContextOptions<DeviceContext> options) : base(options) { }
public DbSet<Device> Devices { get; set; }
public DbSet<DeviceLocation> DeviceLocations { get; set; }
}
public class Device
{
public int Id { get; set; }
public string DeviceName { get; set; }
public string ServerName { get; set; }
public string MacAddress { get; set; }
public string LastUpdate { get; set; }
public string WiredIPAddress { get; set; }
public string WirelessIPAddress { get; set; }
public DeviceLocation DeviceLocation { get; set; }
}
public class DeviceLocation
{
public int Id { get; set; }
public string Location { get; set; }
public virtual ICollection<Device> Devices { get; set; }
}
}
I would like to be able to fetch a specific Device based on DeviceName, but I would also like to fetch ALL the devices in a particular Location.
I think the following would work for the first question:
var _Devices = DeviceContext.Devices.FirstOrDefault(d => d.DeviceName == "BLA");
I am just having a hard time getting the second query to run. Ideally, the output would be rendered to JSON to be consumed by an API. I would like the output to look something like this:
{
"Locations": {
"NYC": ["ABC", "123"],
"Boston": ["DEF", "456"],
"Chicago": ["GHI", "789"]
}
}
UPDATE
If I use the following code, it give me the following error:
Code:
// Grouping by ProfileName
var devices = DeviceContext.DeviceLocations.Include(n => n.Device).ToList();
var result = new { success = true, message = "Successfully fetched Devices", data = devices };
return JsonConvert.SerializeObject(result);
Error:
Additional information: Self referencing loop detected for property 'DeviceLocation' with type 'Project.Models.DeviceLocation'. Path 'data[0].Device[0]'.
You can try as shown below.
Note : Use Eager Loading with Include.
using System.Data.Entity;
var devicesList = DeviceContext.DeviceLocations.Where(d=>d.Location = "Your-Location-Name")
.Include(p => p.Devices)
.ToList();
Update :
var devicesList = DeviceContext.DeviceLocations
.Include(p => p.Devices)
.ToList();

using DataContractJsonSerializer when Json object propety is named with "#" symbol

When using DataContractJsonSerializer to parse a json response, I've come across a json object with a property name of #id and I can't seem to populate this field. All of the other fields are populated without a problem. I can even get this field populated using Fiddler, but not using DataContractJsonSerializer. My code is as follows...
public IEnumerable<Vehicle> vehicles { get; set; }
public async Task GetVehicleList(string access_token)
{
vehicles = await GetVehicleListInfo(access_token);
}
private async Task<IEnumerable<Vehicle>> GetVehicleListInfo(string access_token)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://www.example.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.example.api-v1+json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer" + access_token);
HttpResponseMessage response = await client.GetAsync("vehicles");
//IEnumerable<Vehicle> vehicles = new IEnumerable<Vehicle>();
Vehicle vehicle = new Vehicle();
PagedVehicleResult pvr = new PagedVehicleResult();
Stream dataStream = null;
if (response.IsSuccessStatusCode)
{
dataStream = await response.Content.ReadAsStreamAsync();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(pvr.GetType());
pvr = (PagedVehicleResult)serializer.ReadObject(dataStream);
}
vehicles = pvr.vehicle;
return vehicles;
}
}
}
/// <summary>
/// parent object returned by json. contains list of vehicle objects
/// </summary>
public class PagedVehicleResult
{
public int count { get; set; }
public int index { get; set; }
public int limit { get; set; }
public int total { get; set; }
public string type { get; set; }
public IEnumerable<Vehicle> vehicle { get; set; }
}
public class Vehicle
{
public int id { get; set; } //This is the property in question.
//When it comes back in fiddler it looks like "#id", and it does get populated.
//when it comes back in my console application it is 0 if int and null if string.
public string vin { get; set; }
public string label { get; set; }
public string color { get; set; }
public string make { get; set; }
public string model { get; set; }
public string deviceSerialNumber { get; set; }
public int year { get; set; } //consider making date? unknown repercussions
public CreateTimestamp createdTimestamp { get; set; }
public ModifiedTimestamp modifiedTimestamp { get; set; }
}
public class CreateTimestamp
{
public string readOnly { get; set; }
public string value { get; set; } //had to cast as string.. can convert to datetime before database insert
}
public class ModifiedTimestamp
{
public string readOnly { get; set; }
public string value { get; set; }
}
How can I map that json #id field to my Vehicle class id property?
Okay, I figured this out on my own from reading various different post on the subject.
All I had to do was parse it into a json string first, then call a .replace() on the string to change the id. Then I used the JavaScriptSerializer on that and it worked.
if (response.IsSuccessStatusCode)
{
dataStream = await response.Content.ReadAsStreamAsync();
string content;
using (StreamReader reader = new StreamReader(dataStream, true))
{
content = reader.ReadToEnd();
content = content.Replace("#id", "id");
JavaScriptSerializer js = new JavaScriptSerializer();
pvr = js.Deserialize<PagedVehicleResult>(content);
}
}
vehicles = pvr.vehicle;
return vehicles;
Now, when i try, my object id is correctly inserted.

DocumentDB LAMBDA SqlMethod.Like and .Contains NotImplemented Exception

Im trying to do a simple "LIKE" query using a LAMBDA expression using CreateDocumentQuery; however after trying .Contains and SqlMethod.Like and both times receiving the response NotImplementedException I don't know what to try next!
Update: As of 5/6/15, DocumentDB added a set of String functions including STARTSWITH, ENDSWITH, and CONTAINS. Please note that most of these functions do not run on the index and will force a scan.
LIKE and CONTAINS are not yet supported in DocumentDB.
You'll want to check out the DocumentDB feedback page and vote on features (e.g. LIKE and CONTAINS) to get your voice heard!
Because I only needed to search against a discreet subset of properties of the larger object I implemented a .Contains search function as below. It works as expected though I have no idea regarding performance or scalability.
Domain Models
public interface ITaxonomySearchable
{
string Name { get; set; }
string Description { get; set; }
}
public class TaxonomySearchInfo : ITaxonomySearchable {
[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
}
public class TaxonomyContainer : ITaxonomySearchable
{
[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }
[JsonProperty(PropertyName = "userId")]
public string UserId { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
[JsonProperty(PropertyName = "tags")]
public string[] Tags { get; set; }
[JsonProperty(PropertyName = "taxonomy")]
public Node[] Taxonomy { get; set; }
}
Search method
public async static Task<List<TaxonomySearchInfo>> Search(string searchTerm)
{
var db = GetJsonDocumentDb.GetDb();
using (var client = GetJsonDocumentDb.GetClient())
{
var documentCollection = await GetJsonDocumentDb.GetDocumentCollection(db, client, "TaxonomyContainerCollection");
return client.CreateDocumentQuery<TaxonomySearchInfo>(documentCollection.DocumentsLink)
.AsEnumerable()
.Where(r => r.Name.Contains(searchTerm) || r.Description.Contains(searchTerm))
.ToList();
}
}

ASP.NET web API casting http response to json array

My Code works fine when calling REST URL:
http://api.feedzilla.com/v1/categories.json
but when I call following URL I get error:
http://api.feedzilla.com/v1/categories/15/articles.json?count=36&since=2012-11-15&client_source=&order=relevance&title_only=0&
Error:
{"Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.IEnumerable`1[Nitin.News.DAL.Resources.Article]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'articles', line 1, position 12."}
My Code is as follows:
public class Article
{
public string publish_date { get; set; }
public string source { get; set; }
public string source_url { get; set; }
public string summary { get; set; }
public string title { get; set; }
public string url { get; set; }
}
public IEnumerable<Article> Get()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://api.feedzilla.com/v1/");
//Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// call the REST method
HttpResponseMessage response = client.GetAsync("http://api.feedzilla.com/v1/categories/2/articles.json??count=36&since=2012-11-15&client_source=&order=relevance&title_only=0&").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
return response.Content.ReadAsAsync<IEnumerable<Article>>().Result;
//wont work
//string JSON =response.Content.ReadAsStringAsync().Result;
//return JsonConvert.DeserializeObject<IEnumerable<T>>(JSON);
}
else
{
throw new Exception(string.Format("Data access faild,{0} ({1}) method:{2}", (int)response.StatusCode, response.ReasonPhrase, MethodURL));
}
}
You need another level in your object hierachy... i.e. a root to contain the IEnumerable.
Runing the JSON into the tool at http://json2csharp.com/ generates the following proxy classes:
public class Enclosure
{
public int length { get; set; }
public string media_type { get; set; }
public string uri { get; set; }
}
public class Article
{
public string author { get; set; }
public string publish_date { get; set; }
public string source { get; set; }
public string source_url { get; set; }
public string summary { get; set; }
public string title { get; set; }
public string url { get; set; }
public List<Enclosure> enclosures { get; set; }
}
public class RootObject
{
public List<Article> articles { get; set; }
public string description { get; set; }
public string syndication_url { get; set; }
public string title { get; set; }
}
You just need to change your code to this then:
// Parse the response body. Blocking!
return response.Content.ReadAsAsync<RootObject>().Result.articles;
Obviously strip out any properties you dont need. Just thought I would show them all out of interest.

Resources