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.
You can get DbSet from DbContext by Type using the method DbContext. Set(Type entityType) . So if you have the model class name as string you should do some mapping to actual clr type.
Definition. A DbSet represents the collection of all entities in the context, or that can be queried from the database, of a given type. DbSet objects are created from a DbContext using the DbContext.
Intuitively, a DbContext corresponds to your database (or a collection of tables and views in your database) whereas a DbSet corresponds to a table or view in your database. So it makes perfect sense that you will get a combination of both!
In Entity Framework Core, the DbSet represents the set of entities. In a database, a group of similar entities is called an Entity Set. The DbSet enables the user to perform various operations like add, remove, update, etc. on the entity set.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With