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();
}
Related
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.
I red a few articles on internet but all value to me, I couldn't understand how can I avoid adding a duplicate object to a list, I tried something like this.
I actually have created a class which overrides GetHashCode and Equal method.
Now I want to form a collection of non duplicate object list.
public class FlightInfo
{
public string Origin { get; set; }
public string DepartureTime { get; set; }
public string Destination { get; set; }
public string DestinationTime { get; set; }
public string Price { get; set; }
public override bool Equals(object obj)
{
var other = obj as FlightInfo;
if (other == null)
return false;
if (Origin != other.Origin || DepartureTime != other.DepartureTime || Destination != other.Destination
|| DestinationTime != other.DestinationTime || Price != other.Price)
return false;
return true;
}
public override int GetHashCode()
{
int hashOrigin = Origin.GetHashCode();
int hashDestination = Destination.GetHashCode();
int hashDepartureTime = DepartureTime.GetHashCode();
int hashDestinationTime = DestinationTime.GetHashCode();
int hashPrice = Price.GetHashCode();
return hashOrigin ^ hashDestination ^ hashDepartureTime ^ hashDestinationTime ^ hashPrice;
}
}
I also tried one article by Eric
https://blogs.msdn.microsoft.com/ericlippert/2011/02/28/guidelines-and-rules-for-gethashcode/
but this article has
private List<T>[] buckets = new List<T>[100];
insead of private List<T>() buckets = new List<T>()
but I want to return a list with no fix size.
Since you already implemented the Equals and GetHashCode methods you can have your own custom list of FlightInfo that will make use of those methods:
public class FlightInfoList : IList<FlightInfo>
{
private readonly List<FlightInfo> _flightInfos = new List<FlightInfo>();
public IEnumerator<FlightInfo> GetEnumerator()
{
return _flightInfos.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(FlightInfo item)
{
if (_flightInfos.Any(flightInfo => flightInfo.Equals(item)))
{
throw new Exception("Cannot add duplicated values!");
}
_flightInfos.Add(item);
}
public void Clear()
{
_flightInfos.Clear();
}
public bool Contains(FlightInfo item)
{
return _flightInfos.Contains(item);
}
public void CopyTo(FlightInfo[] array, int arrayIndex)
{
_flightInfos.CopyTo(array, arrayIndex);
}
public bool Remove(FlightInfo item)
{
return _flightInfos.Remove(item);
}
public int Count => _flightInfos.Count;
public bool IsReadOnly => false;
public int IndexOf(FlightInfo item)
{
return _flightInfos.IndexOf(item);
}
public void Insert(int index, FlightInfo item)
{
_flightInfos.Insert(index, item);
}
public void RemoveAt(int index)
{
_flightInfos.RemoveAt(index);
}
public FlightInfo this[int index]
{
get => _flightInfos[index];
set => _flightInfos[index] = value;
}
}
Notice that in the Add method I'm checking if there's a duplicated. Another way to solve this is to use a dictionary.
I am mapping my class ProcessingCode in fluent-nhibernate.
It has a property Code which is a ValueObject.
How can I map it as a primary key?
Here is how I am mapping it not as a primary key:
public class ProcessingCodeMap : ClassMap<ProcessingCode>
{
public ProcessingCodeMap()
{
Component(x => x.Code, p => p.Map(x => x.Value).Not.Nullable());
... other properties
}
}
Here are the classes that are relevant for the mapping:
public class ProcessingCode
{
public virtual Code Code { get; set; }
//Other properties...
}
public class Code : IEquatable<Code>
{
public Code()
{
}
public Code(string value)
{
Value = value;
}
public string Value { get; set; }
//Some other methods
public static implicit operator string(Code code)
{
if (code == null)
return null;
return code.Value;
}
public static implicit operator Code(string value)
{
return new Code(value);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Code)obj);
}
public bool Equals(Code other)
{
return string.Equals(Value, other.Value);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
Component method is used to map a value object as a regular property of the a class. There are two methods to map IDs: Id to map a regular types and CompositeId to map components. So, the answer is to use CompositeId instead of Component for an "easy" solution:
public class ProcessingCodeMap : ClassMap<ProcessingCode>
{
public ProcessingCodeMap()
{
CompositeId(x => x.Code, p => p.KeyProperty(x => x.Value));
//... other properties
}
}
Or alternatively you can implement custom IUserType.
I have trouble deleting the duplicate references in my list.
I have this list
List<SaveMongo> toReturn
with my class SaveMongo that looks like this
public class SaveMongo
{
public ObjectId _id { get; set; }
public DateTime date { get; set; }
public Guid ClientId { get; set; }
public List<TypeOfSave> ListType = new List<TypeOfSave>();
public List<ObjectId> ListObjSave = new List<ObjectId>();
public SaveMongo()
{ }
}
Whenever I want to add an element to my list I use the following code
public static fctName(BsonDocument doc)
{
toReturn.Add(AddingSaveMongo(doc.GetValue("_id")));
}
public static SaveMongo AddingSaveMongo(BsonValue ObjValue)
{
foreach (SaveMongo doc in SpeCollection.FindAll())
{
foreach (var id in doc.ListObjSave)
{
if (id == ObjValue)
return (doc);
}
}
return (null);
}
However, I sometimes get duplicates references. I tried using this
toReturn = toReturn.Distinct().ToList();
to delete them. Without success.
I also tried to do this
if (!toReturn.Contains(AddingSaveMongo(doc.GetValue("_id"))))
toReturn.Add(AddingSaveMongo(doc.GetValue("_id")));
Still without success. But whenever I print the references in my list I get those result
What I am missing here so that I still have duplicates references in my List ?
Currently, Distinct is matching your objects using object.Equals, which is doing reference equality. One way to tell it to match objects based on other criteria is by implementing IEquatable<SaveMongo>. This example compares objects based on their Id:
public class SaveMongo : IEquatable<SaveMongo>
{
public ObjectId _id { get; set; }
public DateTime date { get; set; }
public Guid ClientId { get; set; }
public List<TypeOfSave> ListType = new List<TypeOfSave>();
public List<ObjectId> ListObjSave = new List<ObjectId>();
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((SaveMongo) obj);
}
public override int GetHashCode()
{
return _id.GetHashCode();
}
public bool Equals(SaveMongo other)
{
return _id.Equals(other._id);
}
}
Use grouping:
toReturn = (from e in toReturn
group e by e._id into g
select g.First()).ToList();
Also, you can group by two (or more) fields:
toReturn = (from e in toReturn
// group by ID and Date component
group e by new { e._id, e.date.Date } into g
select g.First()).ToList();
What would be the best (most elegant or performing) way of overloading the equality operator on a class containing only string attributes?
Example:
class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
}
I know how to overload the operator itself, however, I am wondering about the following points:
Is there a way to elegantly compare such two objects (e.g. without having to write an if statement containing mutual comparisons of all the attributes
What would be a good implementation of the GetHashCode() method in such case
How about something like this, Just create array of all properties and a loop.
internal class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
private string[] AllProperties//Array of all properties
{
get
{
return new[]
{
FirstAttribute,
SecondAttribute,
ThirdAttribute,
FourthAttribute,
FifthAttribute
};
}
}
protected bool Equals(MagicClass other)
{
var thisProps = this.AllProperties;
var otherProps = other.AllProperties;
return thisProps.SequenceEqual(otherProps);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MagicClass) obj);
}
public override int GetHashCode()
{
unchecked
{
var thisProps = this.AllProperties;
int hashCode = 0;
foreach (var prop in thisProps)
{
hashCode = (hashCode * 397) ^ (prop != null ? prop.GetHashCode() : 0);
}
return hashCode;
}
}
}
Then you can call Equals method inside your operator overload. If you're lazy to create AllProperties array you can use Reflection but IMO reflection is overkill here.
Not saying this is the 'best' or the most elegant solution, but I'd have the tendency to use an array and an index initializer, using an enumeration, so I could reuse get and set logic and in this case reset a hash code for a quick first comparison.
The advantage of the enumeration is, that you don't have to recheck your compare logic when an attribute is added, and you can prevent the overhead of resorting to reflection.
class MagicClass
{
string[] Values = new string[Enum.GetValues(typeof(MagicClassValues)).Length];
public string this[MagicClassValues Value] //and/or a GetValue/SetValue construction
{
get
{
return Values[(int)Value];
}
set
{
Values[(int)Value] = value;
hash = null;
}
}
int? hash; //buffered for optimal dictionary performance and == comparisson
public override int GetHashCode()
{
if (hash == null)
unchecked
{
hash = Values.Sum(s => s.GetHashCode());
}
return hash.Value;
}
public static bool operator ==(MagicClass v1, MagicClass v2) //used == operator, in compliance to the question, but this would be better for 'Equals'
{
if(ReferenceEquals(v1,v2))return true;
if(ReferenceEquals(v1,null) || ReferenceEquals(v2,null) || v1.GetHashCode() != v2.GetHashCode())return false;
return v1.Values.SequenceEqual(v2.Values);
}
public static bool operator !=(MagicClass v1, MagicClass v2)
{
return !(v1 == v2);
}
//optional, use hard named properties as well
public string FirstAttribute { get { return this[MagicClassValues.FirstAttribute]; } set { this[MagicClassValues.FirstAttribute] = value; } }
}
public enum MagicClassValues
{
FirstAttribute,
SecondAttribute,
//etc
}