2

I am trying to show data from Azure Cosmos DB (JSON data) using C# MVC web application. Json data is nested as below. I want to display information in Entries array as a table using c# MVC. I am not able to display data.

I am trying to show data from Azure Cosmos DB (JSON data). I checked few post on line which are doing it but they are using Json data strightway not Azure Jason. Json data is nested as below. I want to display information in Entries array ("dateTime": "2019-07-04T00:00:00", "precisCode": "showers-rain", "min": 10, "max": 19)as a table using c# MVC. I am new to both azure and c# mvcI am not able to display data. Please check code below and let me know whats wrong and what can be done.

Json data:

"id": "302620190704",
    "LocationId": "3026",
    "CreatedAt": "2019-07-04T21:42:53",
    "DateTime": "2019-07-04T00:00:00",
    "Entries": {
        "0": {
            "dateTime": "2019-07-04T00:00:00",
            "precisCode": "showers-rain",
            "min": 10,
            "max": 19
        }
    },
    "_rid": "sklTAKvKt1MBAAAAAAAAAA==",
    "_self": "dbs/sklTAA==/colls/sklTAKvKt1M=/docs/sklTAKvKt1MBAAAAAAAAAA==/",
    "_etag": "\"020002c6-0000-1a00-0000-5d1e906d0000\"",
    "_attachments": "attachments/",
    "_ts": 1562284141
}

Model:

namespace PelicanboatRamp.Models
{
    public class Weather
    {

        [JsonProperty(PropertyName = "id")]
        public string Id { get; set; }
        [JsonProperty(PropertyName = "DateTime")]
        public DateTime Datetime { get; set; }

        public static DateTime Today { get; }

        [JsonProperty(PropertyName = "LocationId")]
        public string LocationId { get; set; }

        [JsonProperty(PropertyName = "Entries")]
        public Entries entries { get; set; }
    }

    public class Entries
    {
        [JsonProperty(PropertyName = "precisCode")]
        public string Forecast { get; set; }

        [JsonProperty(PropertyName = "min")]
        public int MinTemp { get; set; }

        [JsonProperty(PropertyName = "max")]
        public int MaxTemp { get; set; }
    }
}

Controller:

 public class WeatherController : Controller
    {
        // GET: Weather
        [ActionName("Index")]
        public async Task<ActionResult> IndexAsync()

        {
            DateTime thisDay = DateTime.Today;
            var items = await DocumentDBRepository<Weather>.GetWeatherAsync(d => d.Id != null);
            items.Select(t => new Entries
            { Datetime = t.Datetime,
            Forecast = t.entries.Forecast,
            MinTemp = t.entries.MinTemp,
            MaxTemp = t.entries.MaxTemp
            }).ToList();
            return View(items);
        }
    }

DocumentDBRepository:

 public static async Task<IEnumerable<T>> GetWeatherAsync(Expression<Func<T, bool>> predicate)
        {
            IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
                UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
                new FeedOptions { MaxItemCount = 10, EnableCrossPartitionQuery = true })
                .Where(predicate)
                .AsDocumentQuery();

            List<T> results = new List<T>();
            while (query.HasMoreResults)
            {
                results.AddRange(await query.ExecuteNextAsync<T>());

            }

            return results;
        }
        public static async Task<T> GetWeatherAsync(string LocationId, string id)
        {
            try
            {
                //RequestOptions feedOptions = new RequestOptions { PartitionKey = new PartitionKey(Year) };
                Document document = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), new RequestOptions { PartitionKey = new PartitionKey(LocationId) });

                return (T)(dynamic)document;
            }
            catch (DocumentClientException e)
            {
                if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    return null;
                }
                else
                {
                    throw;
                }
            }
        }


    }

Index:

@model IEnumerable<PelicanboatRamp.Models.Weather>

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Todays Weather</h2>

 @foreach (var item in Model)
    {
        DateTime mydatetime = DateTime.Now;
       <h3>
            DateTime: @mydatetime
        </h3>
        <h3>
            Forecast:@Html.DisplayFor(modelItem => item.entries.Forecast)
        </h3>
        <h3>
            MinTemp:  @Html.DisplayFor(modelItem => item.entries.MinTemp)
        </h3>
        <h3>
            MaxTemp  @Html.DisplayFor(modelItem => item.entries.MaxTemp)
        </h3>

    }

I would like to display: (Current date and Time Forecaste: "showers-rain", minTemp: 10, maxTemp: 19

It's not displaying Forecast, Min Temp and Max Temp

1 Answer 1

2

First problem

Model classes incorrectly reflect the JSON structure. Here:

"Entries": {
    "0": {
        "dateTime": "2019-07-04T00:00:00",
        "precisCode": "showers-rain",
        "min": 10,
         "max": 19
    }
}

Entries is an object that contains numeric key 0, and I guess might contain 1, 2, etc. Under every numeric key there is a single entry object.

To represent such a structure in C# we should use a dictionary, e.g., Dictionary<string, Entry> (let's rename Entries to Entry, as it should represent a single entry object). Below is the correct model:

public class Weather
{
    // ... other properties

    // this property was declared incorrectly
    [JsonProperty(PropertyName = "Entries")]
    public Dictionary<string, Entry> Entries { get; set; }
}

// renamed from Entries
public class Entry 
{
    // ... properties
}        

Second problem

In the view, the model is declared as IEnumerable<...> while you seem to want to access a single instance of Weather. To list all entries from all Weather objects returned by the query, you should loop over the Model like this:

@{
    DateTime mydatetime = DateTime.Now;
    var allEntries = Model.SelectMany(m => m.Entries.Values);
}
@foreach (var entry in allEntries)
{
    <h3>
        DateTime: @mydatetime
    </h3>
    <h3>
        Forecast:@Html.DisplayFor(modelItem => entry.Forecast)
    </h3>
    <h3>
        MinTemp:  @Html.DisplayFor(modelItem => entry.MinTemp)
    </h3>
    <h3>
        MaxTemp  @Html.DisplayFor(modelItem => entry.MaxTemp)
    </h3>
}

BTW, this code in your controller:

items.Select(t => new Entries { 
    Datetime = t.Datetime,
    Forecast = t.entries.Forecast,
    MinTemp = t.entries.MinTemp,
    MaxTemp = t.entries.MaxTemp
}).ToList();

is completely redundant and has no effect.

If you want to display only a single Weather object

Then in the view, change the model to be of type Weather, and change the loop accordingly, as shown below:

@model PelicanboatRamp.Models.Weather
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@{
    DateTime mydatetime = DateTime.Now;
}
@foreach (var entry in Model.Entries.Values)
{
    <h3>
        DateTime: @mydatetime
    </h3>
    <h3>
        Forecast:@Html.DisplayFor(modelItem => entry.Forecast)
    </h3>
    <h3>
        MinTemp:  @Html.DisplayFor(modelItem => entry.MinTemp)
    </h3>
    <h3>
        MaxTemp  @Html.DisplayFor(modelItem => entry.MaxTemp)
    </h3>
}

In the controller, pick a single Weather object to pass to the view:

[ActionName("Index")]
public async Task<ActionResult> IndexAsync()
{
    DateTime thisDay = DateTime.Today;
    var items = await DocumentDBRepository<Weather>.GetWeatherAsync(d => d.Id != null);

    // select a single Weather object according to your business logic
    // for example like this:
    var todaysWeather = items.Single(weather => weather.DateTime.Date == thisDay);

    // pass the single Weather to the view
    return View(todaysWeather);
}

Sign up to request clarification or add additional context in comments.

1 Comment

Works perfect @felix-b. It will be great if you can explain code in more detail or provide link to related documentation or examples. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.