Hierarchical Data - Composite Pattern C# - c#

I have a recurring pattern that I see in some of the reports that I have to generate. These reports are date range based reports and need to be aggregated by different levels.
For the sake of simplicity, let's assume that this report produces a title and a quota at the leaf node level (lowest level). At the line item level (which is a collection of various leaf nodes), I would like to aggregate the Quota and provide a separate title. These line items would further roll up to another level which would again aggregate the quota and have a unique title.
So the report would be something like this:
ROOT LEVEL | Title = "Main Report" | Quota = 100
Month Level | Title = "Jan" | Quota = 100
Week Level | Title = "Week 1" | Quota = 25
Week Level | Title = "Week 2" | Quota = 75
Is there way for me to build this using a composite pattern? I have tried numerous approaches. Most of them fall short because I cannot effectively aggregate/sum up the quota to the higher level.
I can build an interface like so:
public interface IInventoryReportItem
{
string Title { get; set; }
int Quota { get; set; }
}
Then I can build a Line Item like so:
public class LineItem : IInventoryReportItem
I can also build a collection like so:
public class LineItems : IList<IInventoryReportItem>, IInventoryReportItem
{
private readonly List<IInventoryReportItem> _subLineItems;
public LineItems()
{
_subLineItems = new List<IInventoryReportItem>();
}
And my report would be like so:
public class InventoryReport
{
public DateRange DateRange { get; set; }
public LineItems LineItems { get; set; }
}
I can build the report easily in a hierarchical fashion now, but I still need to call the aggregation functions from the outside as opposed to it auto-calculating this for me:
var report = new InventoryReport();
var week1Days = new LineItems
{
new LineItem {Quota = 20, Title = "Day 1"},
new LineItem {Quota = 10, Title = "Day 2"}
};
var week2Days = new LineItems
{
new LineItem {Quota = 10, Title = "Day 1"},
new LineItem {Quota = 10, Title = "Day 2"}
};
var week1 = new LineItems {week1Days};
week1.Quota = week1.Sum(x => x.Quota);
week1.Title = "Week1";
var week2 = new LineItems {week2Days};
week2.Quota = week2.Sum(x => x.Quota);
week2.Title = "Week2";
var month1 = new LineItems(new List<IInventoryReportItem> {week1, week2});
month1.Title = "January";
month1.Quota = month1.Sum(x => x.Quota);
report.LineItems = new LineItems(new List<IInventoryReportItem> {month1});
Is there a way I can still have the flexibility of adding either a single line item or a range of items, and it also auto-calculate/aggregate the data for me using the composite?
Any help would be great!
Thank You,
Anup

For me, it seems you're looking for RX (Reactive eXtensions) so you don't have to sum manually each time at each level. Instead, just setup necessary subscriptions and get re-calculations automatically. For example:
Good example of Reactive Extensions Use

I solved this problem. For those who are interested, here's how I solved it:
I built an interface as shown:
public interface IInventoryReportItem
{
string Title { get; set; }
int Quota { get; }
int TotalTicketsSold { get; }
int TotalUnitsSold { get; }
decimal TotalSalesAmount { get; }
}
I implemented this interface in a class called as a LineItem as follows:
public class LineItem : IInventoryReportItem
{
public LineItem(string title, int quota, int totalTicketsSold, int totalUnitsSold, int totalCheckedIn,
decimal totalSalesAmount)
{
Title = title;
Quota = quota;
TotalUnitsSold = totalUnitsSold;
TotalTicketsSold = totalTicketsSold;
TotalCheckedIn = totalCheckedIn;
TotalSalesAmount = totalSalesAmount;
}
public string Title { get; set; }
public int Quota { get; }
public int TotalTicketsSold { get; }
public int TotalUnitsSold { get; }
public int TotalCheckedIn { get; }
public decimal TotalSalesAmount { get; }
}
I also created a custom collection class called LineItems as shown. Note that the collection is of the type IInventoryReportItem itself:
public class LineItems : IInventoryReportItem
{
public string Title { get; set; }
public int Quota => Contents?.Sum(x => x.Quota) ?? 0;
public int TotalTicketsSold => Contents?.Sum(x => x.TotalTicketsSold) ?? 0;
public int TotalUnitsSold => Contents?.Sum(x => x.TotalUnitsSold) ?? 0;
public decimal TotalSalesAmount => Contents?.Sum(x => x.TotalSalesAmount) ?? 0;
public readonly List<IInventoryReportItem> Contents;
public LineItems(List<IInventoryReportItem> lineItems)
{
Contents = lineItems ?? new List<IInventoryReportItem>();
}
}
All of the aggregation is done at this collection class level.
The report class is as follows:
public class InventoryReport
{
public DateRange DateRange { get; set; }
public IInventoryReportItem LineItems { get; set; }
}
I was then able to build the report like so:
Report = new InventoryReport();
var week1 = new LineItems(new List<IInventoryReportItem>
{
new LineItem("Day1", 10, 10, 10, 4, 100),
new LineItem("Day2", 10, 5, 5, 1, 50)
})
{Title = "Week1"};
var week2 = new LineItems(new List<IInventoryReportItem>
{
new LineItem("Day1", 20, 20, 20, 20, 200),
new LineItem("Day2", 20, 5, 5, 5, 50)
}) {Title = "Week2"};
var month1 = new LineItems(new List<IInventoryReportItem> {week1, week2}) {Title = "January"};
Report.LineItems = new LineItems(new List<IInventoryReportItem> {month1}) {Title = "Daily Report"};
The final output (JSON) that I receive from my API is like so:
{
"lineItems": {
"contents": [
{
"contents": [
{
"contents": [
{
"title": "Day1",
"quota": 10,
"totalTicketsSold": 10,
"totalUnitsSold": 10,
"totalCheckedIn": 4,
"totalSalesAmount": 100
},
{
"title": "Day2",
"quota": 10,
"totalTicketsSold": 5,
"totalUnitsSold": 5,
"totalCheckedIn": 1,
"totalSalesAmount": 50
}
],
"title": "Week1",
"quota": 20,
"totalTicketsSold": 15,
"totalUnitsSold": 15,
"totalSalesAmount": 150
},
{
"contents": [
{
"title": "Day1",
"quota": 20,
"totalTicketsSold": 20,
"totalUnitsSold": 20,
"totalCheckedIn": 20,
"totalSalesAmount": 200
},
{
"title": "Day2",
"quota": 20,
"totalTicketsSold": 5,
"totalUnitsSold": 5,
"totalCheckedIn": 5,
"totalSalesAmount": 50
}
],
"title": "Week2",
"quota": 40,
"totalTicketsSold": 25,
"totalUnitsSold": 25,
"totalSalesAmount": 250
}
],
"title": "January",
"quota": 60,
"totalTicketsSold": 40,
"totalUnitsSold": 40,
"totalSalesAmount": 400
}
],
"title": "Daily Report",
"quota": 60,
"totalTicketsSold": 40,
"totalUnitsSold": 40,
"totalSalesAmount": 400
}
}
Using this approach, I was able to eliminate the overhead of performing an aggregation and was still able to use a collection or individual items using the same signature.
Hopefully someone finds this helpful!

Related

Foreach in List and return new empty list in condition

I have create two class as below :
public class SearchResult
{
public int Id { get; set; }
public int Total { get; set; }
public IEnumerable<Book> Books { get; set; }
}
public class Book {
public int BookId { get; set; }
public string BookName { get; set; }
public string Publisher { get; set; } = string.Empty;
public string ISBNCode { get; set; } = string.Empty;
public DateTime PublishDate { get; set; } = DateTime.MinValue;
}
Initial Data as below:
{
"Id": 0,
"Total": 2,
"Books": [
{
"BookId": 1,
"BookName": "Book A",
"Publisher": "Peter",
"ISBNCode": "ISBN0001",
"PublishDate": "2022-03-03T10:19:23.9038822+00:00"
},
{
"BookId": 2,
"BookName": "Book C",
"Publisher": "Kate",
"ISBNCode": "ISBN0003",
"PublishDate": "2022-02-26T10:19:23.9039301+00:00"
}
]
}
I want to make the result if PublishDate > 2022-03-01 and then only keep BookId and PublishDate remain property need to empty, the expected output will be
{
"Id": 0,
"Total": 2,
"Books": [
{
"BookId": 1,
"BookName": "",
"Publisher": "",
"ISBNCode": "",
"PublishDate": "2022-03-03T10:19:23.9038822+00:00"
},
{
"BookId": 2,
"BookName": "Book C",
"Publisher": "Kate",
"ISBNCode": "ISBN0003",
"PublishDate": "2022-02-26T10:19:23.9039301+00:00"
}
]
}
For book class will have many properties and sublist in future, so I would like make a new book, just like this
searchResult.Books.Where(dt => dt.PublishDate > '2022-3-1').ToList().ForEach(
b => new Book {
BookId = b.BookId,
PublishDate = b.PublishDate
});
But I have no idea inside, can anyone advise about this ?
Thank you
I think it's a little strange to clear properties, maybe I don't get it completely, but..
You could create a filter, which will be used with Select:
private static Book ClearPropertiesAfterDate(Book book, DateTime date)
{
if(book.PublishDate > date)
return new Book { BookId = b.BookId, PublishDate = b.PublishDate }
else
return book;
}
// use select to transform each item.
// The ClearPropertiesAfterDate returns a new book (with only the id
// and publishDate when it was released after a certain date
// otherwise it returns the original book.
var booksFiltered = searchResult.Books.Select(dt =>
ClearPropertiesAfterDate(dt, '2022-3-1')).ToList();
'2022-3-1' should be a DateTime? Just should how you could transform items, didn't tested it in visualstudio or fiddle thingy

Plot Multiple Y-Axis against one X-Axis of DateTime in LiveCharts

In my application, I have four series that I want to plot to a Line Graph. Each series is of the same size, three of which are double and the last one is a DateTime list. The three double series come in a list of class objects of type GraphData which look like this:
public class GraphData
{
public string Name { get; set; }
public List<double> Data { get; set; }
}
As an additional requirement, I want to have a Y-Axis of its own for each of these.
Here's my entire program so far, and it plots the three graphs on its own axes with no problem.
public partial class MainWindow : Window
{
public SeriesCollection SeriesCollection { get; set; }
public AxesCollection YAxesCollection { get; set; }
public List<GraphData> GraphDatas { get; set; }
public List<DateTime> TimeStamps { get; set; }
public MainWindow()
{
InitializeComponent();
GraphDatas = GetGraphData();
TimeStamps = GetTimeStamps(GraphDatas[0].Data.Count);
Plot();
}
private void Plot()
{
SeriesCollection = new SeriesCollection();
YAxesCollection = new AxesCollection();
var count = 0;
foreach (var data in GraphDatas)
{
var gLineSeries = new GLineSeries
{
Title = data.Name,
Values = data.Data.AsGearedValues().WithQuality(Quality.Low),
PointGeometry = null,
Fill = Brushes.Transparent,
ScalesYAt = count
};
SeriesCollection.Add(gLineSeries);
YAxesCollection.Add(new Axis() { Title = data.Name });
count++;
}
DataContext = this;
}
private List<GraphData> GetGraphData()
{
var dataList = new List<GraphData>
{
new GraphData() { Name = "DataA", Data = new List<double>() { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 11.0, 11.0, 9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, 1.1, } },
new GraphData() { Name = "DataB", Data = new List<double>() { 26, 33, 65, 28, 34, 55, 25, 44, 50, 36, 26, 37, 43, 62, 35, 38, 45, 32, 28, 34 } },
new GraphData() { Name = "DataC", Data = new List<double>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 } }
};
return dataList;
}
private List<DateTime> GetTimeStamps(int limit)
{
var timeStamps = new List<DateTime>();
var now = DateTime.Now;
for (int i = 0; i < limit; i++)
{
if (i == 0)
timeStamps.Add(now);
else
{
now = now.AddDays(1);
timeStamps.Add(now);
}
}
return timeStamps;
}
}
My XAML looks simple:
<Grid>
<lvc:CartesianChart Series="{Binding SeriesCollection}"
AxisY="{Binding YAxesCollection}"
DisableAnimations="True"
LegendLocation="Right">
</lvc:CartesianChart>
</Grid>
GetGraphData() and GetTimeStamps() are dummy functions here that simulates my original functions.
Now this works fine, except that the X-axis is not DateTime since obviously I haven't plotted it so. But how would I go about doing this?
The official documentation as well as this SO Post only shows how to do this with only one Y-Axis.
I'd start with some changes to the model in order for it to show the full picture. The timestamp is part of the data point and you'll need to wrap them together to allow the Live Charts mapper to plot the data.
public class DataPoint
{
public DataPoint(DateTime timeStamp, double value)
{
TimeStamp = timeStamp;
Value = value;
}
public double Value { get; }
public DateTime TimeStamp { get; }
}
public class GraphData
{
public string Name { get; set; }
public List<DataPoint> Data { get; set; }
}
If you want to keep the current flow of extraction (CSV), you can simply LINQ Zip the data in to its plottable form.
public partial class MainWindow : Window
{
public SeriesCollection SeriesCollection { get; set; }
public Func<double, string> Formatter { get; set; }
public AxesCollection YAxesCollection { get; set; }
public List<GraphData> GraphDatas { get; set; }
public MainWindow()
{
InitializeComponent();
var timeStamps = GetTimeStamps(20);
GraphDatas = GetGraphData(timeStamps);
Plot();
}
private List<GraphData> GetGraphData(List<DateTime> timeStamps)
{
var valuesA = new List<double>() { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 11.0, 11.0, 9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, 1.1, };
var valuesB = new List<double>() { 26, 33, 65, 28, 34, 55, 25, 44, 50, 36, 26, 37, 43, 62, 35, 38, 45, 32, 28, 34 };
var valuesC = new List<double>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
List<DataPoint> MergeData(List<double> values) => timeStamps.Zip(values, (x, y) => new DataPoint(x, y)).ToList();
var dataList = new List<GraphData>
{
new GraphData() { Name = "DataA", Data = MergeData(valuesA) },
new GraphData() { Name = "DataB", Data = MergeData(valuesB) },
new GraphData() { Name = "DataC", Data = MergeData(valuesC) },
};
return dataList;
}
private void Plot()
{
var mapper = Mappers.Xy<DataPoint>()
.X(dp => (double)dp.TimeStamp.Ticks)
.Y(dp => dp.Value);
SeriesCollection = new SeriesCollection(mapper);
YAxesCollection = new AxesCollection();
var count = 0;
foreach (var data in GraphDatas)
{
var gLineSeries = new GLineSeries
{
Title = data.Name,
Values = data.Data.AsGearedValues().WithQuality(Quality.Low),
PointGeometry = null,
Fill = Brushes.Transparent,
ScalesYAt = count
};
SeriesCollection.Add(gLineSeries);
YAxesCollection.Add(new Axis() { Title = data.Name });
count++;
}
Formatter = value => new DateTime((long)value).ToString("yyyy-MM:dd HH:mm:ss");
DataContext = this;
}
private List<DateTime> GetTimeStamps(int limit)
{
var timeStamps = new List<DateTime>();
var now = DateTime.Now;
for (int i = 0; i < limit; i++)
{
if (i == 0)
timeStamps.Add(now);
else
{
now = now.AddDays(1);
timeStamps.Add(now);
}
}
return timeStamps;
}
}
XAML
<lvc:CartesianChart Series="{Binding SeriesCollection}"
AxisY="{Binding YAxesCollection}"
DisableAnimations="True"
LegendLocation="Right">
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Formatter}" />
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>

Returning Specific field in Linq .net core

I am having a problem on returning the fields that I want. I have a model and I want to return a specific data from my Model.
Here is my model.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace api.Models
{
[Table("ScheduleSectionRows")]
public partial class ScheduleSectionRows
{
[Key]
[Column("ScheduleRowID")]
public int ScheduleRowId { get; set; }
[Column("RowID")]
public int? RowId { get; set; }
[Column("StadiumID")]
public int? StadiumId { get; set; }
[Column("SectionID")]
public int? SectionId { get; set; }
[Column("ScheduleID")]
public int? ScheduleId { get; set; }
[Column(TypeName = "decimal(20, 4)")]
public decimal? Amount { get; set; }
public int? AvailableSeats { get; set; }
[StringLength(50)]
public string RowNumber { get; set; }
}
}
Basically I just want to return a Json object that returns the header of ScheduleID and the List of sectionsID of that ScheduleID.
Here's the return the sample data from the DB
[
{
"ScheduleRowId": 20491,
"RowId": 4559,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 8,
"RowNumber": "7"
},
{
"ScheduleRowId": 20492,
"RowId": 4560,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 10,
"RowNumber": "8"
},
{
"ScheduleRowId": 20493,
"RowId": 4561,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 10,
"RowNumber": "9"
},
{
"ScheduleRowId": 20494,
"RowId": 4562,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 10,
"RowNumber": "10"
},
{
"ScheduleRowId": 20495,
"RowId": 4563,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 10,
"RowNumber": "11"
},
{
"ScheduleRowId": 20496,
"RowId": 4564,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 10,
"RowNumber": "12"
},
{
"ScheduleRowId": 20497,
"RowId": 4565,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 5,
"RowNumber": "13"
},
{
"ScheduleRowId": 20498,
"RowId": 4566,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 10,
"RowNumber": "14"
},
{
"ScheduleRowId": 20499,
"RowId": 4567,
"StadiumId": 3,
"SectionId": 81,
"ScheduleId": 43,
"Amount": 100,
"AvailableSeats": 10,
"RowNumber": "15"
}
]
Basically here is the sample output that I want to get.
{
"Status" : "Success",
"ScheduleID" : 43,
"SectionID": [
{
"SectionID" : 81,
},
{
"SectionID" : 82,
},
{
"SectionID" : 83,
},
{
"SectionID" : 84,
}
]
}
Here is the code that I have.
public async Task<SectionDTO<ScheduleSectionRows>> GetSection(int scheduleId)
{
var data = _context.MlfbScheduleSectionRows
.Where(s => s.ScheduleId == scheduleId)
.GroupBy(
s => s.SectionId,
( Section) => new { Section = Section})
.ToListAsync();
return new SectionDTO<MlfbScheduleSectionRows>("Success",scheduleId,data);
}
Here is the DTO.
public class SectionDTO<T> where T : class
{
public SectionDTO(string _status,int _scheduleID, IList<T> _data)
{
Status = _status;
ScheduleID = _scheduleID;
Data = _data;
}
public string Status { get; set; }
public int ScheduleID { get; set; }
public IList<T> Data { get; set; }
}
First of all, the results looks like all that's needed is to return distinct SectionIDs. It could be rewritten as :
var query = _context.MlfbScheduleSectionRows
.Where(s => s.ScheduleId == scheduleId)
.Select(s => s.SectionId)
.Distinct();
var data = await query.ToListAsync();
ToListAsync() doesn't return the data itself, it returns a Task that will return the data once it completes. await is needed to await that task and get the data.
The SQL query will look something like this:
SELECT DISTINCT SectionID
FROM ScheduleSectionRows
WHERE ScheduleId = #p0
The result is a List<int?> which would be serialized to JSON as:
[ 11, 23, 43 ]
To get the desired result we'd have to convert return a type that contains only a single SectionID property. LINQ's .Select() could easily return an anonymous type, eg data.Select(id=>new {SectionId = id}) but we need an actual "named" type to pass to SectionDTO.
public class Section
{
public int? SectionID {get;set;}
}
....
public async Task<SectionDTO<Section>> GetSection(int scheduleId)
{
var query = _context.MlfbScheduleSectionRows
.Where(s => s.ScheduleId == scheduleId)
.Select(s => s.SectionId)
.Distinct();
var data = await query.ToListAsync();
var sections=data.Select(id=>new Section{SectionID=id})
.ToList();
return new SectionDTO<Section>("Success",scheduleId,sections);
}
You have to create c# model according to your json response something like this
public class SectionID
{
public int SectionID { get; set; }
}
public class RootObject
{
public string Status { get; set; }
public int ScheduleID { get; set; }
public List<SectionID> SectionID { get; set; }
}
And then you can fill up this model using Linq .select.
When using groupby with reference type, please ensure that you override equals and gethashcode methods. These are used for generating the "key" of your grouped entity.
In the code below, I have removed the constraint where T : class on your SectionDTO and using the key to be int (non-reference type). It is grouping up the values.
//using Newtonsfot.Json library
//using it to get the list data below, not required for the solution
JArray j = JArray.Parse(GetJSONString());
//Select the schedule id/section id as a list
var list = j.Select(x => new { ScheduleId= Convert.ToInt32(x["ScheduleId"]) ,SectionID = Convert.ToInt32(x["SectionId"]) });
//group by into a SectionDTO object
var result = list.GroupBy(x => x.ScheduleId, (key, value) => new SectionDTO<int> { Status = "Success", ScheduleID = key, Data = value.Select(x=>x.SectionID).ToList() });
//if you want to convert it to a json string then use the convert method
string JSonString = JsonConvert.SerializeObject(result);

Group By Multiple Column In LINQ in C#

I have a class like as follows:
public class ActualClass
{
public string BookName { get; set; }
public string IssuerName { get; set; }
public DateTime DateOfIssue { get; set; }
public bool Status { get; set; }
}
It has following data in the table:
I would like to group them by IssuerName and DateOfIssue for the following viewModel class:
public class ViewModel
{
public string IssuerName { get; set; }
public DateTime DateOfIssue { get; set; }
public List<string> Books { get; set; }
}
And data will be displayed as follows: (Screenshot data will be replaced by the previous table data after successful grouping)
Special attention: Is there anything wrong in my ViewModel according to my expectation?
I tried a lot after following some stackoverflow answers but none did work for me. Any help will be highly appreciated.
The code I have tried:
var viewmodel = from b in db.BookIssues
group b by new
{
b.IssuerName,
b.DateOfIssue
}
into g
select new ViewModel()
{
Name = g.key.IssuerName,
DateOfIssue = g.Key.DateOfIssue,
Books = g.ToList() //Actually this line of code is not working
};
Books = g.ToList() //Actually this line of is not working
Probably because Books property is type of List<string>, not List<ActualClass>.
Can you please try this query, I added b.Select(bn => bn.BookName).ToList() to extract only names of books:
var books = new List<ActualClass>
{
new ActualClass { BookName = "A", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "B", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "C", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "D", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "2" },
new ActualClass { BookName = "E", DateOfIssue = new DateTime(2015, 10, 10, 12, 10, 0), IssuerName = "2" },
new ActualClass { BookName = "F", DateOfIssue = new DateTime(2015, 10, 10, 12, 10, 0), IssuerName = "2" }
};
var result = books.GroupBy(x => new { x.IssuerName, x.DateOfIssue })
.Select(b => new ViewModel
{
Books = b.Select(bn => bn.BookName).ToList(),
// Accessing to DateOfIssue and IssuerName from Key.
DateOfIssue = b.Key.DateOfIssue,
IssuerName = b.Key.IssuerName
});
I grouped by: x.IssuerName, x.DateOfIssue. I did that by passing anonymous type in GroupBy() with following manner: x => new { x.IssuerName, x.DateOfIssue }.
Now they are in key and you can access to IssuerName and DateOfIssue from KEY in SELECT statement like in following: b.Key.IssuerName and b.Key.DateOfIssue.
if you need to select list of books from group result, you need Books = v.Select(c=>c.BookName).ToList() also note that in case of you have time in issue date time you may need to group by only the date part using EntityFunctions.TruncateTime function. if you only storing date only then you can ignore this function.
var viewmodel = db.BookIssues.GroupBy(x=>new {IssuerName =x.IssuerName, DateOfIssue=EntityFunctions.TruncateTime(x.DateOfIssue) })
.Select(v=>new ViewModel(){IssuerName =v.Key.IssuerName, DateOfIssue = v.Key.DateOfIssue, Books = v.Select(c=>c.BookName).ToList() })
.ToList();

Json MultiArray & ServiceStack

There is a line {"level": [{"level": 1, "points": 0, "name": "Some"}, {"level": 2, "points": 50, "name": "Second level "}, {" level ": 3," points ": 100," name ":" third level "}]}
How to fix the existing code or to add to get at the request when the exact same line?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
using System.ComponentModel;
using ServiceStack.ServiceHost;
namespace MSWA.Classes
{
[DataContract]
[Description("Get config")]
[RestService("/gconf")]
public class GameConfiguration
{
[DataMember]
public string puid { get; set; }
}
[DataContract]
public class GameConfigurationResponse
{
[DataMember]
public LevelList ll;
[DataMember]
public string TestResponse { get; set; }
}
// Level List
[DataContract]
public class LevelList
{
[DataMember]
public List<Level> level { get; set; }
}
// Desc one level
[DataContract]
public class Level
{
[DataMember]
public int level { get; set; }
[DataMember]
public int points { get; set; }
[DataMember]
public string name { get; set; }
}
/// Create your Web Service implementation
public class GameConfigurationService : IService<GameConfiguration>
{
public object Execute(GameConfiguration request)
{
// Original data
string respValue = "";
respValue = request.puid;
if (respValue == null || respValue == "0") respValue = "0";
Level lvl = new Level(){level=1, points=0, name=""};
LevelList llist = new LevelList();
llist.level.Add(lvl);
return new GameConfigurationResponse
{
ll = llist
};
}
}
}
I hope I have understood your question. I think you are asking how to update your existing code so that it outputs this object:
{
"level": [
{"level": 1, "points": 0, "name": "Some"},
{"level": 2, "points": 50, "name": "Second level"},
{"level": 3, "points": 100, "name": "Third level"}
]
}
You should remove these lines:
Level lvl = new Level(){level=1, points=0, name=""};
LevelList llist = new LevelList();
llist.level.Add(lvl);
And replace with these:
LevelList llist = new LevelList();
llist.level = new List<Level>();
llist.level.Add(new Level { level = 1, points = 0, name = "Some" });
llist.level.Add(new Level { level = 2, points = 50, name = "Second level" });
llist.level.Add(new Level { level = 3, points = 100, name = "Third level" });
Update:
I presume from your comment you want to change GameConfigurationResponse to just output the List<Level> without having the LevelList object?
[DataContract]
public class GameConfigurationResponse
{
[DataMember]
public List<Level> level { get; set; }
}
So the corresponding Execute method would be:
public object Execute(GameConfiguration request)
{
// Original data
string respValue = request.puid ?? "0";
return new GameConfigurationResponse
{
level = new List<Level> {
new Level { level = 1, points = 0, name = "Some" },
new Level { level = 2, points = 50, name = "Second level" },
new Level { level = 3, points = 100, name = "Third level" }
}
};
}
I am not sure what you are using respValue for. I have simplified it to string respValue = request.puid ?? "O"; That will set respValue to request.puid unless it is null, in which case it will be set to 0. But you aren't using this value, at least not in the code posted.

Categories