Is there a way to call generic repository methods on an entity that was generated at run time with codeDOM?

594 views Asked by At

I am using codeDOM to generate my entity classes at run time. I also have a generic repository to deal with various DB functionality. Here is the Insert method as an example method in my generic repository:

public void Insert<TEntity>(TEntity entity) where TEntity : class, IBusinessEntity
{
    if (entity == null)
    {
        throw new ArgumentNullException("entity");
    }

    TEntity existing = Existing(entity);
    if (existing == null)
    {
        _context.Set<TEntity>().Add(entity);
        this._context.SaveChanges();
    }
}

Here is some example code of how I generate an entity class and how to create an entity based on that entity class using codeDOM:

//Generate the fields of the new entity class
EntityGenerator.EntityFieldInfo entityField1 = new EntityGenerator.EntityFieldInfo("Name", typeof(string), RelationshipType.NoRelation);
EntityGenerator.EntityFieldInfo entityField2 = new EntityGenerator.EntityFieldInfo("Shape", typeof(string), RelationshipType.NoRelation);
ICollection<EntityGenerator.EntityFieldInfo> entityFieldList = new List<EntityGenerator.EntityFieldInfo> { entityField1, entityField2 };

// Create the new entity class using the fields established above 
// as well as the name of the entity (typeName = "Thing")
string typeName = "Thing";
EntityGenerator.CreateEntityClass(entityFieldList, typeName);
CompilerResults results = EntityGenerator.GetCompiledEntity(typeName);

// Create an entity instance based on the new entity class that was just created
Object newThing = EntityGenerator.CreateInstanceOfEntity(results, typeName);
SetObjectField(newEntity, "Name", "Box");
SetObjectField(newEntity, "Shape", "Cuboid");

As you can see, newThing (the new entity instance) is an Object type. If this was a hardcoded entity class then I could just say

Thing newThing;

But the Thing entity created by CodeDOM isn't a hardcoded class so I have to use type Object instead of type Thing. This is a problem because I am using a generic repository. Let's say I want to insert this entity into the database. I would like to call:

myRepository.Insert<Thing>(newThing);

However, Thing was just created by CodeDOM at run time, so it isn't a class, which means it can't go inside the <>. You may have noticed above in my Insert method, TEntity is also an IBusinessEntity. If I try

myRepository.Insert<IBusinessEntity>(newThing);

I get the error:

Argument type 'object' is not assignable to parameter type 'Models.IBusinessEntity'

If I try without anything inside the <>, like this:

myRepository.Insert(newThing);

I get the error:

The type 'object' must be convertible to 'Models.IBusinessEntity' in order to use it as a parameter 'TEntity' in the generic method 'void Insert(TEntity)'.

Does anyone know how I can reconcile this codeDOM generated entity with a generic repository? Could reflection help? It would be nice if reflection could somehow give me a class of Thing which could be passed into the <>. Also I should note that all entities I create with CodeDOM extend IBusinessEntity.

1

There are 1 answers

1
bubi On BEST ANSWER

I think it will be hard to make it work because the DbSets contained in the DbContext are used by EF to create mappings. How do you think to create them?

Anyway, you don't need the type to work with EF, you can often use GetType. In your methods (Existing(.) is missing but I think is similar) you can use

public void Insert(object entity)
{
    if (entity == null)
        throw new ArgumentNullException("entity");

    if (!(entity is IBusinessEntity))
        throw new ArgumentInvalidException("entity is not an IBusinessEntity");

    object existing = Existing(entity);
    if (existing == null)
    {
        _context.Set(entity.GetType()).Add(entity);
        this._context.SaveChanges();
    }

}

Using Set<> or Set(.) I'm quite sure that EF will search in mappings created starting from DbSets contained in DbContext. I can't remember the exact exception but I sow it different times (when I used DbContext.Set(myEntityType)).