How to update shadow properties with Entity Framework Core ExecuteUpdate method? - entity-framework-core

On saving changes to the database, we want to update our shadow properties (CreatedOn & ModifiedOn) automatically. This can be done by using overriding SaveChangesAsync method in the DbContext class.
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
ChangeTracker.DetectChanges();
var timestamp = systemClock.UtcNow.DateTime;
foreach (var entry in ChangeTracker.Entries()
.Where(e => e.Entity is BaseIdentifierEntity)
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified))
{
if (entry.State == EntityState.Added)
{
entry.Property(nameof(BaseIdentifierEntity.CreatedOn)).CurrentValue = timestamp;
}
if (entry.State == EntityState.Modified)
{
entry.Property(nameof(BaseIdentifierEntity.ModifiedOn)).CurrentValue = timestamp;
}
};
return base.SaveChangesAsync(cancellationToken);
}
Now we want to use the ExecuteUpdateAsync EF code method to update records in bulk but those changes are not detected by the change tracker.
Eg.
await context.Invoices
.Where(_ => _.Status == InvoiceStatusEnum.Draft)
.ExecuteUpdateAsync(_ => _.SetProperty(invoice => invoice.Status, InvoiceStatusEnum.Approved), cancellationToken);
One possible solution we're thinking about, is having a ExecuteUpdateWithShadowPropertiesAsync method but we don't succeed to merge the 2 expressions into one.
public static class EntityFrameworkExtensions
{
public static Task<int> ExecuteUpdateWithShadowPropertiesAsync<TSource>(this IQueryable<TSource> source, Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setPropertyCalls, CancellationToken cancellationToken = default) where TSource : BaseIdentifierEntity
{
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setShadowPropertyCalls = _ => _.SetProperty(p => p.ModifiedOn, DateTime.UtcNow);
// TODO: A method to combine both expressions into one expression
var mergedPropertyCalls = Merge(setPropertyCalls, setShadowPropertyCalls);
return source.ExecuteUpdateAsync(mergedPropertyCalls, cancellationToken: cancellationToken);
}
}

Actually there are two or three questions here, so let handle them separately.
How to update shadow properties with Entity Framework Core ExecuteUpdate method?
Shadow properties inside any EF Core query expression tree are accessed through EF.Property method, which is EF Core generic property accessor expression and works for both shadow and regular properties.
So if your ModifiedOn was a shadow property (it isn't) of type DateTime, it can be updated as follows:
query.ExecuteUpdateAsync(s => s
.SetProperty(p => EF.Property<DateTime>("ModifiedOn"), DateTime.UtcNow)
...);
How to combine lambda expressions?
This has been covered by many answers on SO, or over internet. But basically you need to emulate "call" to one of the expressions passing the other as argument. This is achieved with either Expression.Invoke which is not always supported by query translators (including EF Core), or (which always works) by replacing the parameter of the "called" lambda expression with the body of the other lambda expression.
The later is achieved with custom ExpressionVisitor. You can find many implementations, EF Core also provides its own called ParameterReplacingVisitor, but I'm using my own little expression helper class, which is general and have no EF Core or other 3rd party dependencies. It is quite simple:
namespace System.Linq.Expressions;
public static class ExpressionUtils
{
public static Expression ReplaceBodyParameter<T, TResult>(this Expression<Func<T, TResult>> source, Expression value)
=> source.Body.ReplaceParameter(source.Parameters[0], value);
public static Expression ReplaceParameter(this Expression source, ParameterExpression target, Expression replacement)
=> new ParameterReplacer(target, replacement).Visit(source);
class ParameterReplacer : ExpressionVisitor
{
readonly ParameterExpression target;
readonly Expression replacement;
public ParameterReplacer(ParameterExpression target, Expression replacement)
=> (this.target, this.replacement) = (target, replacement);
protected override Expression VisitParameter(ParameterExpression node)
=> node == target ? replacement : node;
}
}
With that helper, the method you are looking for would be:
// TODO: A method to combine both expressions into one expression
var mergedPropertyCalls = Expression.Lambda<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>>(
setShadowPropertyCalls.ReplaceBodyParameter(setPropertyCalls.Body),
setPropertyCalls.Parameters);
You can go further and add a shortcut helper method specific for SetPropertyCalls:
public static Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> Append<TSource>(
this Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> target,
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> source)
where TSource : class
=> Expression.Lambda<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>>(
source.ReplaceBodyParameter(target.Body), target.Parameters);
and then the generic method in question would be simply:
public static Task<int> ExecuteUpdateWithShadowPropertiesAsync<TSource>(
this IQueryable<TSource> source,
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setPropertyCalls,
CancellationToken cancellationToken = default)
where TSource : BaseIdentifierEntity
=> source.ExecuteUpdateAsync(setPropertyCalls
.Append(s => s.SetProperty(p => p.ModifiedOn, DateTime.Now)),
cancellationToken);
Now having the previous two questions answered, the next would be - Instead of using custom extension method, can this be done better in EF Core? Ideally on a similar fashion as the change tracker (SaveChanges) approach.
And the answer is yes. EF Core 7.0 along with batch updates introduced a long asked and very handy feature called Interception to modify the LINQ expression tree (unfortunately not documented yet). It allows you to intercept and modify LINQ query expression tree before EF Core. In this case, it could be used to add more update properties to ExecuteUpdate query.
In order to utilize it, we first define interceptor class
#nullable disable
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Microsoft.EntityFrameworkCore;
internal class ExecuteUpdateInterceptor : IQueryExpressionInterceptor
{
List<(Type Type, Delegate Calls, Func<IEntityType, bool> Filter)> items = new();
public ExecuteUpdateInterceptor Add<TSource>(
Func<Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>>> source,
Func<IEntityType, bool> filter = null)
{
items.Add((typeof(TSource), source, filter));
return this;
}
Expression IQueryExpressionInterceptor.QueryCompilationStarting(
Expression queryExpression, QueryExpressionEventData eventData)
{
if (queryExpression is MethodCallExpression call &&
call.Method.DeclaringType == typeof(RelationalQueryableExtensions) &&
call.Method.Name == nameof(RelationalQueryableExtensions.ExecuteUpdate))
{
var setPropertyCalls = (LambdaExpression)((UnaryExpression)call.Arguments[1]).Operand;
var body = setPropertyCalls.Body;
var parameter = setPropertyCalls.Parameters[0];
var targetType = eventData.Context?.Model.FindEntityType(parameter.Type.GetGenericArguments()[0]);
if (targetType != null)
{
foreach (var item in items)
{
if (!item.Type.IsAssignableFrom(targetType.ClrType)) continue;
if (item.Filter != null && !item.Filter(targetType)) continue;
var calls = (LambdaExpression)item.Calls.Method.GetGenericMethodDefinition()
.MakeGenericMethod(targetType.ClrType)
.Invoke(null, null);
body = calls.Body.ReplaceParameter(calls.Parameters[0], body);
}
if (body != setPropertyCalls.Body)
return call.Update(call.Object, new[] { call.Arguments[0], Expression.Lambda(body, parameter) });
}
}
return queryExpression;
}
}
This requires a bit more knowledge of expressions, so you can just use it as is. Basically what it does is intercepting the ExecuteUpdate "calls" and appending additional SetProperty "calls" based on statically configured rules and filters.
The only remaining is to create, configure and add the interceptor inside your OnConfigure override:
optionsBuilder.AddInterceptors(new ExecuteUpdateInterceptor()
//.Add(...)
//.Add(...)
);
The configuration is based on delegates, with only limitation/requirement the SetPropertyCalls generic Func to be a real generic method and not anonymous delegate (I haven't found a way to make it easy for use and at the same time being anonymous).
So here are some usages:
property of a base class (your case):
optionsBuilder.AddInterceptors(new ExecuteUpdateInterceptor()
.Add(SetModifiedOn<BaseIdentifierEntity>)
);
static Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> SetModifiedOn<TSource>()
where TSource : BaseIdentifierEntity
=> s => s.SetProperty(p => p.ModifiedOn, DateTime.UtcNow);
entity property with specific name and type (shadow or regular). Uses presence of the property as a filter. Also works in your case.
const string ModifiedOn = nameof(ModifiedOn);
optionsBuilder.AddInterceptors(new ExecuteUpdateInterceptor()
.Add(SetModifiedOn<object>, t => t.FindProperty(ModifiedOn) is { } p && p.ClrType == typeof(DateTime))
);
static Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> SetModifiedOn<TSource>()
where TSource : class
=> s => s.SetProperty(p => EF.Property<DateTime>(p,ModifiedOn), DateTime.UtcNow);
Note: The SetPropertyCalls func must be generic, to allow binding it to the actual source type from the query.
Also, I haven't mentioned it explicitly till now, but with the last approach, you just use a standard ExecuteUpdate or ExecuteUpdateAsync methods, and the interceptor adds the cofigured additional SetProperty expressions.

The following extension updates shadow property with other fields:
public static class EntityFrameworkExtensions
{
public static Task<int> ExecuteUpdateWithShadowPropertiesAsync<TSource>(this IQueryable<TSource> source,
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setPropertyCalls,
CancellationToken cancellationToken = default)
where TSource : class
{
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setShadowPropertyCalls =
x => x.SetProperty(p => EF.Property<DateTime>(p, "ModifiedOn"), p => DateTime.UtcNow);
var mergedPropertyCalls = Merge(setPropertyCalls, setShadowPropertyCalls);
return source.ExecuteUpdateAsync(mergedPropertyCalls, cancellationToken: cancellationToken);
}
static Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> Merge<TSource>(
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> setPropertyCalls,
Expression<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>> additional)
{
var newBody = ReplacingExpressionVisitor.Replace(additional.Parameters[0], setPropertyCalls.Body, additional.Body);
return Expression.Lambda<Func<SetPropertyCalls<TSource>, SetPropertyCalls<TSource>>>(newBody,
setPropertyCalls.Parameters);
}
}

Related

Using Find from subclass not in DbContext

ASP .NET 5 MVC Core application.
Following method is used to find entity by key:
class MyStudents: Students { }
public TEntity FindNormalized<TEntity>(params object[] keyValues)
where TEntity : class
{
return Find<TEntity>(keyValues);
}
void FindNormalized<TEntity>(TEntity entity, params object[] keyValues)
where TEntity : class
{
var res = FindNormalized<TEntity>(keyValues);
if (res == null)
throw new KeyNotFoundException(entity.GetType().Name + " entity key not found " + keyValues[0]);
CopyPropertiesTo(res, entity);
}
static void CopyPropertiesTo<T, TU>(T source, TU dest)
{ // https://stackoverflow.com/questions/3445784/copy-the-property-values-to-another-object-with-c-sharp
var sourceProps = typeof(T).GetProperties().Where(x => x.CanRead).ToList();
var destProps = typeof(TU).GetProperties()
.Where(x => x.CanWrite)
.ToList();
foreach (var sourceProp in sourceProps)
if (destProps.Any(x => x.Name == sourceProp.Name))
{
var p = destProps.First(x => x.Name == sourceProp.Name);
p.SetValue(dest, sourceProp.GetValue(source, null), null);
}
}
Using it with subclass
FindNormalized<MyStudents>(1);
throws exception
Cannot create a DbSet for 'MyStudents' because this type is not
included in the model for the context.
FindNormalized<Students>(1);
works.
How to fix this so that it can used with subclass type also ?
For getting table attribute from subclass code from Dynamic Linq works:
string GetTableAttrib(Type t)
{
foreach (Type t2 in SelfAndBaseClasses(t))
{
IEntityType entityType = Model.FindEntityType(t2);
if (entityType == null)
continue;
return entityType.GetTableName();
}
throw new ApplicationException(t.FullName + " no table attribute");
}
/// <summary>
/// Enumerate inheritance chain - copied from DynamicLinq
/// </summary>
static IEnumerable<Type> SelfAndBaseClasses(Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
Maybe this can used to implement Find also. Unfortunately Find throws exception. Is it reasonable to wrap Find using try/catch or is there better way ?
The EFCore Find method throws exception because the TEntity is not an entity type but a type deriving from the entity type. So to solve this, we need to first get the most derived type which is the ancestor type of the passed in TEntity type, so when passing in MyStudents you need to get type Students first to use as type argument for Find<> method. Because that type is not known at design time, so we need to use reflection to invoke that Find<> method or better use the other overload of Find that accepts the first argument of entity type (Type). Of course I understand that your Find<> method in your question is from DbContext.Find.
So the solution can be simple like this, firstly we need method to get the most derived entity type from the passed in type (which inherits from the entity type):
public static class DbContextExtensions {
public static Type GetMostDerivedEntityType(this DbContext dbContext, Type type){
while(type != null && dbContext.Model.FindEntityType(type) == null) {
type = type.BaseType;
}
return type;
}
}
Next just use that extension method in your FindNormalized method to find the most derived entity type first before using Find:
//I suppose that this is in the context (class) of your custom DbContext
void FindNormalized<TEntity>(TEntity entity, params object[] keyValues)
where TEntity : class
{
var actualEntityType = this.GetMostDerivedEntityType(typeof(TEntity)) ??
throw new InvalidOperationException($"The type {typeof(TEntity)} is not an entity type or a derive from an entity type");
//use the actualEntityType instead
var res = Find(actualEntityType, keyValues);
if (res == null)
throw new KeyNotFoundException(entity.GetType().Name + " entity key not found " + keyValues[0]);
//copy the properties
CopyPropertiesTo(res, entity);
}
Note that in the above code I use another overload (non-generic version) of Find which accepts the first argument of Type (entity type) directly instead of using your wrapper FindNormalized which is generic only (you can add your one overload of that wrapper which wraps the non-generic Find as used directly in my code above).
I've not tested the code at all. Just try it and let me know if there is any error.

Custom EF Core AddOrUpdate with composite keys

I've built an extension for Microsoft.EntityFrameworkCore that implements the AddOrUpdateMethod. It's working fine, but with entities with a composite primary key the AnyAsync method return always false, even if there are objects with the same key.
This is the method:
public static async Task AddOrUpdateAsync<TEntity>(this DbSet<TEntity> table, Expression<Func<TEntity, object>> key, Expression<Func<TEntity, bool>> deleteExpression, params TEntity[] entities) where TEntity : class
{
var getKeyFunction = key.Compile();
var getShouldDeleteFunction = deleteExpression.Compile();
var context = GetDbContext(table);
foreach (var entity in entities)
{
var primaryKey = getKeyFunction(entity);
var body = Expression.Equal(Expression.Convert(key.Body, primaryKey.GetType()), Expression.Constant(primaryKey));
Expression<Func<TEntity, bool>> query = Expression.Lambda<Func<TEntity, bool>>(body, key.Parameters);
var exist = await table.AnyAsync(query);
context.Entry(entity).State = exist
? getShouldDeleteFunction(entity) ? EntityState.Deleted : EntityState.Modified
: getShouldDeleteFunction(entity) ? EntityState.Detached : EntityState.Added;
}
}
private static DbContext GetDbContext<T>(this DbSet<T> table) where T : class
{
var infrastructure = table as IInfrastructure<IServiceProvider>;
var serviceProvider = infrastructure.Instance;
var currentDbContext = serviceProvider.GetService(typeof(ICurrentDbContext)) as ICurrentDbContext;
return currentDbContext.Context;
}
and I'm using it like this:
await db.Reports.AddOrUpdateAsync(r => new { r.Number, r.Year }, r => r.Active == false, response.Reports.ToArray());
I think that's happening because I'm using an anonymous type as the key, but I've no idea how to fix this.
The problem seems to be the usage of the anonymous type constant expression, which currently is causing client evaluation, and C# operator == compares anonymous types by reference, hence always returns false.
The trick to get the desired server translation is to "invoke" the key expression with entity by replacing the parameter with Expression.Constant(entity) (Expression.Invoke doesn't work in this case)
So remove the line var getKeyFunction = key.Compile(); as no longer needed, and use the following:
foreach (var entity in entities)
{
var parameter = key.Parameters[0];
var body = Expression.Equal(
key.Body,
key.Body.ReplaceParameter(parameter, Expression.Constant(entity))
);
var query = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
var exist = await table.AnyAsync(query);
// ...
}
where ReplaceParameter is the usual expression helper method:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
=> new ParameterReplacer { Source = source, Target = target }.Visit(expression);
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : node;
}
}

How can I dynamically make entity properties read-only?

I'm working with EF 4.5 and DbContext. At business rules layer level, I should implement checks to avoid change entity value properties in some entity scenarios. Sample: StartProjecteDate should be readonly if ProjectIsStarted but not in other status.
I follow DRY principle, for this reason, I should be able to inspect readonly properties list from context and also from UI.
My question:
Is there a DataAnnotation validator that can dynamically set properties as readonly?
(and if not, is there a different / better solution to this problem?)
Notice than I'm working with Web Forms (and Telerik) architecture, a clean and elegant pattern will be welcome.
I'm trying to set and get at run time EditableAttribute as Jesse Webb explains, but I'm not able to get dataannotation attributes from property, my code:
<EditableAttribute(False)>
<MaxLength(400, ErrorMessage:="Màxim 400 caracters")>
Public Property NomInvertebrat As String
Edited Nov 8 2013 after digging docs, it seems that dataanottions if for class but for instance object itself. Perhaps an iReadonlyableProperties interface may be a way.
I have a class containing extension methods that lets me read data annotations like this:
int maxRefLen = ReflectionAPI.GetProperty<Organisation, String>(x => x.Name)
.GetAttribute<StringLengthAttribute>()
.GetValueOrDefault(x => x.MaximumLength, 256);
So if you use it you should be able to do get the value of the EditableAttribute like this:
bool isEditable = ReflectionAPI.GetProperty<Foo, String>(x => x.NomInvertebrat)
.GetAttribute<EditableAttribute>()
.GetValueOrDefault(x => x.AllowEdit, true);
As for setting the data annotations at run-time, I haven't done it myself but I have read that there is a solution here: Setting data-annotations at runtime
Getting a list of all data annotations of a particular type I think would entail reading the entity framework metadata. Again I haven't tried this.
If you add that together I personally think it feels clunky rather than elegant, but you have asked for a solution using DataAnnotations and something more elegant would probably mean getting into your architecture.
I would be inclined to do this:
public bool StartDateIsReadOnly
{
//use this property client-side to disable the input
get{ return Project.IsStarted;}
}
//Implement IValidatable object to do server side validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext
{
bool startdateIsChanged = // I'll leave you to work out this bit
var results = new List<ValidationResult>();
if(StartDateIsReadOnly && startdateIsChanged)
results.Add(new ValidationResult("Start Date cannot be changed after project is started");
}
Here is the ReflectionAPI class:
Please note that the class includes part of a hack that #JonSkeet posted and described as "evil". I personally think this bit ain't so bad, but you should read the following references:
Override a generic method for value types and reference types.
Evil code - overload resolution workaround
public static class ReflectionAPI
{
public static int GetValueOrDefault<TInput>(this TInput a, Func<TInput, int> func, int defaultValue)
where TInput : Attribute
//Have to restrict to struct or you get the error:
//The type 'R' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'
{
if (a == null)
return defaultValue;
return func(a);
}
public static Nullable<TResult> GetValueOrDefault<TInput, TResult>(this TInput a, Func<TInput, TResult> func, Nullable<TResult> defaultValue)
where TInput : Attribute
where TResult : struct
//Have to restrict to struct or you get the error:
//The type 'R' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'
{
if (a == null)
return defaultValue;
return func(a);
}
//In order to constrain to a class without interfering with the overload that has a generic struct constraint
//we need to add a parameter to the signature that is a reference type restricted to a class
public class ClassConstraintHack<T> where T : class { }
//The hack means we have an unused parameter in the signature
//http://msmvps.com/blogs/jon_skeet/archive/2010/11/02/evil-code-overload-resolution-workaround.aspx
public static TResult GetValueOrDefault<TInput, TResult>(this TInput a, Func<TInput, TResult> func, TResult defaultValue, ClassConstraintHack<TResult> ignored = default(ClassConstraintHack<TResult>))
where TInput : Attribute
where TResult : class
{
if (a == null)
return defaultValue;
return func(a);
}
//I don't go so far as to use the inheritance trick decribed in the evil code overload resolution blog,
//just create some overloads that take nullable types - and I will just keep adding overloads for other nullable type
public static bool? GetValueOrDefault<TInput>(this TInput a, Func<TInput, bool?> func, bool? defaultValue)
where TInput : Attribute
{
if (a == null)
return defaultValue;
return func(a);
}
public static int? GetValueOrDefault<TInput>(this TInput a, Func<TInput, int?> func, int? defaultValue)
where TInput : Attribute
{
if (a == null)
return defaultValue;
return func(a);
}
public static T GetAttribute<T>(this PropertyInfo p) where T : Attribute
{
if (p == null)
return null;
return p.GetCustomAttributes(false).OfType<T>().LastOrDefault();
}
public static PropertyInfo GetProperty<T, R>(Expression<Func<T, R>> expression)
{
if (expression == null)
return null;
MemberExpression memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
return null;
return memberExpression.Member as PropertyInfo;
}
}
.NET allows you to dynamically change structure of Class by implementing System.ComponentModel.ICustomTypeDescriptor. Most serializers support this interface.
// Sample Serialization
foreach(PropertyDescriptor p in TypeDescriptor.GetProperties(obj)){
string name = p.PropertyName;
object value = p.GetValue(obj);
}
Internally TypeDescriptor uses Reflection, but the implementation allows us to override reflection attributes easily.
Here are three steps of implementation,
// Implement System.ComponentModel.ICustomTypeDescriptor Interface on
// your Entity
public class MyEntity: System.ComponentModel.ICustomTypeDescriptor
{
....
// most methods needs only call to default implementation as shown below
System.ComponentModel.AttributeCollection
System.ComponentModel.ICustomTypeDescriptor.GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
string System.ComponentModel.ICustomTypeDescriptor.GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
string System.ComponentModel.ICustomTypeDescriptor.GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
System.ComponentModel.TypeConverter System.ComponentModel.ICustomTypeDescriptor.GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
System.ComponentModel.EventDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
System.ComponentModel.PropertyDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
object System.ComponentModel.ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
System.ComponentModel.PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
return TypeDescriptor.GetProperties(this, attributes, true);
}
object System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd)
{
return this;
}
// The Only method that needs different implementation is below
System.ComponentModel.PropertyDescriptorCollection
System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
// ... you are supposed to create new instance of
// PropertyDescriptorCollection with PropertyDescriptor
PropertyDescriptorCollection pdc = new PropertyDescriptorCollection();
foreach(PropertyDescriptor p in TypeDescriptor.GetProperties(this,true)){
// if readonly..
AtomPropertyDescriptor ap = new AtomPropertyDescriptor(p, p.Name);
// or
AtomPropertyDescriptor ap = new AtomPropertyDescriptor(p, p.Name,
true,
new XmlIgnoreAttribute(),
new ScriptIgnoreAttribute(),
new ReadOnlyAttribute());
pdc.Add(ap);
}
return pdc;
}
}
// And here is the AtomPropertyDescriptorClass
public class AtomPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor desc;
bool? readOnly = null;
public AtomPropertyDescriptor(PropertyDescriptor pd, string name,
bool? readOnly, params Attribute[] attrs) :
base(name, attrs)
{
desc = pd;
this.readOnly = readOnly;
}
public override bool CanResetValue(object component)
{
return desc.CanResetValue(component);
}
public override Type ComponentType
{
get
{
return desc.ComponentType;
}
}
public override object GetValue(object component)
{
return desc.GetValue(component);
}
public override bool IsReadOnly
{
get
{
if (readOnly.HasValue)
return readOnly.Value;
return desc.IsReadOnly;
}
}
public override Type PropertyType
{
get { return desc.PropertyType; }
}
public override void ResetValue(object component)
{
desc.ResetValue(component);
}
public override void SetValue(object component, object value)
{
desc.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return desc.ShouldSerializeValue(component);
}
}
I think what you are looking for is a custom Annotation Attribute like this:
<DisableEditAttribute(this.IsProjectStarted)>
Public Property NomInvertebrat As String
public override bool IsValid(bool value)
{
bool result = true;
// Add validation logic here.
if(value)
{
//Compare Current Value Against DB Value.
}
return result;
}
See MSDN: http://msdn.microsoft.com/en-us/library/cc668224(v=vs.98).aspx

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();

LINQ to Entities - cannot cast 'System.DateTime' to type 'System.Object' in orderBy

I am trying to order an IQueryable of entities by date from a passed in Expression< Func< T, object>> and am getting the error: "Unable to cast the type 'System.Nullable`1' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types." The entity has a nullable datetime property on it on which I am trying to sort:
Example: (where e.Date is a nullable DateTime)
Expression<Func<T,object>> sorter = (e) => e.Date;
IOrderedQueryable<T> sortedData = data.OrderBy(sorter);
Thanks in advance!
I wrote a simple class for ordering entities based on a lambda expression at runtime.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace DataModeling
{
public class QueryOrderer<TEntity>
where TEntity : class
{
private LambdaExpression defaultSortExpression;
private Dictionary<string, LambdaExpression> orderFieldLookup;
public QueryOrderer()
{
orderFieldLookup = new Dictionary<string, LambdaExpression>();
}
public void AddOrderMapping<TProp>(string fieldName, Expression<Func<TEntity, TProp>> selector)
{
orderFieldLookup[fieldName] = selector;
}
public void SetDefaultSortExpression<TProp>(Expression<Func<TEntity, TProp>> selector)
{
defaultSortExpression = selector;
}
public IQueryable<TEntity> GetOrderedEntries(string field, bool isDescending, IQueryable<TEntity> entries)
{
return orderEntries(entries, field, isDescending);
}
private IQueryable<TEntity> orderEntries(IQueryable<TEntity> entries, string fieldName, bool isDescending)
{
dynamic lambda = getOrderByLambda(entries, fieldName);
if (lambda == null)
{
return entries;
}
if (isDescending)
{
return Queryable.OrderByDescending(entries, lambda);
}
else
{
return Queryable.OrderBy(entries, lambda);
}
}
private dynamic getOrderByLambda(IQueryable<TEntity> entries, string fieldName)
{
if (!String.IsNullOrWhiteSpace(fieldName) && orderFieldLookup.ContainsKey(fieldName))
{
return orderFieldLookup[fieldName];
}
else
{
return defaultSortExpression;
}
}
}
}
You use this class by initially setting up all of the fields:
QueryOrderer<User> orderer = new QueryOrderer<User>();
orderer.SetDefaultSortExpression(u => u.FullName);
orderer.AddOrderMapping("UserId", u => u.UserId);
orderer.AddOrderMapping("Name", u => u.FullName);
orderer.AddOrderMapping("Email", u => u.Email);
orderer.AddOrderMapping("CreatedOn", u => u.CreatedOn);
...
var users = orderer.GetOrderedEntries("CreatedOn", isDescending: false, context.Users);
I nice feature of this code is that it handles look-up values perfectly. For instance, if you're trying to sort using the description rather than a key, you can use the outer context when building up the sort expression:
orderer.AddOrderMapping("UserType",
u => context.UserTypes
.Where(t => t.UserTypeId == u.UserTypeId)
.Select(t => t.Description)
.FirstOrDefault());
Entity Framework is smart enough to just fold the sub-query right into the outer query.
Two problem here: First you use object in your sorter, you should use DateTime. Secondly every element must have a place in the order so you have to define what should happen with elements where Date is null:
Expression<Func<T, DateTime>> sorter = (e) => e.Date ?? DateTime.MaxValue;
IOrderedQueryable<T> sortedData = data.OrderBy(sorter);
Try to reconstruct expression body
private LambdaExpression CreateLambdaPropertyGetter(Expression<Func<TEntity, object>> expression)
{
Expression body;
if (expression.Body is UnaryExpression && ((UnaryExpression)expression.Body).NodeType == ExpressionType.Convert)
body = ((UnaryExpression)expression.Body).Operand;
else
body = expression.Body;
var lambda = Expression.Lambda(body, expression.Parameters);
return lambda;
}
Try using Func delegate instead on Expression<Func>
Func<T,object> sorter = (e) => e.Date;
IOrderedEnumerable<T> sortedData = data.OrderBy(sorter);