I'm trying to test if the resulting list is not null and matches what is expected from the service method.
I am using xUnit and Moq in ASP.NET C#.
I am not able to get the expected results (it should fail with the given parameters).
This is my test:
[Theory]
[InlineData(1, "2022")]
public async Task GetLevelsAsync_ReturnsLevelList(int appId, string year)
{
//Arrange
IOptions<Settings> options = Options.Create(set);
_geoRepository.Setup(g => g.GetLevelsByYear(appId, year)).ReturnsAsync(GetLevelsYear2022());
GeoService geo = new GeoService(_geoRepository.Object, _loggerGeoService.Object, options);
//Act
List<Level> result = await geo.GetLevelsAsync(appId, year);
//Assert
Assert.NotNull(result);
Assert.Equal(GetLevelsYear2022().Count(), result.Count());
Assert.Equal(
GetLevelsYear2022().OrderByDescending(l => l.Id),
result.OrderByDescending(l => l.Id),
new LevelEqualityComparer());
Assert.NotEqual(
GetLevelsYear2021().OrderByDescending(l => l.Id),
result.OrderByDescending(l => l.Id),
new LevelEqualityComparer());
}
public class LevelEqualityComparer : IEqualityComparer<Level>
{
public bool Equals(Level x, Level y)
{
if (x is null || y is null) return false;
return x.Id == y.Id;
}
public int GetHashCode(Level obj)
{
return obj.Id;
}
}
This is my controller:
public async Task<ActionResult<List<Level>>> GetLevelsAsync([FromRoute] string CountryCode, string Year)
{
int appId = ICCode.FromName(CountryCode).Id;
var result = await _geoService.GetLevelsAsync(appId, Year);
return result;
}
This is my service:
public async Task<List<Level>> GetLevelsAsync(int appId, string year)
{
if (string.IsNullOrWhiteSpace(year))
throw new ArgumentNullException(nameof(year));
var result = await _geoRepository.GetLevelsByYear(appId, year);
return result;
}
This is my repository:
public async Task<List<Level>> GetLevelsByYear(int appId, string year)
{
if (string.IsNullOrWhiteSpace(year))
throw new ArgumentNullException(nameof(year));
var result = new List<Level>();
var parameters = new { AppId = appId, Year = year };
string sql = #"SELECT * FROM [Table] WHERE AppId = #AppId AND DataYear = #Year";
using (IDbConnection db = new SqlConnection(_settings.SqlServerConnString))
{
try
{
result = db.Query<Level>(sql, parameters).ToList();
}
catch (Exception e)
{
_logger.LogError(e, "Error querying levels by year", new { appId, year });
throw;
}
}
return result;
}
This is my Level class:
public class Level
{
public int Id { get; set; }
public string Year { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int GeometryType { get; set; }
}
Your service returns List<Level>.
If you call GetLevelList().ToString() the result is always going to show the name of the type because List<T> doesn't override ToString(). That means that regardless of what the list contains, ToString() will always return the same result.
It appears that what you want is to compare two List<Level> and see if they contain the same result.
How you do that depends somewhat on how you compare two instances of Level to determine whether they are equivalent. You could determine that they're equivalent if all of their properties are equal. Or if these represent records from a database you could determine that two are the same if they have the same Id.
There are different ways to do this. Here's one.
First create an IEqualityComparer that compares two instances of Level to see if they are equivalent using criteria that you define. (This probably belongs in your test project.)
public class LevelEqualityComparer : IEqualityComparer<Level>
{
public bool Equals(Level x, Level y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return x.Id == y.Id;
}
public int GetHashCode(Level obj)
{
return obj.Id;
}
}
That looks like a lot of typing. Resharper (developer productivity tool) generated it for me. For practical purposes you could shorten it to
public class LevelEqualityComparer : IEqualityComparer<Level>
{
public bool Equals(Level x, Level y)
{
if(x is null || y is null) return false;
return x.Id == y.Id;
}
public int GetHashCode(Level obj)
{
return obj.Id;
}
}
Now, if you have two collections of Level, you'd want to see if they are the same. You can do this:
Assert.Equal(
levels1.OrderBy(l=>l.Id),
levels2.OrderBy(l=>l.Id),
new LevelEqualityComparer());
This will compare the two collections and see if they contain the same items. It uses the LevelEqualityComparer, which means it will only look at the IDs to compare them. It sorts them because otherwise they could contain the same IDs in a different order, and they wouldn't match.
If wanted comparing Level by ID to be the default in all your code then you could define that within the Level class itself, like this:
public class Level : IEqualityComparer<Level>
{
public int Id { get; set; }
public string Year { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int GeometryType { get; set; }
public bool Equals(Level x, Level y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return x.Id == y.Id;
}
public int GetHashCode(Level obj)
{
return obj.Id;
}
}
Then you wouldn't need the separate equality comparer class.
Or, what if you just wanted to compare the IDs, and that's never going to change? Then you could just skip the equality comparison and do this:
var levels1Ids = levels1.Select(l => l.Id).OrderBy(id => id);
var levels2Ids = levels2.Select(l => l.Id).OrderBy(id => id);
Assert.Equal(levels1Ids, levels2Ids);
I saved that for last because it doesn't cover as many scenarios, but it's easiest and what I would probably do.
It just gets the IDs from both collections, sorts them, and compares the two lists of IDs.
Related
I'm trying to create an audit-trail like order state history table. This way, Orders could have many OrderStates, and a single State which points to the most recent history item. So far so good when saving an updating. The problems arise when I try to query as if I was using an enum:
public class OrderState
{
public static OrderState Placed = new OrderState("Placed", 1, 1);
public static OrderState Accepted = new OrderState("Accepted", 10, 2);
public static OrderState Cancelled = new OrderState("Cancelled", 20, 3);
public static OrderState Completed = new OrderState("Completed", 30, 4);
protected OrderState()
{
}
public OrderState(string name, int order, int id)
{
Name = name;
Order = order;
Id = id;
}
public int Id { get; set; }
public string Name { get; protected set; }
public int Order { get; protected set; }
public static bool operator == (OrderState state1, OrderState state2)
{
if (ReferenceEquals(state1, null))
{
return ReferenceEquals(state2, null);
}
return state1.Equals(state2);
}
public static bool operator !=(OrderState state1, OrderState state2)
{
return !(state1 == state2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (!(obj is OrderState))
{
return false;
}
return Equals((OrderState)obj);
}
public virtual bool Equals(OrderState other)
{
return other.Id.Equals(Id);
}
public override int GetHashCode()
{
unchecked
{
return ((Id.GetHashCode())*397) ^ Order;
}
}
}
Order class
public class Order
{
public Order()
{
Progress(OrderState.Placed);
}
public int Id { get; set; }
public virtual OrderState State
{
get { return States.OrderByDescending(x => x.State.Order).FirstOrDefault()?.State; }
}
public void Progress(OrderState state)
{
if (States.All(x => x.State != state))
{
States.Add(new OrderStateHistory()
{
Order = this,
State = state
});
}
}
public virtual ICollection<OrderStateHistory> States { get; set; } = new List<OrderStateHistory>();
}
In my code, things like these work fine:
order.Progress(OrderState.Accepted);, if (order.State == OrderState.Accepted)
However, what I'd like to get to is Where(x => x.State.Equals(OrderState.Accepted)) or Where(x => x.State == OrderState.Accepted)
Unfortunately, either of the criterias will yield an 'The specified type member 'State' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.' error.
I know I have been able to do this with NHibernate. Can I even do this with EF?
Since EF needs to translate your LINQ statements to SQL statements,
you can't do this. If you have complex comparison logic in your
overridden Equals() method you will have to duplicate that in the
LINQ statement.
public IQueryable<Foo> FoosEqualTo(IQueryable<Foo> allFoos, Foo target) {
return from foo in allFoos
where foo.Id == target.Id // or other comparison logic...
select foo;
}
public Foo getFoo(Foo target) {
return FoosEqualTo(DC.foos, target).FirstOrDefault();
}
I have the following class and a HashSet of it:
public class VersionSettings {
public String Culture { get; set; }
public String Domain { get; set; }
public String Name { get; set; }
}
HashSet<VersionSetting> vs = ...
Questions
I would like two VersionSettings to be equal when Culture is the same.
In this moment it is not what happens. How can i solve this?
Would be possible to get a VersionSetting from the HashSet using:
var b = vs["en-US"];
Maybe using some HashSet extension?
You can override Equals and GetHashCode to not only compare references what Object.Equals does:
public class VersionSettings
{
public String Culture { get; set; }
public String Domain { get; set; }
public String Name { get; set; }
public override bool Equals(object obj)
{
VersionSettings other = obj as VersionSettings;
if(other == null) return false;
return Culture == other.Culture;
}
public override int GetHashCode()
{
return Culture == null ? 0 : Culture.GetHashCode();
}
}
Another approach is to implement a custom IEqualityComparer<VersionSettings> which does not require to modify the class itself:
public class VersionSettingComparer : IEqualityComparer<VersionSettings>
{
public bool Equals(VersionSettings x, VersionSettings y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.Culture == y.Culture;
}
public int GetHashCode(VersionSettings obj)
{
if(obj == null) return 0;
return obj.Culture == null ? 0 : obj.Culture.GetHashCode();
}
}
Now you can use the constructor of HashSet<T>:
var vs = new HashSet<VersionSettings>(new VersionSettingComparer());
According to your second question how you can access the HashSet<VersionSetting> in this way:
var enUsSetting = vs["en-US"];
That doesn't work because a hashset has no indexer like a collection or dictionary. You probably want a Dictionary<string, VersionSettings> instead.
As simple but not that efficient workaround you can use LINQ:
var enUsSetting = vs.FirstOrDefault(x => x.Culture == "en-US");
If you want to create the mentioned dictionary you can also use LINQ:
Dictionary<string, VersionSettings> cultureLoookup = vs
.ToDictionary(x => x.Culture, x => x); // you only need to create this once
Now this works:
var enUsSetting = cultureLoookup["en-US"];
Why in a simple way you doesn't use Dictionary<string, VersionSettings>. For instace:
var settings = new Dictionary<string, VersionSettings>();
settings["en-US"] = usSettings;
settings["es-ES"] = esSettings;
...
var currentSetting = settings[currentSettingIdText];
It is just a tip.
This is my object:
public class MyObject
{
public int id { get; set; }
public string fileName { get; set; }
public string browser { get; set; }
public string protocol { get; set; }
public string family { get; set; }
}
and i have a list of my object:
List<Capture> list = db.Captures.Where(x => x.family == "Web").ToList();
What i want to do is get new list that removed the duplicate protocol.
for example if i have in my list 10 object and 9 of them with protocol DOC and 1 PDF i want a new list with only 2 object DOC and 1 PDF
There are several ways to do this, depending on how you generally want to use the instances of your MyObject class.
The easiest one is implementing the IEquatable<T> interface so as to compare only the protocol fields:
public class MyObject : IEquatable<MyObject>
{
public sealed override bool Equals(object other)
{
return Equals(other as MyObject);
}
public bool Equals(MyObject other)
{
if (other == null) {
return false;
} else {
return this.protocol == other.protocol;
}
}
public override int GetHashCode()
{
return protocol.GetHashCode();
}
}
You can then call Distinct before converting your enumerable into a list.
Alternatively, you can use the Distinct overload that takes an IEqualityComparer.
The equality comparer would have to be an object that determines equality based on your criteria, in the case described in the question, by looking at the protocol field:
public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
if (x == null) {
return y == null;
} else {
if (y == null) {
return false;
} else {
return x.protocol == y.protocol;
}
}
}
public int GetHashCode(MyObject obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
return obj.protocol.GetHashCode();
}
}
I believe this is the simplest approach: The following will group list by protocol and then get the first instance from each group to produce an enumerable with one instance of each type of protocol.
list.GroupBy(x => protocol, x => x)
.SelectMany(k, v => v.First());
You could either use Distinct, or use the same solution provided here:
Distinct() with lambda?
Select distinct protocols, loop on them and subselect only first object of the same protocol - thus you'll get the list you need.
I have an Entity Framework Entity that looks something like this:
class ListItemEtlObject
{
public int ID { get; set; }
public string ProjectName { get; set; }
public string ProjectType { get; set; }
public string ProjectCode { get; set; }
public string ProjectDescription { get; set; }
public string JobNo { get; set; }
public string JobDescription { get; set; }
public bool Include { get; set; }
}
I am pulling items from two different data sources into IEnumerable lists. How might I go about comparing the items without using a bunch of if statements to check if there are differences between the properties' values and then set the property's value if they do not match? The idea is to keep the lists synchronized. Also list A has an ID value set, list B does not. I just feel there is a better way to do this than a bunch of
if(objectA.ProjectName != objectB.ProjectName)
{
objectA.ProjectName = objectB.ProjectName;
}
If you have control of the source object then the best declarative way to support value based equality is to implement IEquatable<T>. This does unfortunately require you to enumerate out all of those checks but it's done once at the actual object definition location and doesn't need to be repeated throughout the code base.
class ListItemEtlObject : IEquatable<ListITemEtlObject>
{
...
public void Equals(ListITemEtlObject other) {
if (other == null) {
return false;
}
return
ID == other.ID &&
ProjectName == other.ProjectName &&
ProjectType == other.ProjectType &&
... ;
}
}
Additionally you could choose to overload the equality operator on the object type and allow consumers to simply use != and == on ListItemEtlObject instances and get value equality instead of reference equality.
public static bool operator==(ListItemEtlObject left, ListItemEtlObject right) {
return EqualityComparer<ListItemEtlObject>.Default.Equals(left, right);
}
public static bool operator!=(ListItemEtlObject left, ListItemEtlObject right) {
return !(left == right);
}
The easiest way would be to provide a method on your class that computes a specific hash, much like GetHashCode, and then if two instances compute the same hash, they can be said to be equivalent.
You could simplify it using reflection =)
public virtual void SetDifferences(MyBaseClass compareTo)
{
var differences = this.GetDifferentProperties(compareTo);
differences.ToList().ForEach(x =>
{
x.SetValue(this, x.GetValue(compareTo, null), null);
});
}
public virtual IEnumerable<PropertyInfo> GetDifferentProperties(MyBaseClass compareTo)
{
var signatureProperties = this.GetType().GetProperties();
return (from property in signatureProperties
let valueOfThisObject = property.GetValue(this, null)
let valueToCompareTo = property.GetValue(compareTo, null)
where valueOfThisObject != null || valueToCompareTo != null
where (valueOfThisObject == null ^ valueToCompareTo == null) || (!valueOfThisObject.Equals(valueToCompareTo))
select property);
}
And here are a couple of tests I did for you
[TestMethod]
public void CheckDifferences()
{
var f = new OverridingGetHashCode();
var g = new OverridingGetHashCode();
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include = true;
f.GetDifferentProperties(g).Should().NotBeNull().And.HaveCount(1).And.Contain(f.GetType().GetProperty("Include"));
g.Include = true;
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
g.JobDescription = "my job";
f.GetDifferentProperties(g).Should().NotBeNull().And.HaveCount(1).And.Contain(f.GetType().GetProperty("JobDescription"));
}
[TestMethod]
public void SetDifferences()
{
var f = new OverridingGetHashCode();
var g = new OverridingGetHashCode();
g.Include = true;
f.SetDifferences(g);
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include = true;
g.Include = false;
f.SetDifferences(g);
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include.Should().BeFalse();
}
I got the following piece of code:
public class Collect
{
public string name{ get; set; }
public int id { get; set; }
public DateTime registerDate { get; set; }
}
public class ControllingMyList
{
public void prepareList()
{
List<Collect> list = new List<Collect>();
list= loadList();
//Rest of the opperations
}
}
Considering that my loadList method returns for me many duplicated records (id variable) I want to get only one record by ID.
The Distinct() function seems to be a good solution but if I remember correctly, Distinct() filters all the members of the object so just because of a second of difference from "registerDate" variable is considered a criteria to make it distinct, even if its with the same ID.
var list= loadList();
list = list
.GroupBy(i => i.id)
.Select(g => g.First())
.ToList();
You have several options:
Use DistinctBy() extension method from the MoreLinq project
Use the Distinct() overload that accepts a custom equality comparer, and implement a custom comparer
Use Linq GroupBy( x=> x.id) and then take the first item of each group.
Pass in a comparer that uses the id:
public class idCompare : IEqualityComparer<Collect>
{
public bool Equals(Collect x, Collect y)
{
return Equals(x.id, y.id);
}
public int GetHashCode(Collect obj)
{
return obj.id.GetHashCode();
}
}
....
list.Distinct(new idCompare());
static List GetUniques(IEnumerable collection, string attribute) where T : Entity
{
Dictionary<string, bool> tempkvp = new Dictionary<string, bool>();
List<T> uniques = new List<T>();
List<string> columns = collection.FirstOrDefault().GetType().GetProperties().ToList().ConvertAll<string>(x => x.Name.ToLower());
var property = attribute != null && collection.Count() > 0 && columns.Contains(attribute.ToLower()) ? ViewModelHelpers.GetProperty(collection.FirstOrDefault(), attribute) : null;
if (property != null)
{
foreach (T obj in collection)
{
string value = property.GetValue(obj, null).ToString();
if (!(tempkvp.ContainsKey(value)))
{
tempkvp.Add(value, true);
uniques.Add(obj);
}
}
}
return uniques;
}
Implement IEquatable<T> and override Equals and GetHashCode. You can have those take into account only id.
using System;
public class Collect : IEquatable<Collect>
{
public string name{ get; set; }
public int id { get; set; }
public DateTime registerDate { get; set; }
public bool Equals(Collect other)
{
if(other == null)
{
return false;
}
return this.id == other.id;
}
}