C# ASP.Net MVC - Unit Test Assert Method Fail - c#

So a quick rundown about the program I am testing. The program is a small app that register athletes' track records from all kinds of track meet. Right now I am testing the functionality of the option to create a new meet "CreateMeet". Every meet have different type of events: track, javelin, etc... and it takes place at specific time and location.
I wrote a small test function that allows me to test the creation of a new meet into the database, and so far the test failed because according to the test results, the problem is in my Assert function. I was sure that it would work but I guess it didn't. The test failure error gives me this:
Test Failed - CreateMeet
Message: Assert.IsTrue failed.
Below is the test function:
public void CreateMeet()
{
var createMeet = new Meet()
{
MeetId = 1234,
MeetName = Spring Meet,
MeetDate = DateTime.Now,
MeetLocation = "Paris, France",
MeasurementType = "Metric"
};
var MeetController = new MeetsController();
MeetController.AddMeet(createMeet);
var testList = new List<Meet>(MeetController.RetrieveAllMeets());
Assert.IsTrue(testList.Contains(createMeet));
}
Which Assert or which type of function do you guys think I may need for this function to generate a successful test? I'm stumped on any other options. Your input is appreciated.
UPDATE
Additional code as per D-Shih's request.
public IList<Meet> RetrieveAllMeets()
{
JObject jsonMeetObj = JObject.Parse(FieldScribeAPIRequests.FieldScribeGETAsync(FieldScribeAPIRequests.FieldScribeAPIRootAddress + "meets"));
IList<JToken> results = jsonMeetObj["value"].Children().ToList();
IList<Meet> meets = new List<Meet>();
foreach (JToken item in results)
meets.Add(item.ToObject<Meet>());
return meets;
}

I believe you're comparing instances, when you want to compare values.
Briefly, the object 'createMeet' is not the same object as will be found in 'testList' - they are of the same class, and they hold the same data values, but they are not the same instance of an object.
To get it to do what you want, you could build a CompareTo(Meet) method on your DTO that compares all the field values, and returns a true/false that the objects are 'data equivalent'.
-- Edit: further info.
Consider the following class
public class DTO
{
public int Value;
public bool CompareTo(DTO Target)
{
return (Target.Value == Value);
}
}
Now, consider this code:
private void button1_Click(object sender, EventArgs e)
{
DTO a = new DTO();
DTO b = new DTO();
a.Value = 1;
b.Value = 1;
if (a == b)
MessageBox.Show("Should not happen");
if (a.CompareTo(b))
MessageBox.Show("Should happen");
}
Variables a and b are data equivalent; they mean the same thing. But they are not the same instance of an object. So, the first if statement fails, and you will not see "Should not happen", but the second if passes, and you will see "Should happen".
You could override the == operator like below, but I wouldn't recommend it:
public class DTO
{
public int Value;
public bool CompareTo(DTO Target)
{
return (Target.Value == Value);
}
public static bool operator ==(DTO a, DTO b)
{
return (a.Value == b.Value);
}
public static bool operator !=(DTO a, DTO b)
{
return (a.Value != b.Value);
}
}

From your question your expect might want to compare object's fields from Meet Class.
Contain method in List which will be to check object exist by Equals method.
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable.Equals method for T (the type of values in the list).
Meet is a reference type,When you used Contains method will compare object's pointer.
A Simple way to do override Equals(object obj) on Meet
public class Meet
{
public int MeetId { get; set; }
public string MeetName { get; set; }
public DateTime MeetDate { get; set; }
public string MeetLocation { get; set; }
public string MeasurementType { get; set; }
public override bool Equals(object obj)
{
Meet input = obj as Meet;
if (obj==null)
return false;
return MeetId == input.MeetId &&
MeetName == input.MeetName &&
MeetDate == input.MeetDate &&
MeetLocation == input.MeetLocation &&
MeasurementType == input.MeasurementType;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}

Since you are storing meet with With Id , instead of retrieving all meets and
checking if the collection contains the meet, write a method to get meet by meet id. Assert that it returns a meet and all properties that you are expecting do match. Take a look at fluent assertions which can make things simpler for you when it comes to assertions. http://fluentassertions.com/
Also, since you mentioned is as "Unit test" you should consider mocking to create a kind of in-memory collection and test against them instead of testing against Database.

Related

Value-equals and circular references: how to resolve infinite recursion?

I have some classes that contain several fields. I need to compare them by value, i.e. two instances of a class are equal if their fields contain the same data. I have overridden the GetHashCode and Equals methods for that.
It can happen that these classes contain circular references.
Example: We want to model institutions (like government, sports clubs, whatever). An institution has a name. A Club is an institution that has a name and a list of members. Each member is a Person that has a name and a favourite institution. If a member of a certain club has this club as his favourite institution, we have a circular reference.
But circular references, in conjunction with value equality, lead to infinite recursion. Here is a code example:
interface IInstitution { string Name { get; } }
class Club : IInstitution
{
public string Name { get; set; }
public HashSet<Person> Members { get; set; }
public override int GetHashCode() { return Name.GetHashCode() + Members.Count; }
public override bool Equals(object obj)
{
Club other = obj as Club;
if (other == null)
return false;
return Name.Equals(other.Name) && Members.SetEquals(other.Members);
}
}
class Person
{
public string Name { get; set; }
public IInstitution FavouriteInstitution { get; set; }
public override int GetHashCode() { return Name.GetHashCode(); }
public override bool Equals(object obj)
{
Person other = obj as Person;
if (other == null)
return false;
return Name.Equals(other.Name)
&& FavouriteInstitution.Equals(other.FavouriteInstitution);
}
}
class Program
{
public static void Main()
{
Club c1 = new Club { Name = "myClub", Members = new HashSet<Person>() };
Person p1 = new Person { Name = "Johnny", FavouriteInstitution = c1 }
c1.Members.Add(p1);
Club c2 = new Club { Name = "myClub", Members = new HashSet<Person>() };
Person p2 = new Person { Name = "Johnny", FavouriteInstitution = c2 }
c2.Members.Add(p2);
bool c1_and_c2_equal = c1.Equals(c2); // StackOverflowException!
// c1.Equals(c2) calls Members.SetEquals(other.Members)
// Members.SetEquals(other.Members) calls p1.Equals(p2)
// p1.Equals(p2) calls c1.Equals(c2)
}
}
c1_and_c2_equal should return true, and in fact we (humans) can see that they are value-equal with a little bit of thinking, without running into infinite recursion. However, I can't really say how we figure that out. But since it is possible, I hope that there is a way to resolve this problem in code as well!
So the question is: How can I check for value equality without running into infinite recursions?
Note that I need to resolve circular references in general, not only the case from above. I'll call it a 2-circle since c1 references p1, and p1 references c1. There can be other n-circles, e.g. if a club A has a member M whose favourite is club B which has member N whose favourite club is A. That would be a 4-circle. Other object models might also allow n-circles with odd numbers n. I am looking for a way to resolve all these problems at once, since I won't know in advance which value n can have.
An easy workaround (used in RDBMS) is to use a unique Id to identify a Person(any type). Then you don't need to compare every other property and you never run into such cuircular references.
Another way is to compare differently in Equals, so provide the deep check only for the type of the Equals and not for the referenced types. You could use a custom comparer:
public class PersonNameComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
if(object.ReferenceEquals(x, y)) return true;
return x.Name == y.Name;
}
public int GetHashCode(Person obj)
{
return obj?.Name?.GetHashCode() ?? int.MinValue;
}
}
Now you can change the Equals implementation of Club to avoid that the Members(Persons) will use their deep check which includes the institution but only their Name:
public override bool Equals(object obj)
{
if (Object.ReferenceEquals(this, obj))
return true;
Club other = obj as Club;
if (other == null)
return false;
var personNameComparer = new PersonNameComparer();
return Name.Equals(other.Name)
&& Members.Count == other.Members.Count
&& !Members.Except(other.Members, personNameComparer).Any();
}
You notice that i can't use SetEquals because there is no overload for my custom comparer.
Following the suggestion of Dryadwoods, I changed the Equals methods so that I can keep track of the items that were already compared.
First we need an equality comparer that checks reference equality for corresponding elements of pairs:
public class ValuePairRefEqualityComparer<T> : IEqualityComparer<(T,T)> where T : class
{
public static ValuePairRefEqualityComparer<T> Instance
= new ValuePairRefEqualityComparer<T>();
private ValuePairRefEqualityComparer() { }
public bool Equals((T,T) x, (T,T) y)
{
return ReferenceEquals(x.Item1, y.Item1)
&& ReferenceEquals(x.Item2, y.Item2);
}
public int GetHashCode((T,T) obj)
{
return RuntimeHelpers.GetHashCode(obj.Item1)
+ 2 * RuntimeHelpers.GetHashCode(obj.Item2);
}
}
And here is the modified Equals method of Club:
static HashSet<(Club,Club)> checkedPairs
= new HashSet<(Club,Club)>(ValuePairRefEqualityComparer<Club>.Instance);
public override bool Equals(object obj)
{
Club other = obj as Club;
if (other == null)
return false;
if (!Name.Equals(other.Name))
return;
if (checkedPairs.Contains((this,other)) || checkedPairs.Contains((other,this)))
return true;
checkedPairs.Add((this,other));
bool membersEqual = Members.SetEquals(other.Members);
checkedPairs.Clear();
return membersEqual;
}
The version for Person is analogous. Note that I add (this,other) to checkedPairs and check if either (this,other) or (other,this) is contained because it might happen that after the first call of c1.Equals(c2), we end up with a call of c2.Equals(c1) instead of c1.Equals(c2). I am not sure if this actually happens, but since I can't see the implementation of SetEquals, I believe it is a possibility.
Since I am not happy with using a static field for the already checked pairs (it will not work if the program is concurrent!), I asked another question: make a variable last for a call stack.
For the general case that I am interested in
-- where we have classes C1, ..., Cn where each of these classes can have any number of VALUES (like int, string, ...) as well as any number of REFERENCES to any other classes of C1, ..., Cn (e.g. by having for each type Ci a field ICollection<Ci>) --
the question "Are two objects A and B equal?", in the sense of equality that I described here,
seems to be EQUIVALENT to
the question "For two finite, directed, connected, colored graphs G and H, does there exist an isomorphism from G to H?".
Here is the equivalence:
graph vertices correspond to objects (class instances)
graph edges correspond to references to objects
color corresponds to the conglomerate of values and the type itself (i.e. colors of two vertices are the same if their corresponding objects have the same type and the same values)
That's an NP-hard question, so I think I'm going to discard my plan to implement this and go with a circular-reference-free approach instead.

Generate Multi-Parameter LINQ Search Queries with Run-time Specified Return Type

Having spent a long time solving this problem, I wanted to share the solution.
Background
I maintain a large web application with the primary function of managing orders. It is an MVC over C# application using EF6 for data.
There are LOTS of search screens. The search screens all have multiple parameters and return different object types.
The Problem
Every search screen had:
A ViewModel with the search parameters
A Controller method to handle the Search event
A method to pull the correct data for that screen
A method to apply all the search filters to the dataset
A method to convert the results into a NEW results ViewModel
The Results ViewModel
This adds up quickly. We have about 14 different search screens, which means about 84 models & methods to handle these searches.
My Goal
I wanted to be able to create a class, analogous to the current search parameter ViewModel, that would inherit from a base SearchQuery class such that my Controller could simply trigger the search to run to populate a Results field of the same object.
An Example of My Ideal State (Because It's a Bear To Explain)
Take the following class structure:
public class Order
{
public int TxNumber;
public Customer OrderCustomer;
public DateTime TxDate;
}
public class Customer
{
public string Name;
public Address CustomerAddress;
}
public class Address
{
public int StreetNumber;
public string StreetName;
public int ZipCode;
}
Let's assume I have lots of those records in a queryable format--an EF DBContext object, an XML object, whatever--and I want to search them. First, I create a derived class specific to my ResultType(in this case, Order).
public class OrderSearchFilter : SearchQuery
{
//this type specifies that I want my query result to be List<Order>
public OrderSearchFilter() : base(typeof(Order)) { }
[LinkedField("TxDate")]
[Comparison(ExpressionType.GreaterThanOrEqual)]
public DateTime? TransactionDateFrom { get; set; }
[LinkedField("TxDate")]
[Comparison(ExpressionType.LessThanOrEqual)]
public DateTime? TransactionDateTo { get; set; }
[LinkedField("")]
[Comparison(ExpressionType.Equal)]
public int? TxNumber { get; set; }
[LinkedField("Order.OrderCustomer.Name")]
[Comparison(ExpressionType.Equal)]
public string CustomerName { get; set; }
[LinkedField("Order.OrderCustomer.CustomerAddress.ZipCode")]
[Comparison(ExpressionType.Equal)]
public int? CustomerZip { get; set; }
}
I use attributes to specify what field/property of the target ResultType any given search field is linked to, as well as the comparison type (== < > <= >= !=). A blank LinkedField means that the name of the search field is the same as the name of the target object field.
With this configured, the only things I should need for a given search are:
A populated search object like the one above
A data source
No other scenario-specific coding should be required!
The Solution
For starters, we create:
public abstract class SearchQuery
{
public Type ResultType { get; set; }
public SearchQuery(Type searchResultType)
{
ResultType = searchResultType;
}
}
We'll also create the attributes we used above to define the search field:
protected class Comparison : Attribute
{
public ExpressionType Type;
public Comparison(ExpressionType type)
{
Type = type;
}
}
protected class LinkedField : Attribute
{
public string TargetField;
public LinkedField(string target)
{
TargetField = target;
}
}
For each search field, we'll need to know not only WHAT search is done, but also WHETHER the search is done. For example, if the value of "TxNumber" is null, we wouldn't want to run that search. So we create a SearchField object that contains, in addition to the actual search value, two expressions: one that represents performing the search, and one that validates whether the search should be applied.
private class SearchFilter<T>
{
public Expression<Func<object, bool>> ApplySearchCondition { get; set; }
public Expression<Func<T, bool>> SearchExpression { get; set; }
public object SearchValue { get; set; }
public IQueryable<T> Apply(IQueryable<T> query)
{
//if the search value meets the criteria (e.g. is not null), apply it; otherwise, just return the original query.
bool valid = ApplySearchCondition.Compile().Invoke(SearchValue);
return valid ? query.Where(SearchExpression) : query;
}
}
Once we have created all our filters, all we need to do is loop through them and call the "Apply" method on our dataset! Easy!
The next step is creating the validation expressions. We'll do this based on the Type; every int? is validated the same as every other int?.
private static Expression<Func<object, bool>> GetValidationExpression(Type type)
{
//throw exception for non-nullable types (strings are nullable, but is a reference type and thus has to be called out separately)
if (type != typeof(string) && !(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)))
throw new Exception("Non-nullable types not supported.");
//strings can't be blank, numbers can't be 0, and dates can't be minvalue
if (type == typeof(string )) return t => !string.IsNullOrWhiteSpace((string)t);
if (type == typeof(int? )) return t => t != null && (int)t >= 0;
if (type == typeof(decimal? )) return t => t != null && (decimal)t >= decimal.Zero;
if (type == typeof(DateTime?)) return t => t != null && (DateTime?)t != DateTime.MinValue;
//everything else just can't be null
return t => t != null;
}
This was all I needed for my application, but there is definitely more validation that could be done.
The search expression is slightly more complicated and required a parser to "De-qualify" Field/Property names (there's probably a better word, but if so, I don't know it). Basically, if I specified "Order.Customer.Name" as a linked field and I'm searching through Orders, I need to turn that into "Customer.Name" because there is no Order Field inside an Order object. Or at least I hope not. :) This isn't certain, but I considered it better to accept and correct fully-qualified object names than to support that edge case.
public static List<string> DeQualifyFieldName(string targetField, Type targetType)
{
var r = targetField.Split('.').ToList();
foreach (var p in targetType.Name.Split('.'))
if (r.First() == p) r.RemoveAt(0);
return r;
}
This is just straight text parsing, and returns the Field name in "levels" (e.g. "Customer"|"Name").
All right, let's get our search expression together.
private Expression<Func<T, bool>> GetSearchExpression<T>(
string targetField, ExpressionType comparison, object value)
{
//get the property or field of the target object (ResultType)
//which will contain the value to be checked
var param = Expression.Parameter(ResultType, "t");
Expression left = null;
foreach (var part in DeQualifyFieldName(targetField, ResultType))
left = Expression.PropertyOrField(left == null ? param : left, part);
//Get the value against which the property/field will be compared
var right = Expression.Constant(value);
//join the expressions with the specified operator
var binaryExpression = Expression.MakeBinary(comparison, left, right);
return Expression.Lambda<Func<T, bool>>(binaryExpression, param);
}
Not so bad! What we're trying to create is, for example:
t => t.Customer.Name == "Searched Name"
Where t is our ReturnType--an Order, in this case. First we create the parameter, t. Then, we loop through the parts of the property/field name until we have the full title of the object we're targeting (naming it "left" because it's the left side of our comparison). The "right" side of our comparison is simple: the constant provided by the user.
Then we create the binary expression and turn it into a lambda. Easy as falling off a log! If falling off a log required countless hours of frustration and failed methodologies, anyway. But I digress.
We've got all the pieces now; all we need is a method to assemble our query:
protected IQueryable<T> ApplyFilters<T>(IQueryable<T> data)
{
if (data == null) return null;
IQueryable<T> retVal = data.AsQueryable();
//get all the fields and properties that have search attributes specified
var fields = GetType().GetFields().Cast<MemberInfo>()
.Concat(GetType().GetProperties())
.Where(f => f.GetCustomAttribute(typeof(LinkedField)) != null)
.Where(f => f.GetCustomAttribute(typeof(Comparison)) != null);
//loop through them and generate expressions for validation and searching
try
{
foreach (var f in fields)
{
var value = f.MemberType == MemberTypes.Property ? ((PropertyInfo)f).GetValue(this) : ((FieldInfo)f).GetValue(this);
if (value == null) continue;
Type t = f.MemberType == MemberTypes.Property ? ((PropertyInfo)f).PropertyType : ((FieldInfo)f).FieldType;
retVal = new SearchFilter<T>
{
SearchValue = value,
ApplySearchCondition = GetValidationExpression(t),
SearchExpression = GetSearchExpression<T>(GetTargetField(f), ((Comparison)f.GetCustomAttribute(typeof(Comparison))).Type, value)
}.Apply(retVal); //once the expressions are generated, go ahead and (try to) apply it
}
}
catch (Exception ex) { throw (ErrorInfo = ex); }
return retVal;
}
Basically, we just grab a list of fields/properties in the derived class (that are linked), create a SearchFilter object from them, and apply them.
Clean-Up
There's a bit more, of course. For example, we're specifying object links with strings. What if there's a typo?
In my case, I have the class check whenever it spins up an instance of a derived class, like this:
private bool ValidateLinkedField(string fieldName)
{
//loop through the "levels" (e.g. Order / Customer / Name) validating that the fields/properties all exist
Type currentType = ResultType;
foreach (string currentLevel in DeQualifyFieldName(fieldName, ResultType))
{
MemberInfo match = (MemberInfo)currentType.GetField(currentLevel) ?? currentType.GetProperty(currentLevel);
if (match == null) return false;
currentType = match.MemberType == MemberTypes.Property ? ((PropertyInfo)match).PropertyType
: ((FieldInfo)match).FieldType;
}
return true; //if we checked all levels and found matches, exit
}
The rest is all implementation minutia. If you're interested in checking it out, a project that includes a full implementation, including test data, is here. It's a VS 2015 project, but if that's an issue, just grab the Program.cs and Search.cs files and throw them into a new project in your IDE of choice.
Thanks to everyone on StackOverflow who asked the questions and wrote the answers that helped me put this together!

Can IEquatable compare custom objects having a list property of other custom objects?

I'd like to compare two custom class objects of the same type. The custom class being compared has a List property which is filled with items of another custom type. Is this possible by inheriting IEquatable?
I couldn't figure out how to make this work by modifying MSDN's code to compare class objects containing List properties of a custom type.
I did successfully derive from the EqualityComparer class to make a separate comparison class (code below), but I'd like to implement the comparison ability in the actual classes being compared. Here's what I have so far:
EDIT: This doesn't work after all. My apologies - I've been working on this awhile and I may have pasted incorrect example code. I'm working on trying to find my working solution...
class Program
{
static void Main(string[] args)
{
// Test the ContractComparer.
Contract a = new Contract("Contract X", new List<Commission>() { new Commission(1), new Commission(2), new Commission(3) });
Contract b = new Contract("Contract X", new List<Commission>() { new Commission(1), new Commission(2), new Commission(3) });
ContractComparer comparer = new ContractComparer();
Console.WriteLine(comparer.Equals(a, b));
// Output returns True. I can't get this to return
// True when I inherit IEquatable in my custom classes
// if I include the list property ("Commissions") in my
// comparison.
Console.ReadLine();
}
}
public class Contract
{
public string Name { get; set; }
public List<Commission> Commissions { get; set; }
public Contract(string name, List<Commission> commissions)
{
this.Name = name;
this.Commissions = commissions;
}
}
public class Commission
{
public int ID;
public Commission(int id)
{
this.ID = id;
}
}
public class ContractComparer : IEqualityComparer<Contract>
{
public bool Equals(Contract a, Contract b)
{
//Check whether the objects are the same object.
if (Object.ReferenceEquals(a, b)) return true;
//Check whether the contracts' properties are equal.
return a != null && b != null && a.Name.Equals(b.Name) && a.Commissions.Equals(b.Commissions);
}
public int GetHashCode(Contract obj)
{
int hashName = obj.Name.GetHashCode();
int hashCommissions = obj.Commissions.GetHashCode();
return hashName ^ hashCommissions;
}
}
You have to implement some kind of comparer for Commission, e.g. by implementing Commission : IEquatable<Commission>, then use it:
... && a.Commissions.SequenceEqual(b.Commissions)

Is there a way of comparing all the values within 2 entities?

I'm using EF4.3 so I'm referring to entities, however it could apply to any class containing properties.
I'm trying to figure out if its possible to compare 2 entities. Each entity has properties that are assigned values for clarity let say the entity is 'Customer'.
public partial class Customer
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
The customer visits my website and types in some details 'TypedCustomer'. I check this against the database and if some of the data matches, I return a record from the database 'StoredCustomer'.
So at this point I've identified that its the same customer returning but I wan't to valid the rest of the data. I could check each property one by one, but there are a fair few to check. Is it possible to make this comparison at a higher level which takes into account the current values of each?
if(TypedCustomer == StoredCustomer)
{
.... do something
}
If you're storing these things in the database, it is logical to assume you'd also have a primary key called something like Id.
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
Then all you do is:
if(TypedCustomer.Id == StoredCustomer.Id)
{
}
UPDATE:
In my project, I have a comparer for these circumstances:
public sealed class POCOComparer<TPOCO> : IEqualityComparer<TPOCO> where TPOCO : class
{
public bool Equals(TPOCO poco1, TPOCO poco2)
{
if (poco1 != null && poco2 != null)
{
bool areSame = true;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object v1 = property.GetValue(poco1, null);
object v2 = property.GetValue(poco2, null);
if (!object.Equals(v1, v2))
{
areSame = false;
break;
}
});
return areSame;
}
return poco1 == poco2;
} // eo Equals
public int GetHashCode(TPOCO poco)
{
int hash = 0;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object val = property.GetValue(poco, null);
hash += (val == null ? 0 : val.GetHashCode());
});
return hash;
} // eo GetHashCode
} // eo class POCOComparer
Uses an extension method:
public static partial class TypeExtensionMethods
{
public static PropertyInfo[] GetPublicProperties(this Type self)
{
self.ThrowIfDefault("self");
return self.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where((property) => property.GetIndexParameters().Length == 0 && property.CanRead && property.CanWrite).ToArray();
} // eo GetPublicProperties
} // eo class TypeExtensionMethods
Most simple seems to use reflexion : get the properties and/or fields you want to compare, and loop through them to compare your two objects.
This will be done with getType(Customer).getProperties and getType(Customer).getFields, then using getValue on each field/property and comparing.
You might want to add custom informations to your fields/properties to define the ones that needs
comparing. This could be done by defining a AttributeUsageAttribute, that would inherit from FlagsAttribute for instance. You'll then have to retrieve and handle those attributes in your isEqualTo method.
I don't think there's much of a purpose to checking the entire object in this scenario - they'd have to type every property in perfectly exactly as they did before, and a simple "do they match" doesn't really tell you a lot. But assuming that's what you want, I can see a few ways of doing this:
1) Just bite the bullet and compare each field. You can do this by overriding the bool Equals method, or IEquatable<T>.Equals, or just with a custom method.
2) Reflection, looping through the properties - simple if your properties are simple data fields, but more complex if you've got complex types to worry about.
foreach (var prop in typeof(Customer).GetProperties()) {
// needs better property and value validation
bool propertyMatches = prop.GetValue(cust1, null)
.Equals(prop.GetValue(cust2, null));
}
3) Serialization - serialize both objects to XML or JSON, and compare the strings.
// JSON.NET
string s1 = JsonConvert.SerializeObject(cust1);
string s2 = JsonConvert.SerializeObject(cust2);
bool match = s1 == s2;

C# Select clause returns system exception instead of relevant object

I am trying to use the select clause to pick out an object which matches a specified name field from a database query as follows:
objectQuery = from obj in objectList
where obj.Equals(objectName)
select obj;
In the results view of my query, I get:
base {System.SystemException} = {"Boolean Equals(System.Object)"}
Where I should be expecting something like a Car, Make, or Model
Would someone please explain what I am doing wrong here?
The method in question can be seen here:
// this function searches the database's table for a single object that matches the 'Name' property with 'objectName'
public static T Read<T>(string objectName) where T : IEquatable<T>
{
using (ISession session = NHibernateHelper.OpenSession())
{
IQueryable<T> objectList = session.Query<T>(); // pull (query) all the objects from the table in the database
int count = objectList.Count(); // return the number of objects in the table
// alternative: int count = makeList.Count<T>();
IQueryable<T> objectQuery = null; // create a reference for our queryable list of objects
T foundObject = default(T); // create an object reference for our found object
if (count > 0)
{
// give me all objects that have a name that matches 'objectName' and store them in 'objectQuery'
objectQuery = from obj in objectList
where obj.Equals(objectName)
select obj;
// make sure that 'objectQuery' has only one object in it
try
{
foundObject = (T)objectQuery.Single();
}
catch
{
return default(T);
}
// output some information to the console (output screen)
Console.WriteLine("Read Make: " + foundObject.ToString());
}
// pass the reference of the found object on to whoever asked for it
return foundObject;
}
}
Note that I am using the interface "IQuatable<T>" in my method descriptor.
An example of the classes I am trying to pull from the database is:
public class Make: IEquatable<Make>
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Model> Models { get; set; }
public Make()
{
// this public no-argument constructor is required for NHibernate
}
public Make(string makeName)
{
this.Name = makeName;
}
public override string ToString()
{
return Name;
}
// Implementation of IEquatable<T> interface
public virtual bool Equals(Make make)
{
if (this.Id == make.Id)
{
return true;
}
else
{
return false;
}
}
// Implementation of IEquatable<T> interface
public virtual bool Equals(String name)
{
if (this.Name.Equals(name))
{
return true;
}
else
{
return false;
}
}
}
And the interface is described simply as:
public interface IEquatable<T>
{
bool Equals(T obj);
}
IQueryable<T> executes your query against the backing data store (in this case, it's SQL RDBMS). Your SQL RDBMS has no idea of IEquatable<T>*, and cannot use its implementation: the query function must be translatable to SQL, and obj.Equals(objectName) is not translatable.
You can convert IQueryable<T> to IEnumerable<T> and do the query in memory, but that would be too inefficient. You should change the signature to take a Expression<Func<TSource, bool>> predicate, and pass the name checker to it:
public static T Read<T>(Expression<Func<T,bool>> pred) {
using (ISession session = NHibernateHelper.OpenSession()) {
return session.Query<T>().SingleOrdefault(pred);
}
}
You can now use this method as follows:
Make snake = Read<Make>(x => x.Name == "snake");
* Additionally, your IEquatable<T> is not used in the method that you are showing: the Equals(string) method would qualify as an implementation of IEquatable<string>, but it is not mentioned in the list of interfaces implemented by your Make class.
One of your calls to the Equal override could be throwing an exception.
For debugging purposes, try iterating rather than using Linq and you should find out which one:
foreach(object obj in objectList)
{
bool check = obj.Equals(objectName) // set a breakpoint here
}
As far as what is causing the exception, ensure that each of your object Name and Id properties have values.

Categories