What is the best way to compare two entity framework entities? - entity-framework

I want to know the most efficient way of comparing two entities of the same type.
One entity is created from an xml file by hand ( ie new instance and manually set properties) and the other is retvied from my object context.
I want to know if the property values are the same in each instance.
My first thoughts are to generate a hash of the property values from each object and compare the hashes, but there might be another way, or a built in way?
Any suggestions would be welcome.
Many thanks,
James
UPDATE
I came up with this:
static class ObjectComparator<T>
{
static bool CompareProperties(T newObject, T oldObject)
{
if (newObject.GetType().GetProperties().Length != oldObject.GetType().GetProperties().Length)
{
return false;
}
else
{
var oldProperties = oldObject.GetType().GetProperties();
foreach (PropertyInfo newProperty in newObject.GetType().GetProperties())
{
try
{
PropertyInfo oldProperty = oldProperties.Single<PropertyInfo>(pi => pi.Name == newProperty.Name);
if (newProperty.GetValue(newObject, null) != oldProperty.GetValue(oldObject, null))
{
return false;
}
}
catch
{
return false;
}
}
return true;
}
}
}
I haven't tested it yet, it is more of a food for thought to generate some more ideas from the group.
One thing that might be a problem is comparing properties that have entity values themselves, if the default comparator compares on object reference then it will never be true. A possible fix is to overload the equality operator on my entities so that it compares on entity ID.

Override the Equals method of your object and write an implementation that compares the properties that make it equal.
public override bool Equals(object obj)
{
return MyProperty == ((MyObject)obj).MyProperty
}

Related

MapStruct: How to update a bean reference instead properties values

Folks,
I have the following situation with MapStruct: I want to always update a field with a new instance instead of setting values in a pre-existing instance.
Example:
class A {
     B fieldB;
}
class B {
     String fieldString;
}
class ADTO {
     BDTO fieldB;
}
class BDTO {
     String fieldString;
}
I have the following mapping with MapStruct:
void copy(ADTO aDTO, #MappingTarget A a);
The result generated is similar to:
if (aDTO.getFieldB()!= null) {
if (a.getFieldB() == null) {
a.setFieldB(new B());
}
bDTOToB(aDTO.getFieldB(), a.getFieldB());
} else {
a.setFieldB(null);
}
What I would like to generate is the following:
if (aDTO.getFieldB()!= null) {
a.setFieldB(new B()); // ALWAYS create a new B instance
bDTOToB(aDTO.getFieldB(), a.getFieldB());
} else {
a.setFieldB(null);
}
I add that I need the 2 behaviors: for some fields the current behavior suits me, that is, set the values in an existing instance. For other fields, I need this change in behavior as I mentioned before (a.setFieldB(newB())).
Is it possible to do that? What better strategy?
The only way this is possible is like this:
#BeforeMapping
default void init( #MappingTarget A a ) {
a.setFieldB( new B() );
}
void copy(ADTO aDTO, #MappingTarget A a);
The #BeforeMapping will set your field prior to check. It will however not ommit the (now obsolete) null check on the target in the generated code.
There's no way to control the target check in MapStruct. The NullValuePropertyMappingStrategy defines how a null source should be handled in the target.

entity framework filter logical deleted records

I'm using EF 5.0 and Code First. In my generic repository I have a method for exclude records in a logical way. This method actually perform a update, setting the status of the entity field to false.
I would like to intercept my queries, and filter only where status == true.
Is there a easy way to do that? Ex:
new GenericRepository<Entity>().ToList();
// and internally it will filter where status == true.
create a generic method
public IQueryable<T> All<T>(Expression<Func<T, bool>> predicate) {
return context.Set<T>().Where(predicate);
}
and if you want something more linked to your status property, you have to use reflection and build the Lambda by yourself (as you can't use interfaces with linq to entites queries).
Something like that (untested), calling the generic All method.
public IQueryable<T>AllButDeleted<T>() {
var property = typeof(T).GetProperty("status");
//check if T has a "status" property
if (property == null && || property.PropertyType != typeof(bool)) throw new ArgumentException("This entity doesn't have an status property, or it's not a boolean");
//build the expression
//m =>
var parameter = new ParameterExpression(typeof(T), "m");
// m.status
Expression body = Expression.Property(parameter, property);
//m.status == true (which is just m.status)
body = Expression.IsTrue(body);
//m => m.status
var lambdaPredicate = Expression.Lambda<Func<T, bool>>(body, new[]{parameter});
return All(lambdaPredicate);
}
You can make all your entities implement some IDeletable interface:
public interface IDelitable
{
bool IsDeleted { get; }
}
And add constraint to generic parameter of your repository
public class GenericRepository<T>
where T: class, IDelitable
And add filter when you are returning values:
context.Set<T>().Where(e => !e.IsDeleted)
You can filter it using Where.
.Where(e => e.Status == true).ToList();

How can I execute code when an entity is being persisted to the database with Entity-Framework code-first?

Is there a nice way to execute code when an entity is being saved to the database using EF/code-first?
I have a Url property on many of my entities. If a URL has not been given explicitly, I would like to calculate one as the object is persisted, eg.:
public void OnModelSaving()
{
// If a URL has not been specified, generate one from the name.
if (this.Url == null)
{
this.Url = Helper.GenerateSafeUrl(this.Title);
}
}
Ideally, I'd like all this code to be stored inside the Model, but since I don't have an EF-owned base/partial class, I suspect if it's possible, I'd have to register/wire it up elsewhere. Question is - is it possible, and if so, how do I wire it up?
The only way is to override SaveChanges method on your context, iterate through changed entities and check the Url
public override int SaveChanges()
{
var entities = ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added ||
e.State == EntityState.Modified)
.Select(e => e.Entity())
.OfType<YourEntityType();
foreach (var entity in entities)
{
entity.Url = ...;
}
return base.SaveChanges();
}
If you have many entity types providing Url you can try to define interface with that Url implemented by all that entity types and in OfType use that interface.

IEnumerable - my extension method

public static Record FindOrCreate(this IEnumerable<Record> ienumerable, string name)
{
if (ienumerable.Where(element => element.Name == name).FirstOrDefault() != null)
return ienumerable.Where(element => element.Name == name).FirstOrDefault();
else
{
IEnumerable<Record> result = ienumerable.Concat(new[] {new Record(name, "")});
ienumerable = result;
return ienumerable.Where(element => element.Name == name).FirstOrDefault();
}
}
I know there is no Add method to IEnumerable but I want to stay with IEnumerable instead of ICollection and in really rare cases I have to add something. My methodmalways returns null if it doesn't find element.
Code EDITED !
So, now result has all elements that I want, but method still return null object. I don't know if is it possible to switch objects in extension methods? (like I did ienumerable=result) ?
I think you're trying to bend the IEnumerable<T> beyond it's purpose.
An IEnumerable<T> is just a sequence of elements and that's why IEnumerable<T> does not have an Add<T> method in it's interface. It's read only.
I would recommend changing your implementation of your extension method.
So the question is "how can I modify the code to not return null if the element isn't found, but return a new item"?
public static Record FindOrCreate(this IEnumerable<Record> source, string name)
{
Func<Record,bool> pred = element => element.Name == name;
return source.Any(pred) ? source.FirstOrDefault(pred) : new Record(name, "");
}
or
public static Record FindOrCreate(this IEnumerable<Record> source, string name)
{
return source.FirstOrDefault(element => element.Name == name) ?? new Record(name, "");
}
If the question is "how can I add a new item to the IEnumerable<Record> source, the short answer is you can't. The longer answer is cast the source to an ICollection and then add it. But at that point, you may as well just specify that you need an ICollection<T>
Note, I don't mean "you shouldn't" when I say "you can't", really, IEnumberable has no way to add items. Think about what it means to try to add a new number to the following seq
public IEnumerable<int> ShortSeq()
{
yield return 0;
}
var seq = ShortSeq();

How do I set multiple error messages for different scenarios in a Custom validation attribute?

I'm just getting to grips with custom validation attributes, and I'm trying to write a custom validation attirbute which will be placed at class level to validate against multiple properties of my model.
I can access all properties on my model, and I want to be able to check for multiple conditions in my IsValid overload, and report on them, having different error messages as follows (simplistic example).
public override bool IsValid(object value)
{
var model = (MyObject) value;
//if this value is set, I don't want to do anything other checks
if (model.Prop3)
{
return true;
}
if (model.Prop1 == "blah" && model.Prop2 == 1)
{
ErrorMessage = "you can't enter blah if prop 2 equals 1";
return false;
}
if(model.Prop1 == "blah blah" && model.Prop2 == 2)
{
ErrorMessage = "you can't enter blah blah if prop 2 equals 2";
return false;
}
return true;
}
But when I do this I get an exception on the first time ErrorMessage is referenced "Cannot set property more than once.
Now I could split up my custom attribute into multiple custom attributes, but hoped there would be a way to do it in one, otherwise, I'll be repeating my "catch all" in each
//if this value is set, I don't want to do anything other checks
if (model.Prop3)
{
return true;
}
I've had a search already, but couldn't find anything, so apologies if I am missing anything obvious.
thanks in advance!
In MVC4 you can override IsValid to return different messages as the ValidationResult
public class StrongPasswordAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if (value == null)
return new ValidationResult("Password is required");
var val = value.ToString();
if (!Regex.Match(val, #"^(?=.*[a-z]).{0,}$").Success)
{
return new ValidationResult("Password must contain at least one lower case letter");
}
if (!Regex.Match(val, #"^(?=.*[A-Z]).{0,}$").Success)
{
return new ValidationResult("Password must contain at least one UPPER case letter");
}
if (!Regex.Match(val, #"^(?=.*\d).{0,}$").Success)
{
return new ValidationResult("Password must contain at least one number");
}
return ValidationResult.Success;
}
}
Interesting question! I can think of two work-arounds to this. So not proper solutions based on what you want but they might help to re-use your code. Cant you create a CustomAttribute abstract class called MyCustomAttribute (or something) that overrides IsValid in the following way:
public override bool IsValid(object value)
{
var model = (MyObject) value;
//if this value is set, I don't want to do anything other checks
if (model.Prop3)
{
return true;
}
CustomValidate(model);
}
CustomValidate(MyObject model) is your abstract method then, you can write multiple custom attribute classes that extend MyCustomAttribute and purely need to implement the validation logic for A particular scenario.
So you can have two classes:
public class BlahCustomAttribute : MyCustomAttribute
{
public override Boolean CustomValidate(MyObject obj)
{
if (model.Prop1 == "blah" && model.Prop2 == 1)
{
ErrorMessage = "you can't enter blah if prop 2 equals 1";
return false;
}
}
}
public class BlahBlahCustomAttribute : MyCustomAttribute
{
public override Boolean CustomValidate(MyObject obj)
{
if (model.Prop1 == "blah" && model.Prop2 == 1)
{
ErrorMessage = "you can't enter blah blah if prop 2 equals 1";
return false;
}
}
}
Hope this helps - not exactly what you wanted but will do the job and its clean as well.
The other solution is to comma-separate the error messages in the ErrorMessage property and handle it in the front-end (but I would go with the first approach).