Using Reflection to Upsert Derived Entities in Generic Repository - c#

A bit new at the Repository Pattern. Trying to build a generic repository that will also handle state changes to derived entities. What I've done so far is create a custom attribute to flag the property as one which needs to be upserted.
Attribute:
public class DerivedObjectAttribute : Attribute
{
public enum EntityType
{
REFERENCE,
OBJECT
}
public EntityType DerivedType { get; set; }
}
I define this attribute for any property on which I want to cascade state changes.
Sample Entity:
public class FightingCharacter : BaseEntity
{
public Costume costume { get; set; }
[DerivedObject(DerivedType=DerivedObjectAttribute.EntityType.REFERENCE)]
public SpecialFinish specialFinish { get; set; }
[DerivedObject(DerivedType = DerivedObjectAttribute.EntityType.REFERENCE)]
public List<MoveList> moveList { get; set; }
}
So for this class, the costume property would not need to cascade, but the specialFinish and moveList properties should.
Then in my repository:
public class DataRepository<T> : IRepository<T> where T : BaseEntity {
private void TryDerivedUpsert(T entity)
{
Type type = entity.GetType();
PropertyInfo[] piList = type.GetProperties();
foreach (PropertyInfo pi in piList)
{
foreach (DerivedObjectAttribute attr in pi.GetCustomAttributes(typeof(DerivedObjectAttribute), false))
{
// What to do here?
}
}
}
}
In the innermost loop, I'm able to pinpoint DerivedObjectAttributes without any problem. The question is: how do I obtain the Type and Value of the object, then upsert it? In other words: if property pi is flagged to cascade changes, create a repo cast to the appropriate Entity, and Upsert it. E.G.:
DataRepository<EntityType> repo = new DataRepository<EntityType> ();
repo.Upsert(property as EntityType);
Does that make sense? Or am I going about generic repo entirely the wrong way? If it does make sense (I'll be surprised), how to do it? (The examples listed here are just examples, BTW. I'm still architecting and have no EF classes at all yet.)

You could get the value with pi.GetValue(entity, null), and create the generic repository (Activate.CreateInstance) for the property type (from pi), but you will have to do a lot of reflection.
In this case you should think about dropping the classic generic repository idea (separate repository per type) and use something like an extended DbContext, that can handle all types.
If you have a disconnected scenario (WCF), the main problem will be EF itself, because you have to replicate all changes to nested lists on the server side and manually change the EntityState.

Related

Implement IList<ISomeInterface> with List<SomeObjectThatImplementsISomeInterface>

I have a set of interfaces using each others like this:
public interface IModel
{
string Name { get; }
IModelParameters Parameters { get; }
}
public interface IModelParameter
{
int Value { get; }
}
public interface IModelParameters: IList<IModelParameter>
{
void DoSomething();
}
And to implement those interfaces, I have defined those classes:
public class Model: IModel
{
string Name { get; internal set; }
public ModelParameters Parameters { get; private set; }
IModelParameters IModel.Parameters { get { return Factors; } }
}
public class ModelParameter: IModelParameter
{
int Value { get; internal set; }
}
public class ModelParameters: List<ModelParameter>, IModelParameters
{
void DoSomething()
{
// actual code
}
}
This does not compile because List<ModelParameter> implements IList<ModelParameter> and not IList<IModelParameter> as required by IModelParameters
Changing ModelParameters to be List<IModelParameter> fixes the compilation but it breaks Entity Framework migration generation because it no longer recognizes the list as a navigation property because the type parameter is an interface, not a regular class.
I could also have ModelParameters not implement IModelParameters and declare a second class that gets instantiated and filled directly in the IModelParameters.Factors getter inside Model
But this feels inefficient as it effectively creates two instances of the same list, one for Entity framework and a temporary one for use by the rest of the application. And because this temporary is filled at runtime, it introduces another potential point of failure.
This is why I'm trying to find a way to express the fact List<ModelParameter> implements IList<IModelParameter> just fine because ModelParameter implements IModelParameter itself.
I have a feeling that covariance/contravariance might be of help here, but I'm not sure how to use that.
You cannot do this. It it was possible to cast a List<ModelParameter> to IList<IModelParameter> you could try adding a object of another type to the list, i.e. class MyOtherModelParam : IModelParameter. And that is a contradiction since the type system guarantees that the list only contains ModelParameter objects.
You could replace it with IReadOnlyList<T>, since this interface do not expose any add or set methods it is safe to cast a List<ModelParameter> to IReadOnlyList<IModelParameter>.
Another possible solution would be to just remove the interface. If you intend to have only one implementation of IModelParameter, the interface serves little purpose, and you might as well just remove it.

Generic Collections of unknown (sub-class) types

I have a base abstract class (baseEntity), derived from an Interface. From this, I have many sub-classes - each actually represents a database table for NHibernate. Some sub-classes contain properties that are other baseEntity classes, and some contain properties that are lists of other baseEntity classes. For some of these list properties I converted from using ILIst/List to ISet/HashSet, to stop an NHIbernate exception seen after upgrading from an ancient version
Cannot simultaneously fetch multiple bags
My question is not to do with NHIbernate
:
public interface INhEntity;
public abstract class BaseEntity;
public class OrderItems : baseEntity
{
// properties
}
public class Order : baseEntity
{
public virtual ISet<OrderItems> OrderItems { get; set;}
public Order()
{
OrderItems = new HashSet<OrderItems>();
}
}
We use ISet/HashSet because this seemed an easy and safe way to get rid of the multi-bag problem (The HashSet will quietly stop duplicates)
We also use messaging to send data between sites. Basically, each baseEntity is serialized. At the other end, we deserialize, use PropertyInfo, Type details etc to reconstruct the baseEntity sub-class
Referring to the Order/OrderItem classes above, I need to create/access/get access to the OrderItem property. This is where the fun arrives:
When I am processing a Set property, I try accessing as per below:
private void ProcessSet(object entity, ListItem listItem, PropertyInfo property)
{
//entity is an instance of the Order entity
//listitem contains info I will use to update the order item
//property is the OrderItems property. It has a vale of:
//ISet`1[Full.NameSpace.OrderItem] OrderItem}
var setInstance = GetOrCreateSetInstance(entity, property);
...processing
setInstance.Add(AnOrderItemInstance); //NHibernate knows to link the 2 database tables (OrderItems class does not need/have a FK)
}
private static ISet<baseEntity> GetOrCreateSetInstance(object entity, PropertyInfo prop)
{
var listInstance = prop.GetValue(entity, null); // This looks just what I want
var result = (ISet<baseEntity>)listINstance //cannot cast type OrderItem to baseEntity
return result
}
I have similar code to deal with lists. Because I can return a non-generic Ilist, this works a treat. Prior to upgrading NHibernate, I was using the Iesi Isets, so my GetOrCreateINstance had this signature:
private static Iesi.Collection.ISet GetOrCreateSetInstance(object entity, PropertyInfo prop)
Is there anyway I can have a method to return the various subclass instances. Note I have tried returning HashSet<baseEntity>, Iset/HashSet<INhEntity>
I have tried Casting in various methods, but usually end up with '.. is a variable but is used like a type',
I actually don't think it's possible, but any feedback most welcome

Built in way to get context from model Entity Framework Core

I have many different Model types, each with their own context. Is there a built in mechanism to get the Models context without explicitly writing a getter/helper method? I.e. is there a way to link the context with the Model itself and then have a built in method to fetch the context.
Trying to reduce the redundant code I have to write without writing something generic and unsafe, like based on class names (strings) or something.
Thanks
EDIT:
I have many classes like this:
public class CatContext : DbContext
{
public DbSet<Cat> Cat{ get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=animals.sqlite");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Cat>();
}
}
public class Cat
{
public string name { get; set; }
}
I want to write something generic, like:
public static void AddModel<T>(List<T> entries)
{
// TODO: Get the context for the type T
using (var context = GetModelContext<T>())
{
// do something generic, e.g. add them to the DB
}
}
But I dont want to have write a method to fetch the context in a generic way. The models aren't always related to each other either.
Not sure why you would want to do this, but the only way I could think to make something like this work is with a factory and probably reflection. IDbContextFactory. It would have to return a generic DbContext and the factory would need to know all of the possible DbContexts type to scan through. When given the model type, you'd have to go through that list and see if you could find a generic db set that implements that type. If yes, you'd have to use reflection to instantiate a new instance of that DbContext, cast it to DbContext and return it. If you didn't use reflection, you'd have to build that mapping by hand via types. Or, you'd have to build up a dictionary of ModelType (key) and DbContext type. Either way, I think you'd have to use reflection unless you want to manually wire that check up yourself in the factory directly.
Example Code:
public class DbContextFactory : IDbContextFactory
{
private List<Type> _contextTypes = new List<Type>{ typeof(CatContext), etc };
public DbContext GetContext<T>()
{
foreach (var t in _contextTypes)
{
var props = t.GetProperties();
foreach (var prop in props)
{
//pseudo code
if (prop.PropertyType is DbSet<> && prop.GetGenericTypes()[0] == typeof(T))
{
return Activator.CreateInstance(t) as DbContext;
}
}
}
return null;
}
}

c# given instance of base class, update base class properties of derived class

I have a view model (using MVC4) that inherits a base class
public class TimeTaskViewModel : TimeDetailTask
{
public string TaskTypeDescription { get; set; }
}
I have a method to convert view model to class, given an instance of that class. I'd like to be able to something like this so as to not have to explicitly set every property of the base class, then simply update the additional properties of the view model:
public TimeTaskViewModel ConvertClassToViewModel(TimeDetailTask entity)
{
TimeTaskViewModel viewModel = new TimeTaskViewModel();
viewModel.base = entity;
viewModel.TaskTypeDescription = entity.TaskTypes.TaskTypeDescription;
return viewModel;
}
Anyway to do that? Or am I way off base here?
Note: I need a flat class as a result as the view Model will also be used in an IEnumerable format to pass to a Kendo UI Grid on the front end and it can't handle complex classes.
No, it can't be done exactly as you are asking. Here are some options:
1: Change your TimeTaskViewModel class to contain a TimeDetailTask instead of extend it.
public class TimeTaskViewModel
{
public TimeDetailTask TimeDetailTask { get; set; }
public string TaskTypeDescription { get; set; }
}
2: Create a TimeTaskViewModel(TimeDetailTask) constructor and copy the properties manually there.
public class TimeTaskViewModel : TimeDetailTask
{
public string TaskTypeDescription { get; set; }
public TimeTaskViewModel(TimeDetailTask baseTask)
{
this.SomeProperty = baseTask.SomeProperty;
// and so on
}
}
3: Use AutoMapper, reflection, or something similar to copy properties from one to the other.
Mapper.CreateMap<TimeDetailTask, TimeTaskViewModel>();
// Perform mapping
TimeTaskViewModel viewModel =
Mapper.Map<TimeDetailTask, TimeTaskViewModel>(baseTask);
It sounds like you're looking for easy way for mapping information from one class to an entirely different class that happens to have some of the same properties. I don't think inheritance is the answer you're looking for. It sounds like you want something like AutoMapper, which will allow you to set up rules for mapping properties from one object to another.
For instance, if you're trying to map from your view model to a database entity before writing to the database, you'd do something like:
var entity = Mapper.Map(viewModel);
For straight up matches in property names, AutoMapper would already take care of it, such as for instance copying TimeTaskViewModel.TaskTypeDescription to YourEntity.TaskTypeDescription. In the case where the name isn't a perfect match or you need to do some manipulation of the data (such as casting a string to an int), though, you can set up rules for it in a map file.
In my opinion, AutoMapper is a great tool when you're using it to copy things from a class with one property name to another class with the exact same property name. It's still an okay tool when you need to copy the same datatype between differently named properties, or perform really simple conversions (such as int to string). It starts to feel like more trouble when it's worth if you are doing complex conversions, though (mainly because I find it difficult to debug and unit test the mapping files), at which point it often feels like you should just write your own mapping function. Of course, nothing says you can't use it for the simple cases and roll your own mapping function for the more complex ones.
Try this
//Create a property for TimeDetailTask in TimeTaskViewModel class
public class TimeTaskViewModel : TimeDetailTask
{
public string TaskTypeDescription { get; set; }
public TimeDetailTask TimeDetailTaskProperty { get; set; }
}
//Then you assign the entity values to modelclass TimeDetailTaskProperty
public TimeTaskViewModel ConvertClassToViewModel(TimeDetailTask entity)
{
TimeTaskViewModel viewModel = new TimeTaskViewModel ();
viewModel.TimeDetailTaskProperty =entity;
return viewModel;
}
//finally you can get values from this TimeDetailTaskProperty using TimeTaskViewModel object.

Update derived entity with One-to-Many children

I have these POCO classes, they're mapped using Fluent API with a TPT (Table per Type) strategy.
public class Base
{
...
}
public class Derived : Base
{
...
public virtual ICollection<Foo> Foos {get; set;} // One-to-Many
}
public class Foo
{
...
public virtual ICollection<Bar> Bars {get; set;} // One-to-Many
}
public class Bar
{
...
}
My repository looks like this.
public class Repo
{
public void Update(Base item)
{
using (var ctx = new DbContext())
{
ctx.Entry(item).State = EntityState.Modified;
ctx.SaveChanges();
}
}
}
Action:
public void DoStuff()
{
Derived item = repo.GetById(1);
item.SomeProp = "xyz"; // new value
item.Foos = GenerateFoosWithBars(); // change children
repo.Update(item);
}
To my surprise Update actually works if I'm only updating the Base or Derived classes. However things turn ugly when I try to update the One-to-Many relations. I found a tutorial on how to Update One-to-Many Entities in EF4. I was really expecting EF to be way smarter then this, I mean I have to do it manually... that's so unlike everything else in EF.
So I started out trying to use Entry cause I wanted it to be generic (being able to update any Base derived class) using Entry.OriginalValues to avoid having to write a query myself. But now shit really hits the fan! Entry.OriginalValues fails with an exception saying that DbSet<Derived> doesn't exists. It's totally right, it doesn't. But it shouldn't as the the Derived is mapped to DbSet<Base> via inheritance.
Clearly I must be doing something wrong or something so different from everyone else as I'm unable to find anything useful on the matter. Haven't EF5 improved on this in anyway?
Any suggestions on how I could approach this problem?
Firstly, I think an Update method is not necessary in the Repository since EF tracks changes and applies then when you call SaveChanges() on the context.
Secondly, the problem might be that you're assigning a new collection to the Foos poperty when yo do: item.Foos = GenerateFoosWithBars(); You shouldn't do that since when EF materializes an object of the Derived type it actually returns a proxy which overrides the virtual Foos collection to use a special kind of lazy loaded collection that it tracks. If you assign a different collection of your own that will not be bound to the context. (I don't think that EF will handle that very well). What you should do is modify the collection items not the collection itself! Hope it helps!

Categories

Resources