I have a extension method that checks the object for its type and then populate its member property
public static void LoadMeeting<T>(this T entity, IMeetingRepository meetingRepository)
where T: MyEntity
{
var agenda = entity as Agenda;
if (agenda != null)
{
agenda.Meeting = meetingRepository.GetMeetingById(agenda.MeetingId);
}
var participant = entity as Participant;
if (participant != null)
{
participant.Meeting = meetingRepository.GetMeetingById(participant.MeetingId);
}
}
can I further refactor that to something like this to make it more generic?
public static void LoadMeeting<T>(this T entity, IMeetingRepository meetingRepository) where T : MyEntity
{
var obj = entity as Agenda || entity as Participant;
if (obj != null)
{
obj.Meeting = meetingRepository.GetMeetingById(obj.MeetingId);
}
}
}
PS: I don't want to put the object's property Meeting in the Base class (MyEntity)
I would, personally, just use overloads:
public static void LoadMeeting(this Agenda agenda, IMeetingRepository meetingRepository)
{
if (agenda != null)
{
agenda.Meeting = meetingRepository.GetMeetingById(agenda.MeetingId);
}
}
public static void LoadMeeting(this Participant participant, IMeetingRepository meetingRepository)
{
if (participant != null)
{
participant.Meeting = meetingRepository.GetMeetingById(participant.MeetingId);
}
}
You couldn't do it with a single generic method unless you had some shared contract (either a base class or interface implemented) which provided the Meeting property.
The alternative would be to make a shared interface, ie: IMeeting, then constrain to that:
public interface IMeeting
{
public Meeting Meeting { get; set; }
public int MeetingId { get; }
}
Then you can write:
public static void LoadMeeting<T>(this T entity, IMeetingRepository meetingRepository)
where T: IMeeting
{
if (entity != null)
{
entity.Meeting = meetingRepository.GetMeetingById(entity.MeetingId);
}
}
As you're using EF, you could implement this interface in a partial class:
public partial class Agenda : MyEntity, IMeeting
{
}
You may want to return early out of the method incase you have to handle a class and it's base class.
public static void LoadMeeting<T>(this T entity, IMeetingRepository meetingRepository) where T: MyEntity
{
var agenda = entity as Agenda;
if (agenda != null)
{
agenda.Meeting = meetingRepository.GetMeetingById(agenda.MeetingId);
return;
}
var participant = entity as Participant;
if (participant != null)
{
participant.Meeting = meetingRepository.GetMeetingById(participant.MeetingId);
return;
}
}
You might also consider a table approach:
public static void LoadMeeting<T>(this T entity, IMeetingRepository meetingRepository) where T: MyEntity {
var name = entity.GetType().Name;
if (Table.ContainsKey(name)) {
Table[name](entity, meetingRepository);
}
}
Where Table is:
static Dictionary<String, Action<MyEntity, IMeetingRepository>> Table = new Dictionary<String, Action<MyEntity, IMeetingRepository>>();
And it is initialized like so:
Table.Add("Agenda", (agenda, meetingRepository) => {
((Agenda)agenda).Meeting = meetingRepository.GetMeetingById(((Agenda)agenda).MeetingId); });
Table.Add("Participant", (participant, meetingRepository) => {
((Participant)participant).Meeting = meetingRepository.GetMeetingById(((Participant)participant).MeetingId); });
Obviously your Table will have to be static and available to your extension method (say in the containing class).
Related
I use FluentValidation and PostSharp in my business layer.
My database has an Admin table, and the UserName column in this table is unique.
I want to check the uniqueness with an "Aspect".
My codes are as follows.
AdminValidator
public class AdminValidator : AbstractValidator<Admin>
{
public AdminValidator(IEnumerable<Admin> admins)
{
RuleFor(x => x.Fullname).MaximumLength(50);
RuleFor(x => x.Username).Matches(#"^\S{3,15}$").IsUnique(admins);
RuleFor(x => x.Password).Matches(#"^\S{5,20}$");
}
}
IsUnique extension method
public static class Extensions
{
public static IRuleBuilderOptions<TItem, TProperty> IsUnique<TItem, TProperty>(
this IRuleBuilder<TItem, TProperty> ruleBuilder, IEnumerable<TItem> items)
where TItem : class
{
return ruleBuilder.SetValidator(new UniqueValidator<TItem>(items));
}
}
public class UniqueValidator<T> : PropertyValidator
where T : class
{
private readonly IEnumerable<T> _items;
public UniqueValidator(IEnumerable<T> items)
: base("{PropertyName} must be unique")
{
_items = items;
}
protected override bool IsValid(PropertyValidatorContext context)
{
var editedItem = context.Instance as T;
var newValue = context.PropertyValue as string;
var property = typeof(T).GetTypeInfo().GetDeclaredProperty(context.PropertyName);
return _items.All(item =>
item.Equals(editedItem) || property.GetValue(item).ToString() != newValue);
}
}
FluentValidation aspect
[Serializable]
public class FluentValidationAspect : OnMethodBoundaryAspect
{
private readonly Type _validatorType;
private readonly object[] _parameters;
public FluentValidationAspect(Type validatorType, params object[] parameters)
{
_validatorType = validatorType;
_parameters = parameters;
}
public override void OnEntry(MethodExecutionArgs args)
{
var validator = (IValidator)Activator.CreateInstance(_validatorType, _parameters);
var entityType = _validatorType.BaseType?.GetGenericArguments()[0];
var entities = args.Arguments.Where(x => x.GetType() == entityType);
foreach (var entity in entities)
ValidatorTool.FluentValidate(validator, entity);
}
}
and AdminManager
public class AdminManager : IAdminService
{
private readonly IAdminDal _adminDal;
public AdminManager(IAdminDal adminDal)
{
_adminDal = adminDal;
}
public Admin GetByUsername(string username)
=> string.IsNullOrEmpty(username) ? null : _adminDal.Get(x => x.Username.Equals(username));
[FluentValidationAspect(typeof(AdminValidator))] // I need to pass IEnumerable<Admin> as a second parameter.
public void Add(Admin admin) => _adminDal.Add(admin);
[FluentValidationAspect(typeof(AdminValidator))] //
public void Update(Admin admin) => _adminDal.Update(admin);
public void DeleteById(int id) => _adminDal.Delete(new Admin { Id = id });
}
As I mentioned in the comment line, I need to pass IEnumerable to the FluentValidationAspect.
But, dynamic parameters can not be passed to attributes.
As a result, I'm blocked here.
What is the best way to check for uniqueness?
Thank you in advance for your help?
Best regards...
You can't pass dynamic values in Attributes, so you will have to do the validation in the method body.
public void Add(Admin admin)
{
var admins = GetAll():
var val = new AdminValidator(admins);
validator.ValidateAndThrow(admin);
_adminDal.Add(admin);
}
I'm overriding the ValidateEntity method to check for unique validation and I've hit a stumbling block.
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
if (entityEntry.Entity is ReferenceType && entityEntry.State == EntityState.Added)
{
var entity = entityEntry.Entity as ReferenceType;
var pluralService = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en-gb"));
var pluralEntity = pluralService.Pluralize(entity.GetType().Name);
// I would like Courses to be replaced with the property name of pluralEntity
if (Courses.Where(x => x.Title == entity.Title).Count() > 0)
{
result.ValidationErrors.Add(new DbValidationError(nameof(entity.Title), nameof(entity.Title) + " must be unique."));
}
}
if (result.ValidationErrors.Count > 0)
{
return result;
}
else
{
return base.ValidateEntity(entityEntry, items);
}
}
In my SchoolContext class I have the property DbSet<Course> Courses which is a ReferenceType (a custom abstract class type).
The value of pluralEntity is Courses, but I want to put in the if-statement something similar to:
if (Property(pluralEntity).Where(x => x.Title == entity.Title).Count() > 0)
{
// validate
}
Is there a way to do this?
Update
I've got this:
var prop = (DbSet<ReferenceType>) GetType().GetProperty(pluralEntity).GetValue(this, null);
if (prop.Where(x => x.Title == entity.Title).Count() > 0)
{
result.ValidationErrors.Add(new DbValidationError(nameof(entity.Title), nameof(entity.Title) + " must be unique."));
}
But because ReferenceType is an abstract class it cannot cast it at runtime.
I'd like to do something like this
var prop = (DbSet<typeof(entityEntry.Entity.GetType().Name)>)
But of course that's a variable and can't be passed in as a generic type
The only thing I can think of at the moment is writing a custom validation method, using the repository pattern.
First, create an interface which all your entities will implement
public interface IEntity
{
public string Title {get; set; }
}
Then create the repository:
public class Repository<TEntity> where TEntity: class, IEntity
{
private YourContext context = new YourContext();
private DbSet<TEntity> AppDbSet;
public Repository()
{
AppDbSet = context.Set<TEntity>();
}
//a couple of method to retrieve data...
public List<TEntity> GetAll()
{
return AppDbSet.ToList();
}
public IEnumerable<TEntity> Find(Func<TEntity, bool> predicate)
{
return AppDbSet.Where<TEntity>(predicate);
}
public TEntity Single(Func<TEntity, bool> predicate)
{
return AppDbSet.FirstOrDefault(predicate);
}
//Lastly, implement a validation method
public bool IsValid(TEntity entity)
{
if (AppDbSet.SingleOrDefault(x => x.Title == entity.Title) != null)
return false;
else
return true;
}
}
Use the repository as follow:
Repository<Course> courseRepository = new Repository<Course>();
Course course = new Course();
course.Title = "Boring course";
Console.WriteLine(courseRepository.IsValid(course));
Hope it helps.
I want to do this for all my Types Of AuditedEntity, but as we've told FH to ignore base abstracts, the code isnt getting hit. i dont want to do this for all my entities and then have someone forget when they add a new typeof<AuditedEntity>
public abstract class AuditedEntity : Entity ...
public class AuditedEntityMappings : IAutoMappingOverride<AuditedEntity>
{
public void Override(AutoMapping<AuditedEntity> mapping)
{
mapping.Where("DeletedById is null");
}
}
This post looked promising but that method is deprecated
Got it working thanks to some help on some reasonably complex expressions leading to the following extensions:
var model =
AutoMap.AssemblyOf<AuditedEntity>(new AutomappingConfiguration(databaseName))
.HideDeletedEntities();
...
public static class ReflectiveEnumerator
{
public static IEnumerable<Type> GetSubTypesOf<T>() where T : class
{
return Assembly.GetAssembly(typeof (T)).GetTypes()
.Where(myType => myType.IsClass &&
!myType.IsAbstract &&
myType.IsSubclassOf(typeof (T)));
}
}
public static class AutoPersistenceModelExtensions
{
public static AutoPersistenceModel HideDeletedEntities(this AutoPersistenceModel model)
{
var types = ReflectiveEnumerator.GetSubTypesOf<AuditedEntity>();
foreach (var t in types)
{
var mapped = typeof(AutoMapping<>).MakeGenericType(t);
var p = Expression.Parameter(mapped, "m");
var expression = Expression.Lambda(Expression.GetActionType(mapped),
Expression.Call(p, mapped.GetMethod("Where"),
Expression.Constant("DeletedById is null")), p);
typeof(AutoPersistenceModel).GetMethod("Override").MakeGenericMethod(t)
.Invoke(model, new object[] { expression.Compile() });
}
return model;
}
}
This question already has answers here:
How do I use reflection to call a generic method?
(8 answers)
Closed 8 years ago.
Given the following Interfaces:
interface IEntity
{
int Id{get;}
}
interface IPerson : IEntity
{
string Name{get;}
int Age{get;}
}
interface ITeacher : IPerson
{
string StaffId{get;}
}
interface IStudent : IPerson
{
string StudentId{get;}
string Courses{get;}
}
interface IRepository
{
T Get<T>(int id) where T : IEntity
}
I have the following classes in my namespace
public class EntityBase() : IEntity
{
int Id{get;set;}
}
public class Teacher : EntityBase, ITeacher{}
public class Sudent : EntityBase, IStudent{}
Currently I am implementing this IRepository as follows:
class Repository: IRepository
{
IDataContext Context{get;set;}
T Get<T>(int id) where T : EntityBase
{
if(typeof(T) == typeof(Teacher))
return Context.Get<ITeacher>(id);
if(typeof(T) == typeof(Sudent))
return Context.Get<ISudent>(id);
throw new Exception("Unknown Interface " + typeof(T).Name);
}
}
Is there a betterway of implementing this? Given that our Context has no knowledge of our data types (Teacher, Student), just its interfaces (ITeacher, IStudent).
Can something like this work?
class Repository: IRepository
{
T Get<T>(int id) where T : EntityBase
{
var MyInterface = FindInterface<T>();
return Context.Get<MyInterface>(id);
}
}
I think this will do:
class Repository: IRepository
{
IDataContext Context{get;set;}
T Get<T>(int id) where T : EntityBase
{
string[] interfaceList = new string[]
{ "ITeacher", "IStudent"};
Type interfaceType = null;
foreach (string s in interfaceList)
{
var types = typeof(T).FindInterfaces((x, y) => x.Name == y.ToString(), s);
if (types.Length > 0)
interfaceType = types[0];
}
if (interfaceType == null)
throw new Exception("Unknown Interface " + typeof(T).Name);
MethodInfo method = typeof(Context).GetMethod("Get");
MethodInfo generic = method.MakeGenericMethod(interfaceType);
var returnValue = generic.Invoke(Context, new object[] { id });
return (T)Convert.ChangeType(returnValue, typeof(T));
}
}
EDIT: As I don't know the name of your namespace, I have used the Name property to filter the interfaces. In real world usage I will suggest that you use FullName just to be sure, like this:
...
string[] interfaceList = new string[]
{ "MyNamespace.ITeacher", "MyNamespace.IStudent"};
...
var types = typeof(T).FindInterfaces((x, y) => x.FullName == y.ToString(), s);
I think you can accomplish this through reflection by finding the Get method on Context class, and invoking it as a generic call for the caller-supplied type T. I haven't tested this, but the code should look something like this:
T Get<T>(int id) where T : EntityBase
{
Type context = Context.GetType();
MethodInfo getMethod = context.GetMethod("Get", BindingFlags.Public);
MethodInfo genericGet = getMethod.MakeGenericMethod(new [] {typeof(T)});
return (T)genericGet.Invoke(Context, new object[] { id } );
}
It looks to me like you mean it the other way around. You don't want to pass interface types to Context.Get<>, do you?
// is this what you mean?
if (typeof(T) == typeof(ITeacher))
return Context.Get<Teacher>(id);
If it is, you'll need to use MakeGenericMethod, see this for an example (note the caching part).
If it is not, you might be misunderstanding some concepts of LINQ and/or Repository pattern.
However I'm curious why did you decide to use interfaces. LINQ objects are POCO anyways, why adding another layer of abstraction which involves (grrsh!) calling generic methods on DataContext via reflection?
A simple return Context.Get<T>(id) could be accomplished as following:
class Repository : IRepository
{
public IDataContext Context { get; set; }
public T Get<T>(int id) where T : IEntity, new()
{
return Context.Get<T>(id);
}
}
Following is your object/interface model with implementation for the Context
interface IEntity
{
int Id{get;}
}
interface IPerson : IEntity
{
}
interface ITeacher : IPerson
{
}
interface IStudent : IPerson
{
}
interface IDataContext
{
T Get<T>(int id) where T:new();
}
interface IRepository
{
T Get<T>(int id) where T : IEntity , new() ;
}
public class EntityBase : IEntity
{
public virtual int Id{get;set;}
}
public class Teacher : EntityBase, ITeacher {
int id=0;
public override int Id {
get { return this.id; }
set { this.id = value; }
}
}
public class Student : EntityBase, IStudent
{
int id=0;
public override int Id {
get { return this.id; }
set { this.id = value; }
}
}
class Context<T>: IDataContext where T: EntityBase, new()
{
ArrayList store;
public Context(int dataSize)
{
store = new ArrayList(dataSize);
for (int i = 0; i < dataSize; i++)
{
T t = new T();
t.Id = i;
store.Add(t);
}
}
public T Get<T>(int i) where T:new()
{
if (i<store.Count)
{
return (T)store[i];
}
else
{
return default(T);
}
}
}
Now finally the main method class to demonstrate that it all hangs together nicely.
using System;
using System.Collections;
class MyClass
{
static void Main(string[] args)
{
Context<Teacher> teachersContext = new Context<Teacher>(100);//contructs a db of 100 teachers
Context<Student> studentsContext = new Context<Student>(100);//contructs a db of 100 teachers
Repository repo = new Repository();
// set the repository context and get a teacher
repo.Context = teachersContext;
Teacher teacher1 = repo.Get<Teacher>(83); //get teacher number 83
Console.WriteLine("Teacher Id:{0} ", teacher1.Id);
// redirect the repositry context and now get a student
repo.Context = studentsContext;
Student student1 = repo.Get<Student>(35); //get student number 35
Console.WriteLine("Student Id: {0} ", student1.Id);
Console.ReadLine();
}
I wrote a class that allows a derivate to specify which of its properties can be lazy loaded. The code is:
public abstract class SelfHydratingEntity<T> : DynamicObject where T : class {
private readonly Dictionary<string, LoadableBackingField> fields;
public SelfHydratingEntity(T original) {
this.Original = original;
this.fields = this.GetBackingFields().ToDictionary(f => f.Name);
}
public T Original { get; private set; }
protected virtual IEnumerable<LoadableBackingField> GetBackingFields() {
yield break;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
result = field.GetValue();
return true;
} else {
var getter = PropertyAccessor.GetGetter(this.Original.GetType(), binder.Name);
result = getter(this.Original);
return true;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
LoadableBackingField field;
if (this.fields.TryGetValue(binder.Name, out field)) {
field.SetValue(value);
return true;
} else {
var setter = PropertyAccessor.GetSetter(this.Original.GetType(), binder.Name);
setter(this.Original, value);
return true;
}
}
}
And a derivate class:
public class SelfHydratingPerson : SelfHydratingEntity<IPerson> {
private readonly IDataRepository dataRepository;
public SelfHydratingDerivate(IDataRepository dataRepository, IPerson person)
: base(person) {
this.dataRepository = dataRepository
}
protected override IEnumerable<LoadableBackingField> GetBackingFields() {
yield return new LoadableBackingField("Address", () => this.dataRepository.Addresses.Get(this.Original.AddressID));
}
}
This works perfectly fine for getting and settings property values, but I get a either a RuntimeBinderException when I implicitly cast or an InvalidCastException with an explicitly cast SelfHydratingEntity back to T.
I know that you can override the DynamicObject.TryConvert method, but I'm wondering what exactly to put in this method. I've read a lot about duck typing today, and have tried out several libraries, but none of them work for this particular scenario. All of the libraries I've tried today generate a wrapper class using Reflection.Emit that makes calls to "get_" and "set_" methods and naturally use reflection to find these methods on the wrapped instance. SelfHydratingEntity of course doesn't have the "get_" and "set_" methods defined.
So, I'm wondering if this kind of thing is even possible. Is there any way to cast an instance of SelfHydratingEntity to T? I'm looking for something like this:
var original = GetOriginalPerson();
dynamic person = new SelfHydratingPerson(new DataRepository(), original);
string name = person.Name; // Gets property value on original
var address = person.Address; // Gets property value using LoadableBackingField registration
var iPerson = (IPerson)person;
- or -
var iPerson = DuckType.As<IPerson>(person);
Have you seen this Duck Typing project. It looks pretty good. I have just found a great example from Mauricio. It uses the Windsor Castle dynamic proxy to intercept method calls
Using the code from Mauricio the following code works like a dream
class Program
{
static void Main(string[] args)
{
dynamic person = new { Name = "Peter" };
var p = DuckType.As<IPerson>(person);
Console.WriteLine(p.Name);
}
}
public interface IPerson
{
string Name { get; set; }
}
public static class DuckType
{
private static readonly ProxyGenerator generator = new ProxyGenerator();
public static T As<T>(object o)
{
return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
}
}
public class DuckTypingInterceptor : IInterceptor
{
private readonly object target;
public DuckTypingInterceptor(object target)
{
this.target = target;
}
public void Intercept(IInvocation invocation)
{
var methods = target.GetType().GetMethods()
.Where(m => m.Name == invocation.Method.Name)
.Where(m => m.GetParameters().Length == invocation.Arguments.Length)
.ToList();
if (methods.Count > 1)
throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
if (methods.Count == 0)
throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
var method = methods[0];
if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
method = method.MakeGenericMethod(invocation.GenericArguments);
invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
}
}
impromptu-interface
https://github.com/ekonbenefits/impromptu-interface
Can static cast interfaces onto objects derived from DynamicObject.