Implement IEquatable for POCO - entity-framework-4

I noticed that EF's DbSet.Add() is quite slow. A little googling turned up a SO answer that promises up to 180x performance gains:
https://stackoverflow.com/a/7052504/141172
However, I do not understand exactly how to implement IEquatable<T> as suggested in the answer.
According to MSDN, if I implement IEquatable<T>, I should also override Equals() and GetHashCode().
As with many POCO's, my objects are mutable. Before being committed to the database (SaveChanges()), new objects have an Id of 0. After the objects have been saved, the Id serves as an ideal basis for implementing IEquatable, Equals() and GetHashCode().
It is unwise to include any mutable property in a hash code, and since according to MSDN
If two objects compare as equal, the GetHashCode method for each
object must return the same value
Should I implement IEquatable<T> as a property-by-property comparison (e.g. this.FirstName == other.FirstName) and not override Equals() and GetHashCode()?
Given that my POCO's are used in an EntityFramework context, should any special attention be paid to the Id field?

I came across your question in search for a solution to the same question. Here is a solution that I am trying out, see if it meets your needs:
First, all my POCOs derive from this abstract class:
public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
private readonly Guid _guid = Guid.NewGuid();
#region IEquatable<T> Members
public abstract bool Equals(T other);
#endregion
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof (T))
{
return false;
}
return Equals((T)obj);
}
public override int GetHashCode()
{
return _guid.GetHashCode();
}
}
I created a readonly Guid field that I am using in the GetHashCode() override. This will ensure that were I to put the derived POCO into a Dictionary or something else that uses the hash, I would not orphan it if I called a .SaveChanges() in the interim and the ID field was updated by the base class This is the one part I'm not sure is completely correct, or if it is any better than just Base.GetHashCode()?. I abstracted the Equals(T other) method to ensure the implementing classes had to implement it in some meaningful way, most likely with the ID field. I put the Equals(object obj) override in this base class because it would probably be the same for all the derived classes too.
This would be an implementation of the abstract class:
public class Species : BasePOCO<Species>
{
public int ID { get; set; }
public string LegacyCode { get; set; }
public string Name { get; set; }
public override bool Equals(Species other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ID != 0 &&
ID == other.ID &&
LegacyCode == other.LegacyCode &&
Name == other.Name;
}
}
The ID property is set as the primary key in the Database and EF knows that. ID is 0 on a newly created objects, then gets set to a unique positive integer on .SaveChanges(). So in the overridden Equals(Species other) method, null objects are obviously not equal, same references obviously are, then we only need to check if the ID == 0. If it is, we will say that two objects of the same type that both have IDs of 0 are not equal. Otherwise, we will say they are equal if their properties are all the same.
I think this covers all the relevant situations, but please chime in if I am incorrect. Hope this helps.
=== Edit 1
I was thinking my GetHashCode() wasn't right, and I looked at this https://stackoverflow.com/a/371348/213169 answer regarding the subject. The implementation above would violate the constraint that objects returning Equals() == true must have the same hashcode.
Here is my second stab at it:
public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
#region IEquatable<T> Members
public abstract bool Equals(T other);
#endregion
public abstract override bool Equals(object obj);
public abstract override int GetHashCode();
}
And the implementation:
public class Species : BasePOCO<Species>
{
public int ID { get; set; }
public string LegacyCode { get; set; }
public string Name { get; set; }
public override bool Equals(Species other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ID != 0 &&
ID == other.ID &&
LegacyCode == other.LegacyCode &&
Name == other.Name;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return Equals(obj as Species);
}
public override int GetHashCode()
{
unchecked
{
return ((LegacyCode != null ? LegacyCode.GetHashCode() : 0) * 397) ^
(Name != null ? Name.GetHashCode() : 0);
}
}
public static bool operator ==(Species left, Species right)
{
return Equals(left, right);
}
public static bool operator !=(Species left, Species right)
{
return !Equals(left, right);
}
}
So I got rid of the Guid in the base class and moved GetHashCode to the implementation. I used Resharper's implementation of GetHashCode with all the properties except ID, since ID could change (don't want orphans). This will meet the constraint on equality in the linked answer above.

As with many POCO's, my objects are mutable
But tehy should NOT be mutable on the fields that are the primary key. Per defintiion, or you are in a world of pain database wise anyway later.
Generate the HashCode ONLY on the fields of the primay key.
Equals() must return true IFF the participating objects have the same hash code
BZZZ - Error.
Hashcodes are double. It is possible for 2 objects to have different values and the smae hashcode. A hsahsode is an int (32bit). A string can be 2gb long. You can not mapp every possible string to a separate hashcode.
IF two objects have the same hashcode, they may be diferent. If two objects are the same, they can NOT have different hashcodes.
Where do you get the idea that Equals must return true for objects with the same hashcode?
Also, PCO or not, an object mapped to a database and used in a relation MUST have a stable primary key (which can be used to run the hashcode calculation). An object not having this STIL lshould have primary key (per SQL Server requirements), using a sequence / artificial primary key works here. Again, use that to run the HashCode calculation.

First thing first: Sorry my lame English :)
As TomTom say, they shouldn't be mutable just because they still not received PK/Id...
In our EF:CF system, we use generated negative id (assigned in base class ctor or, if you use ProxyTracking, in ObjectMaterialized event) for every new POCO. Its pretty simple idea:
public static class IdKeeper
{
private static int m_Current = int.MinValue;
private static Next()
{
return ++m_Current;
}
}
MinValue and incremen should be important, because EF will sort POCOs by their PK before committing changes to db and when you use "-1, -2, -3", POCOs are saved flipped, which in some cases (not according to what sort) may not be ideal.
public abstract class IdBase
{
public virtual int Id { get; set; }
protected IdBase()
{
Id = IdKeeper.Next();
}
}
If POCO is materialized from DB, his Id will be override with actual PK as well as when you call SaveChanges(). And as bonus, every single "not yet saved" POCO id will be unique (that should come handy one day ;) )
Comparing two POCO with IEquatable (why does dbset work so slow) is then easy:
public class Person
: IdBase, IEquatable<Person>
{
public virtual string FirstName { get; set; }
public bool Equals(Person other)
{
return Id == other.Id;
}
}

Related

Spring Data Neo4j 4 and dynamic properties

At my Neo4j/SDN 4 project I have a following entity:
#NodeEntity
public class Value extends BaseEntity {
#Index(unique = false)
private Object value;
private String description;
...
}
During the application run-time I want to be able to add a new dynamic properties to Value node, like for example value_en_US, value_fr_FR.
Right now I don't know what exact properties will be added to a particular Value node during application run-time so I can't define these properties at the code as a separate fields in Value.
Is there at SDN 4 any mechanisms to define these properties during the application run-time? I need something similar to DynamicProperties from SDN 3.
There is no such functionality in SDN 4, but it will be added in SDN 5 through a #Properties annotation on Map.
It will be available for testing in snapshot version very soon.
Check out this commit for more details
You might also want to look at this response to a similar question.
https://stackoverflow.com/a/42632709/5249743
Just beware that in that answer the function:
public void addAllFields(Class<?> type) {
for (Field field : type.getDeclaredFields()) {
blacklist.add(field.getName());
}
if (type.getSuperclass() != null) {
addAllFields(type.getSuperclass());
}
}
is not bullet proof. For one thing it doesn't look at #Property annotations. So if you want to go down that route keep your eyes open.
An 'improvement' is
public void addAllFields(Class<?> type) {
for (Field field : type.getDeclaredFields()) {
blacklist.add(findName(field));
}
if (type.getSuperclass() != null) {
addAllFields(type.getSuperclass());
}
}
private String findName(Field field) {
Property property = field.getAnnotation(Property.class);
if(property == null || "".equals(property.name())) {
return field.getName();
} else {
return property.name();
}
}
But this obviously doesn't look for the annotation on methods...

Cannot use enum in repository query (neo4j/Spring Data)

I'm having a problem querying based on an Enum property of my NodeEntity.
The NodeEntity in question is defined:
#NodeEntity(label = "Entity")
public class MyEntity {
#GraphId
private Long internalId;
....
private State state;
#Transient
public enum State {
STATEONE, STATETWO, STATETHREE
}
....
It saves without a problem, the state Enum represented perfectly, and I can query using other properties (Strings) with no problem at all. However the problem is the following query in a repository:
#Query("MATCH (entity:Entity {state:{0}})" +
"RETURN entity")
List<MyEntity> findByState(MyEntity.State state)
i.e. find all entities with the given state.
There's no exception, however using this simply returns a List of 0 Entities.
I've tried all kinds of variations on this, using a WHERE clause for example, with no luck.
The Entities are persisted properly, using findAll() in the same test returns the expected List of Entities with their states exactly as I would expect.
Any thoughts?
Not quite sure what the value #Transient adds to the enum. It is anyway not persistable as a node or relationship in Neo4j. It is sufficient to define the field as one that should persist with
private State state;
and leave off the #Transient annotation from the enum.
With it, SDN ignores the field sent to the derived query.
However, if you have a good reason to mark the enum #Transient, please do share it and we'll re-visit this case.
There is a general problems using spring data rest interface to search on enum fields. Just using the enum-to-string converter cannot work for search where you want to find if the value is IN a collection of values:
public interface AppointmentRepository extends Neo4jRepository<Appointment, Long> {
Page<Appointment> findByDayOfWeekIn(#Param("days") List<DayOfWeek> days, Pageable pageable);
}
The above does not work out of the box because neo4j will try to convert a List to your property type: DayOfWeek
In order to work around this I needed a custom converter that handles both requests providing collection of values (the search) and single values (the normal read and write entity):
#SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class SearchQueryEnumConverter<T extends Enum> {
private Class<T> enumType;
public SearchQueryEnumConverter() {
enumType = (Class<T>) ((ParameterizedType) this.getClass()).getActualTypeArguments();
}
public Object toGraphProperty(Object value) {
if (Collection.class.isAssignableFrom(value.getClass())) {
List<T> values = (List<T>) value;
return values.stream().map(Enum::name).collect(Collectors.toList());
}
return ((Enum) value).name();
}
public Object toEntityAttribute(Object value) {
if (Collection.class.isAssignableFrom(value.getClass())) {
List<String> values = (List<String>) value;
return values.stream().map(v -> (T) T.valueOf(enumType, v)).collect(Collectors.toList());
}
return (T) T.valueOf(enumType, value.toString());
}
}
The abstract converter can be reified by all enums, and used as parameter of the #Convert annotation:
public enum EnumType {
VALUE_A, VALUE_B;
public static class Converter extends SearchQueryEnumConverter<EnumType> implements AttributeConverter {
}
}
#NodeEntity
public Entity {
#Property
#Convert(EnumType.Converter.class)
EnumType type;
}

How can I use Automapper to map all zero int values to null values for nullable int targets?

I use int? for all my required 'FK' properties in ViewModels. This gives me an easy way of specifying on a Create view model that a value is nullable and must be assigned a value to satisfy the Required attribute.
My problem comes in because I create the domain model entity first, using a domain factory, then map it to the view model. Now, many of the nullable ints in the view model get assigned 0 from non-nullable ints in the domain model. I would prefer not to build the new entity in the view model and only map it back to the domain model to avoid his. What else can I do? i'm sure there is som Automapper voodoo that can help me.
EDIT: you dont need to do any of this, but i thought i'd leave it here for people looking for a similar solution. really all you have to do is just provide a mapping from int to int? like this: Mapper.Map<int, int?>()
in that case, I believe you could use a custom type converter, which inherits from automappers ITypeConverter. This code works, I've run it through .NET Fiddle:
using System;
using AutoMapper;
public class Program
{
public void Main()
{
CreateMappings();
var vm = Mapper.Map<MyThingWithInt, MyThingWithNullInt>(new MyThingWithInt());
if (vm.intProp.HasValue)
{
Console.WriteLine("Value is not NULL!");
}
else
{
Console.WriteLine("Value is NULL!");
}
}
public void CreateMappings()
{
Mapper.CreateMap<int, int?>().ConvertUsing(new ZeroToNullIntTypeConverter ());
Mapper.CreateMap<MyThingWithInt, MyThingWithNullInt>();
}
public class ZeroToNullIntTypeConverter : ITypeConverter<int, int?>
{
public int? Convert(ResolutionContext ctx)
{
if((int)ctx.SourceValue == 0)
{
return null;
}
else
{
return (int)ctx.SourceValue;
}
}
}
public class MyThingWithInt
{
public int intProp = 0;
}
public class MyThingWithNullInt
{
public int? intProp {get;set;}
}
}
You can always use the .ForMember() method on your mapping. Something like this:
Mapper
.CreateMap<Entity, EntityDto>()
.ForMember(
dest => dest.MyNullableIntProperty,
opt => opt.MapFrom(src => 0)
);

Should the EnumDataTypeAttribute work correctly in .NET 4.0 using Entity Framework?

I have an enumeration which I'd like to persist as a value of some sort into the underlying database so that I can bring it back and forth.
I have read some articles that suggest to create a enumeration wrapper with static implicit operators defined, mapped using a ComplexType object mapping as described in the link below.
How to fake Enums in EF4
This solution works flawlessly! My thanks to Alex James.
Aside, I discovered of the EnumDataTypeAttribute Class which purpose seems to handle enums persistence through Entity Framework. I tried it and it doesn't seem to work at all. Here's a code sample.
public enum StreetDirection {
East
, None
, North
, NorthEast
, NorthWest
, South
, SouthEast
, SouthWest
, West
}
public enum StreetType {
Avenue
, Boulevard
, Court
, Crescent
, Drive
, Hill
, None
, Road
, Street
}
public class StreetTypeWrapper {
public int Value {
get {
return (int)t;
}
set {
t = (StreetType)value;
}
}
public StreetType EnumValue {
get {
return t;
}
set {
t = value;
}
}
public static implicit operator int(StreetTypeWrapper w) {
return w.Value;
}
public static implicit operator StreetType(StreetTypeWrapper w) {
return w == null ? StreetType.None : w.EnumValue;
}
public static implicit operator StreetTypeWrapper(int i) {
return new StreetTypeWrapper() { Value = i };
}
public static implicit operator StreetTypeWrapper(StreetType t) {
return new StreetTypeWrapper() { EnumValue = t };
}
private StreetType t;
}
public class Street {
[EnumDataType(typeof(StreetDirection))]
public StreetDirection Direction { get; set; }
public string Name { get; set; }
public int StreetId { get; set; }
public StreetTypeWrapper Type { get; set; }
}
public class StreetTypeMapping
: ComplexTypeConfiguration<StreetTypeWrapper> {
public StreetTypeMapping() {
Property(o => o.Value)
.HasColumnName("StreetType");
}
}
Now, if I believe and/or understanding correctly what MSDN says about the EnumDataTypeAttribute class, the Direction property should get persisted into the database. Well, it doesn't! I can't find a reason for this, except that EF doesn't support enums persistence. As for the StreetTypeWrapper and its StreetTypeMapping class, it does work flawlessly.
Are there any clue why the EnumDataType shouldn't work as expected?
This is because of design flaw in .NET framework. .NET framework contains famous System.ComponentModel.DataAnnotations namespace where multiple different attributes are defined. Many different parts of .NET framework are using this namespace but they are using it to achieve different tasks and every such part use only some subset of attributes. This causes a lot of confusion.
EnumDataTypeAttribute is such example. This attribute is only for ASP.NET Dynamic Data. It allows you to mark int property with this attribute and automatically generated UI will show drop down with enum values instead of textbox for numeric values. So there is mapping from enum to int but it is in UI layer not in model / persistence layer.

How to use methods in ObjectContext class, if I use IObjectSet<T> for unit test?

I am using EF4 with POCO and trying to make it testable architecture.
So I create IObjectContext interface as follow :
public interface IObjectContext
{
IObjectSet<Employee> Employees { get; }
IObjectSet<Team> Teams { get; }
void Commit();
}
Then I changed type of properties to IObjectSet in my real ObjectContext class as follow :
public partial class HRManagementEntities : ObjectContext, IUnitOfWork
{
// skip some codes here...........
public IObjectSet<Employee> Employees
{
get { return _employees ?? (_employees = CreateObjectSet<Employee>("Employees"));
}
private IObjectSet<Employee> _employees;
public IObjectSet<Team> Teams
{
get { return _teams ?? (_teams = CreateObjectSet<Team>("Teams")); }
}
private IObjectSet<Team> _teams;
public void Commit()
{
SaveChanges();
}
}
In my service layer, consume EF like this :
public class Service
{
private IObjectContext ctx;
public HRService(IObjectContext ctx)
{
this.ctx = ctx;
}
public List<Team> GetAllTeams()
{
return ctx.Teams.ToList();
}
}
Here is my problem, How to call methods in ObjectContext for example, ApplyCurrentValues(), ExecuteStoreCommand(), and so on... ?
Do I need to implement those methods in the IObjectContext to use?
EDIT
As RPM's advice, I created following extension method for ApplyCurrentValues() method, another methods could be extended in same way.
public static T UpdateModel<T>(this IObjectSet<T> iObjectSet, T currentEntity) where T : class
{
ObjectSet<T> objectSet = iObjectSet as ObjectSet<T>;
if (objectSet == null || currentEntity == null)
throw new ArgumentNullException();
return objectSet.ApplyCurrentValues(currentEntity);
}
You need to create extension methods for the methods you need, and cast the IObjectSet to ObjectSet.
For instance, if you need to do .Include (eager loading), use this extension method:
public static IQueryable<TSource> Include<TSource>(this IQueryable<TSource> source, string path)
{
var objectQuery = source as ObjectQuery<TSource>;
if (objectQuery != null)
{
objectQuery.Include(path);
}
return source;
}
You could probably do the same thing for IObjectContext, but not sure why you are even mocking this out. You should not expose the OC to outside the repository, only the repository should know about the OC.
In your example, your service is calling ctx.Teams on the entities/repository.
IMO your service should be calling ctx.Find, which would be strongly-typed (via generics) to the Teams object context. (IRepository)
A common trap is to over-mock things. Don't mock everything just for the sake of it, mock the functionality which you require for unit testing.
If you want to abstract out the Object Context then use the Unit of Work pattern.

Resources