Update derived entity with One-to-Many children - c#

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!

Related

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

Generic method to retrieve DbSet<T> from DbContext

I'm using the Entity Framework with a large database (made up of more than 200 tables).
Trying to create a generic method that returns the DbSet<T> of a specific table T (i.e. class, which can be TableA).
The entity class that was (automatically) created using the entity data model looks like so:
public partial class sqlEntities : DbContext
{
public virtual DbSet<TableA> TableA { get; set; }
public virtual DbSet<TableB> TableB { get; set; }
public virtual DbSet<TableC> TableC { get; set; }
... // other methods
}
My main class is like this
public class TableModifier
{
// Should return first 10 elements from a table of that type T
public IQueryable<T> GetFromDatabase<T>() where T : EntityObject
{
try
{
using (sqlEntities ctx = new sqlEntities())
{
// Get the DbSet of the type T from the entities model (i.e. DB)
DbSet<T> dbSet = ctx.Set<T>();
return dbSet.Take(10);
}
}
catch (Exception ex)
{
// Invalid type was provided (i.e. table does not exist in database)
throw new ArgumentException("Invalid Entity", ex);
}
}
... // other methods
}
I have to set a constraint where T : EntityObject on T to be within the EntityObject bounds. If there was no such constraint then the DbSet<T> dbSet would complain (i.e. T must be a reference type) that it might be getting more than it expects in terms of types (based on this).
The problem occurs when I try to actually call the method with a specific type.
[TestMethod]
public void Test_GetTest()
{
TableModifier t_modifier = new TableModifier();
// The get method now only accepts types of type EntityObject
IQueryable<TableA> i_q = t_modifier.GetFromDatabase<TableA>();
}
It gives an error:
There is no implicit reference conversion from 'TableMod.TableA' to
'System.Data.Entity.Core.Objects.DataClasses.EntityObject'.
How can I (cast?) the TableA type as an EntityObject if I know it exists for that entity model?
Though this is incorrect syntax (and logic) this is what I'm after:
t_modifier.GetFromDatabase<(EntityObject)TableA>();
How can I define the TableA (along with all the other 200 tables) type to be a part of EntityObject?
A potential solution
Turns out my constraint was too specific, all I needed to change was from where T : IEntity to
where T : class
So the T is what the DbSet<T> initially expected, a class type
Saves the trouble of having to add implementations to the 200+ table classes, TableA, TableB, ...
Then of course there's other problems such as changing the return type from IQueryable<T> to List<T> since the IQueryable would otherwise be returned outside of the scope of DbContext (i.e. sqlEntities) rendering it useless.
Why don't you try changing your constrain to class instead of EntityObject
public IQueryable<T> GetFromDatabase<T>() where T : class
I had the same requirement and solved it by using the following:
public static void GetEntitiesGeneric<TEntity>()// where TEntity : System.Data.Entity.Core.Objects.DataClasses.EntityObject <-- NO LONGER NEEDED
{
try
{
var key = typeof(TEntity).Name;
var adapter = (IObjectContextAdapter)MyDbContext;
var objectContext = adapter.ObjectContext;
// 1. we need the container for the conceptual model
var container = objectContext.MetadataWorkspace.GetEntityContainer(
objectContext.DefaultContainerName, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace);
// 2. we need the name given to the element set in that conceptual model
var name = container.BaseEntitySets.Where((s) => s.ElementType.Name.Equals(key)).FirstOrDefault().Name;
// 3. finally, we can create a basic query for this set
var query = objectContext.CreateQuery<TEntity>("[" + name + "]");
// Work with your query ...
}
catch (Exception ex)
{
throw new ArgumentException("Invalid Entity Type supplied for Lookup", ex);
}
}
The code was taken from Using Generics for Lookup Tables in Entity Framework and adapted for EF 6 using the DbContext (first part of the method where the objectcontext is extracted from the dbcontext
Hope it helps
I don't know how you have created your model, and thus how your entities look like. But, if it's Code First, the entity classes don't inherit from a common base class, so you cannot add a type constraint to your generic.
I don't recommend using a base class to be able to specify a constraint. It's much better to do it using an interface. An empty interface will allow you to specify a constraint without having to change your classes at all.
So, what you can do is define an interface like this:
public interface IEntity {};
And then:
implement it in all classes, which can be done in partial classes files, modifying a T4 template or in some other way depending on how your model looks like
use it to specify the generic type constrain with where IEntity
This is the cleanest way to do it, without any interference to your classes.
Issue
I suppose your TableA class doesn't implement EntityObject. That's why you're getting this error. To solve this you can have an abstract class/interface which will be base for all context entities (i.e. IContextEntity which will have unique Id definition):
public class TableA : IContextEntity
{
...
}
Then same method but with new interface instead of EntityObject and you can mock/test it easily
public IQueryable<T> GetFromDatabase<T>() where T : IContextEntity
{
...
}
Usage(Tests)
Second important thing is the way you want to use this method. In case of Entity Framework context it is really important to have separation between integration and unit tests. In code you provided you're trying to reach database which means that this test will be integration:
using (sqlEntities ctx = new sqlEntities()) // This will open a DB connection
Connecting to a databases or external sources is usually a bad practice unless you know what you do and this is exactly it. If you just need some fake/dummy data to perform an action on it - use Stubs.
For any future googlers, my colleague and I just hacked this out in Visual Basic (EF 6 version). It works for our use case of simply getting a list back out but will probably work for the other use cases. No try catch or checking in this one.
Private Class getList(Of T As Class)
Public Shared Function getList() As List(Of T)
Using ctx As New MVPBTEntities()
' Get the DbSet of the type T from the entities model (i.e. DB)
Dim dbSet = ctx.Set(Of T)()
Return dbSet.ToList
End Using
End Function
End Class

Why do Code First classes need navigation properties?

I have some domain classes that look something like this, that I want to model with Code First (in EF 4.3).
public class Foo {
// ...
}
public class Bar {
// ...
public Foo Foo { get; set; }
}
public class Baz {
// ...
public Foo Foo { get; set; }
}
In every example I see though, foreign object references are added in the Foo class. Can my Foo class be agnostic of the Bar and Baz class, or do I really need to do something like this?
public class Foo {
// ...
public virtual Bar { get; set; }
public virtual Baz { get; set; }
}
According to this answer, classes do need to have navigation properties. I'm new at Code First, so can anyone explain why this might be the case? Is there a way I can avoid polluting my Foo class like this by using the Fluent API?
It seems weird to me that Foo would need to know about every class that uses it. Is my design simply fundamentally flawed in some way?
Your problem here will be requirement for one-to-one relation. One-to-one relation in EF is mapped through primary keys. You choose principal entity and the dependent entity must have FK on its PK - they must have same PK value to be related. The reason is missing support for unique keys.
Once you accept this limitation you can simply use your model and map it like:
modelBuilder.Entity<Bar>()
.HasRequired(b => b.Foo)
.WithOptional();
modelBuilder.Entity<Baz>()
.HasRequired(b => b.Foo)
.WithOptional();
The other answer is partly correct.
If you want code-forst to bootstrap your database model with relationships between the tables you'll have to define at least in one class a navigation property.
The mapping will of course also work without the relationship, but you won't have the constraints on the database/sql level. Unless you add them with migrations or some other sql-scripts.
Though in your example I am not quite sure what kind of relationship you're trying to define anyhow. Is that supposed to be a one-to-one relationship?
In that case, Foo doesn't need to know about any other class that has a reference to it, as answered in your linked question, only one class needs to have it.

Using Reflection to Upsert Derived Entities in Generic Repository

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.

Testing Data Model in project that uses NHibernate for persistence

I'm still new with NHibernate, so correct me if I'm wrong on any of this.
When you are using NHibernate to build collections of objects from database records, NH takes care of instantiating your collections and populating them.
How do you instantiate your ISet collections when you are writing tests that don't actually use NH?
You can instantiate a field by using the constructor or by instantiating the field directly in the declaration. Classes mapped with NHibernate can be persistence ignorant.
public class MyEntity
{
private readonly ISet<ChildEntity> children;
public MyEntity()
{
children = new HashedSet<ChildEntity>();
}
public IEnumerable<ChildEntity> Children
{
get { return children; }
}
public void AddChild(Child child)
{
children.Add(child);
}
}
Assuming you're testing something other than the data layer itself (like your domain logic, for example), you can simply create the objects yourself and populate them with test case data. Or you could be really sneaky and create a database with all your test cases.

Categories

Resources