Asserting in MSTEST that a collection OrderBy worked properly - c#

Good morning. I am writing a unit test to validate that my API handler is sorting the collection properly. As you can see, it's mocked data and I intentionally created the test data out of order to test the OrderBy functionality. Once the handler returns the DTO collection, I want to validate that the "act" collection is in the same order as my "expectedDTO" collection. Here's the code:
[TestMethod]
public async Task GetByDay_ReturnsDtoList_WhenFound()
{
var testData = TestMethodData();
var expectedCalendarDto = ExpectedDto();
var testCalendar = testData;
var query = new GetCalendarByDaysQuery();
_mockCalendarRepo.Setup(m => m.GetItemsAsync(It.IsAny<ISpecification<CalendarDay>>(), null, null))
.ReturnsAsync(testCalendar);
var sut = new GetCalendarByDaysHandler(_mockCalendarRepo.Object, _mapper);
var act = await sut.HandleAsync(query);
Assert.IsNotNull(act);
Assert.IsInstanceOfType(act, typeof(IEnumerable<CalendarDto>));
Assert.AreEqual(expectedCalendarDto, act);
}
private GetItemsResult<IEnumerable<CalendarDay>> TestMethodData()
{
var testData = new GetItemsResult<IEnumerable<CalendarDay>>();
testData.ContinuationToken = null;
testData.Results = new List<CalendarDay>()
{
new CalendarDay { Id = "4-5-4|2021-04-01", FiscalYear = 2021, CalendarType = "4-5-4", Quarter = 2, Period = 4, CalendarDate = "2021-04-01", WeekOfYear = 13, DayOfYear = 61, WeekOfPeriod = 1 },
new CalendarDay { Id = "4-5-4|2021-08-01", FiscalYear = 2021, CalendarType = "4-5-4", Quarter = 3, Period = 8, CalendarDate = "2021-08-01", WeekOfYear = 24, DayOfYear = 121, WeekOfPeriod = 1 },
new CalendarDay { Id = "4-5-4|2021-01-01", FiscalYear = 2021, CalendarType = "4-5-4", Quarter = 1, Period = 1, CalendarDate = "2021-01-01", WeekOfYear = 1, DayOfYear = 1, WeekOfPeriod = 1 }
};
return testData;
}
private IEnumerable<CalendarDto> ExpectedDto()
{
var testDto = new List<CalendarDto>()
{
new CalendarDto { FiscalYear = 2021, CalendarType = "4-5-4", Quarter = 1, Period = 1, CalendarDate = "2021-01-01", WeekOfYear = 1, DayOfYear = 1, WeekOfPeriod = 1 },
new CalendarDto { FiscalYear = 2021, CalendarType = "4-5-4", Quarter = 2, Period = 4, CalendarDate = "2021-04-01", WeekOfYear = 13, DayOfYear = 61, WeekOfPeriod = 1 },
new CalendarDto { FiscalYear = 2021, CalendarType = "4-5-4", Quarter = 3, Period = 8, CalendarDate = "2021-08-01", WeekOfYear = 24, DayOfYear = 121, WeekOfPeriod = 1 }
};
return testDto;
}
Currently, in trying to compare the two collections, the test is failing saying that they aren't equal.
Assert.AreEqual failed. Expected:<System.Collections.Generic.List`1[cscentcalendar.infrastructure.DTOs.CalendarDto]>. Actual:<System.Collections.Generic.List`1[cscentcalendar.infrastructure.DTOs.CalendarDto]>.
Not sure what I'm doing wrong. Any assistance would be greatly appreciated.

I believe I solved the problem by using fluent assertion and adding the following line of code to test the two collections. Let me know if this is incorrect. Thanks again!
expectedCalendarDto.Should().BeEquivalentTo(act);

Related

LINQ to Object - How to implement dynamic SELECT projection of sub elements

I have several classes of business logic:
public class Client {
public string Code { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public string Account { get; set; } = string.Empty;
public Total Total { get; set; } = new Total();
public List<Month> Months { get; set; } = new List<Month>();
}
public class Month {
public int Number { get; set; } = 0;
public string Name { get; set; } = string.Empty;
public DateTime Start { get; set; } = new DateTime();
public DateTime End { get; set; } = new DateTime();
public Total Summary { get; set; } = new Total();
}
public class Total {
public int Count { get; set; } = 0;
public decimal Sum { get; set; } = 0.0m;
}
which are instanced as follows:
List<Client> clients = new List<Client>() {
new Client {
Code = "7002.70020604",
Status = "Active",
Account = "7002.915940702810005800001093",
Total = new Total {
Count = 9,
Sum = 172536.45m
},
Months = new List<Month>() {
new Month {
Number = 0,
Name = "January",
Start = new DateTime(2021, 1, 1, 0, 0, 0),
End = new DateTime(2021, 1, 31, 23, 59, 59),
Summary = new Total {
Count = 6,
Sum = 17494.50m
}
},
new Month {
Number = 1,
Name = "February",
Start = new DateTime(2021, 2, 1, 0, 0, 0),
End = new DateTime(2021, 2, 28, 23, 59, 59),
Summary = new Total {
Count = 3,
Sum = 155041.95m
}
},
new Month {
Number = 2,
Name = "March",
Start = new DateTime(2021, 3, 1, 0, 0, 0),
End = new DateTime(2021, 3, 31, 23, 59, 59),
Summary = new Total {
Count = 0,
Sum = 0.0m
}
}
}
},
new Client {
Code = "7002.70020604",
Status = "Active",
Account = "7002.800540702810205800001093",
Total = new Total {
Count = 4,
Sum = 16711.21m
},
Months = new List<Month>() {
new Month {
Number = 0,
Name = "January",
Start = new DateTime(2021, 1, 1, 0, 0, 0),
End = new DateTime(2021, 1, 31, 23, 59, 59),
Summary = new Total {
Count = 0,
Sum = 0.0m
}
},
new Month {
Number = 1,
Name = "February",
Start = new DateTime(2021, 2, 1, 0, 0, 0),
End = new DateTime(2021, 2, 28, 23, 59, 59),
Summary = new Total {
Count = 0,
Sum = 0.0m
}
},
new Month {
Number = 2,
Name = "March",
Start = new DateTime(2021, 3, 1, 0, 0, 0),
End = new DateTime(2021, 3, 31, 23, 59, 59),
Summary = new Total {
Count = 4,
Sum = 16711.21m
}
}
}
}
};
I'm trying to arrange aggregate data of a view like this:
+---------------+--------+------------------+-------------------+------------------+-------------------+
| Code | Status | January | February | March | Total |
| | +-------+----------+-------+-----------+-------+----------+-------+-----------+
| | | Count | Sum | Count | Sum | Count | Sum | Count | Sum |
+---------------+--------+-------+----------+-------+-----------+-------+----------+-------+-----------+
| 7002.70020604 | Active | 6 | 17494.50 | 3 | 155041.95 | 4 | 16711.21 | 13 | 189247.66 |
+---------------+--------+-------+----------+-------+-----------+-------+----------+-------+-----------+
using projection like this:
clients
.GroupBy(x => x.Code)
.Select(y => new {
Code = y.First().Code,
Status = y.First().Status,
Account = y.First().Account,
Total = new {
Count = y.Sum(z => z.Total.Count),
Sum = y.Sum(z => z.Total.Sum)
},
Months = new {
/*
?
*/
}
});
But I can't project the data by month. Assuming the date range (months) can be more than just this example. Please help!
Full interactive code listing at dotnetfiddle
You can use SelectMany to get months out of y and then group by month similarly as you group by code:
//...
Months = y
.SelectMany(client => client.Months)
.GroupBy(month => month.Name, (_, months) => new {
Number = months.First().Number,
Name = months.First().Name,
Start = months.First().Start,
End = months.First().End,
Summary = new {
Count = months.Sum(z => z.Summary.Count),
Sum = months.Sum(z => z.Summary.Sum)
}
}).ToList()
//...
That being said I don't suggest to use y.First() or months.First() more than once in each function because it makes an enumeration each time it is used. The following should in general have better performance:
(_, months) => {
var month = months.First();
return new {
Number = month.Number,
Name = month.Name,
Start = month.Start,
End = month.End,
Summary = new {
Count = months.Sum(z => z.Summary.Count),
Sum = months.Sum(z => z.Summary.Sum)
}
}
}
which is also not ideal because we're still making 3 enumerations here (1 enumeration in .First() and 1 enumeration for every .Sum(...)).
Even better approach would be to use Aggregate function which will do only a single enumeration:
(_, months) => months
.Aggregate((res, nextVal) => new Month {
Number = nextVal.Number,
Name = nextVal.Name,
Start = nextVal.Start,
End = nextVal.End,
Summary = new Total {
Count = res.Summary.Count + nextVal.Summary.Count,
Sum = res.Summary.Sum + nextVal.Summary.Sum
}
})
This LINQ query should prepare data for visualization:
clients
.GroupBy(x => new {x.Code, x.Status})
.Select(g => new
{
Code = g.Key
MonthsSummary = g.SelectMany(x => x.Months)
.OrderBy(x => x.Start)
.GroupBy(x => new {x.Start, x.Name})
.Select(gm => new
{
gm.Key.Name,
Count = gm.Sum(x => x.Summary.Count),
Sum = gm.Sum(x => x.Summary.Sum),
})
.ToList()
});

Sort ObservableCollection in Combobox C# WPf

I want to set sorted the ObservableCollection in combobox,
my result without sorted:
my ViewModel:
private ObservableCollection<IdentificationSystemType> codeTypeEnum;
public IdentificationSystemType CodeType
{
get { return codeType; }
set { codeType = value;
OnPropertyChanged("CodeType");
}
}
public NewIdentificationSystemViewModel()
{
_identificationToAdd = new IdentificationSystem();
identificationDeviceToAdd = new IdentificationDevice();
_resetIdentificationCmd = new RelayCommand<string>(resetIdentification);
saveCommand = new RelayCommand<string>(addFunc, canSave);
codeTypeEnum = new ObservableCollection<IdentificationSystemType>(Enum.GetValues(typeof(IdentificationSystemType)).Cast<IdentificationSystemType>());
}
I had try with var ordered = codeTypeEnum.OrderBy(x => x); but nothing ..it is the same
my Enum declaration:
public enum IdentificationTypes : int
{
TerminalEntryGate = 1,
TerminalExitGate = 2,
LoadingAreaEntryGate = 3,
LoadingAreaExitGate = 4,
IslandEntryGate = 5,
IslandExitGate = 6,
BayEntryGate = 7,
BayExitGate = 8,
ScalingAreaEntryGate = 9,
ScalingAreaExitGate = 10,
OfficeAreaEntryGate = 11,
OfficeAreaExitGate = 12,
TankFarmEntryGate = 13,
TankFarmExitGate = 14,
StagingAreaEntryGate = 15,
StagingAreaExitGate = 16,
LoadingBayIdentification = 21,
LoadingArmIdentification = 22,
LoadingIslandIdentification = 23,
PresetIdentification = 27
}
How can I fix that?
thanks,
Change:
codeTypeEnum = new ObservableCollection<IdentificationSystemType>(Enum.GetValues(typeof(IdentificationSystemType))
.Cast<IdentificationSystemType>());
to:
codeTypeEnum = new ObservableCollection<IdentificationSystemType>(Enum.GetValues(typeof(IdentificationSystemType))
.Cast<IdentificationSystemType>().OrderBy(x => x.ToString()));
to force it to be ordered alphabetically.
As your enum is of type int you are ordering your collection by those numbers. If you want to order your collection alphabetically you need to parse the integers to strings first.
You can do this in the key selector function you are giving the OrderBy method.
var values = Enum.GetValues(typeof(IdentificationTypes)).Cast<IdentificationTypes>();
var valueList = new ObservableCollection<IdentificationTypes>(values);
var orderedList = valueList.OrderBy(x => x.ToString());

LINQ Left Join, Group By and Count

I'm trying to counting how many messages sent per hour. but my code returns wrong result
This is original sqlite query that I used, it works right.
SELECT Hours.hour, ifnull(count(Messages.hour),0) as count Hours LEFT JOIN Messages on Messages.hour = Hours.hour by Hours.hour
but I'm a new at LINQ and here is my code and query.
int[] Hours = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 };
var sortByHour = from h in Hours
join m in Messages on h equals m.Hour into g
orderby h ascending
select new
{
Hour = h,
Total = g.Count()
};
and it returns
[0] { Hour = 0, Total = 33485 }
[1] { Hour = 1, Total = 0 }
[2] { Hour = 2, Total = 0 }
[3] { Hour = 3, Total = 0 }
...
[23] { Hour = 23, Total = 0 }
first data has number of total rows and others has 0. its wrong.
result should have to be like this
[0] { Hour = 0, Total = 501 }
[1] { Hour = 1, Total = 408 }
[2] { Hour = 2, Total = 181 }
[3] { Hour = 3, Total = 84 }
...
[23] { Hour = 23, Total = 1055 }
how can I fix my code? thanks in advanced.
Answer is
var res = from h in Hours
join m in Messages on h equals m.Hour into jn
from j in jn.DefaultIfEmpty()
select new
{
hour = h,
count = j != null ? jn.Count(i => i.Hour == h) : 0
}
But if you have Database, model, relation between Hours and Messages, and we are talking about linq to sql or lint to entities, it's better use (as usr mentioned at comments) h.Messages.Count() and let Database engine make a request
Something like this should solve your problem.
Setup:
int[] Hours = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
var Messages = new List<Message>()
{
new Message() { MessageId = 1, Hour = 5 },
new Message() { MessageId = 2, Hour = 7 },
new Message() { MessageId = 3, Hour = 5 },
new Message() { MessageId = 4, Hour = 3 },
new Message() { MessageId = 5, Hour = 7 },
new Message() { MessageId = 6, Hour = 5 },
};
Query:
var sortByHour = Hours.Select(h =>
new { Hour = h, Total = Messages.Count(m => m.Hour == h) } );
Basically, you just select each hour and its count in Messages.

LINQ Anonymous type - bypass Group By

I have LINQ sql (see below, thanks to Cameron ). I am trying to get a property (ItemCode) from class First without using that in Group by clause.
How do I do that?
Don't use First.ItemCode in group by but still want it in output by First.Begin, First.End order by decending.
public class First
{
public string Account;
public DateTime Begin;
public DateTime End;
public decimal Amount;
public string ItemCode;
}
public class Second
{
public string Account;
public DateTime Begin;
public DateTime End;
public decimal Amount;
}
List<First> firstAccount = new List<First>();
List<Second> secondAccount = new List<Second>();
firstAccount.Add(new First()
{
Account = "1234",
Begin = new DateTime(2014, 5, 13),
End = new DateTime(2014, 6, 12),
Amount = 9999,
ItemCode = "AAA"
});
firstAccount.Add(new First()
{
Account = "1234",
Begin = new DateTime(2014, 6, 13),
End = new DateTime(2014, 7, 7),
Amount = 1000,
ItemCode = "AAA"
});
firstAccount.Add(new First()
{
Account = "1234",
Begin = new DateTime(2014, 6, 13),
End = new DateTime(2014, 7, 14),
Amount = 0,
ItemCode = ""
});
firstAccount.Add(new First()
{
Account = "1234",
Begin = new DateTime(2014, 7, 7),
End = new DateTime(2014, 7, 14),
Amount = 1000,
ItemCode = "BBB"
});
secondAccount.Add(new Second()
{
Account = "1234",
Begin = new DateTime(2014, 5, 13),
End = new DateTime(2014, 6, 12),
Amount = 9999
});
secondAccount.Add(new Second()
{
Account = "1234",
Begin = new DateTime(2014, 6, 13),
End = new DateTime(2014, 7, 14),
Amount = 2000
});
var result = from account in (from first in firstAccount
join second in secondAccount
on first.Account equals second.Account
where
((first.Begin >= second.Begin && first.Begin <= second.Begin) &&
(first.End >= second.Begin && first.End <= second.End))
select new
{
first.Account,
second.Begin,
second.End,
first.Amount,
first.ItemCode
})
group account by new {account.Account, account.Begin, account.End }
into groupedAccounts
select new
{
groupedAccounts.Key.Account,
groupedAccounts.Key.Begin,
groupedAccounts.Key.End,
Sum = groupedAccounts.Sum(a => a.Amount)
};
One way to get the itemcode is to change the last select.
Add this line
Itemcode = String.Join(" ",groupedAccounts.Select(q=> q.ItemCode))
after Sum = groupedAccounts.Sum(a => a.Amount),
It should produce itemcode
foreach (var data in result)
{
Console.WriteLine(data.Account + " " + data.Itemcode);
}
Output
1234 AAA
1234 AAA

SQL Query Order of Operations

It Friday. My brain is fried. This is very simple and I'm ashamed for asking this:
I simply want to query against my Event table (which has a non-null Start Time and nullable End time).However, given my unit test, I keep getting 2 records back (the 2:00 and the 4:00 records, not just the 2:00 as I'd expect)
SELECT
EventId,
TaskId,
MachineId,
LoginId,
EventStartTimeUtc,
EventEndTimeUtc,
OpCode,
UnitId,
PositionId,
WebId,
Comment,
MakereadyCount,
GrossCount,
NetCount,
PerpetualGross,
PerpetualNet,
PerpetualMakeready,
TaskState,
EventTypeId,
IsAutoEvent,
IsTransferred,
LastUpdatedTimeUtc
FROM Event
WHERE MachineId = #MachineId#
AND EventStartTimeUtc >= #StartTimeUtc#
AND (EventEndTimeUtc IS NULL
OR ((EventEndTimeUtc IS NOT NULL) AND EventEndTimeUtc <![CDATA[<=]]> #EndTimeUtc#))
[Test]
public void ShouldSelectEventsInRange()
{
//Arrange
TaskDto testTask = _testRepository.CreateTask(new TaskDto { TaskId = 1234567 }, true);
var machineId = ((ArtemisRepository)_testRepository).CreateMachine(123, "MR40SIM", "0V7", 200, 100, 555555); //Requires a 555555 down-task to exist in database
EventRecordDto result = _testRepository.CreateEvent(new EventRecordDto {TaskId = testTask.TaskId, MachineId = machineId, EventStartTimeUtc = new DateTime(2014, 4, 15, 1, 50, 0), OpCode = "100", MakereadyCount = 1752, GrossCount = 5660, NetCount = 2512, Comment = "Test Event", IsAutoEvent = false, IsTransferred = false});
EventRecordDto result2 = _testRepository.CreateEvent(new EventRecordDto {TaskId = testTask.TaskId, MachineId = machineId, EventStartTimeUtc = new DateTime(2014, 4, 15, 2, 0, 0), OpCode = "100", MakereadyCount = 1752, GrossCount = 5660, NetCount = 2512, Comment = "Test Event", IsAutoEvent = false, IsTransferred = false});
EventRecordDto result3 = _testRepository.CreateEvent(new EventRecordDto {TaskId = testTask.TaskId, MachineId = machineId, EventStartTimeUtc = new DateTime(2014, 4, 15, 4, 0, 0), OpCode = "100", MakereadyCount = 1752, GrossCount = 5660, NetCount = 2512, Comment = "Test Event", IsAutoEvent = false, IsTransferred = false});
//Act
var results = _testRepository.SelectEventsInRange(machineId, new DateTime(2014, 4, 15, 2, 0, 0), new DateTime(2014, 4, 15, 3, 59, 59));
//Assert
Assert.IsTrue(results.Count == 1, "{0} records came, instead of the 1 record expeted!", new object[] { results.Count });
Assert.IsTrue(results.Any(r => r.EventId == result2.EventId), "Expected Event (Id: {0}), Actual Event (ID: {1})", new object[] { result2.EventId, results[0].EventId});
}
The query asks for records where the EventStartTimeUtc is greater than or equal to 2:00, which both those records are, and where the EventEndTimeUtc is null, which they both are.
Did you mean EventStartTimeUtc where you have EventEndTimeUtc in the WHERE clause?

Categories