Get DbContext's DbSet<T> where T is variable - c#

I have a DbContext looking somewhat like this:
class MyDbContext: DbContext
{
public DbSet<Class1> Set1 {get;set;}
public DbSet<Class2> Set2 {get;set;}
...
}
where Class1, Class2 ... : BaseClass
The thing is I'm reading data from xml and I use a dictionary that looks like this:
public class XmlNode
{
public Func<BaseClass> CreateEntity { get; set; }
...
}
public static Dictionary<string, XmlNode> Nodes = new Dictionary<string, XmlNode>()
{
["Tag1"] = new XmlNode()
{
CreateEntity = () => new Class1(),
}
...
}
And then I have to compare the read entity to an existing table and maybe add it. But I can't find a way to get the approptiate table without making a different function for every Class I have. Is there a way to get a DbSet where Class is a variable?

The DbContext your data context derives from has a method called Set(Type t) that accepts a type, type can be created from string.
For the scenario you've described you can create a DbSet from the string in your XML by
var typeName = "Some.Namespace.AndType";
DbSet t = Set(Type.GetType(typeName));
Note that you can't use linq or lambda expressions to query the resulting object, unless you cast it to a typed DbSet<T> or use a library like System.Linq.Dynamic which would allow you to call t.AsQueryable().Where("SomeProperty == #value");, but this should get you started.

From how to check whether dbcontext sett exists in model
you can just check if it exist then get DBset after checked
if(Exists<yourentity>())
{
... TEntity exist
}
from link
public bool Exists<TEntity>() where TEntity : class
{
string entityName = typeof(TEntity).Name;
ObjectContext objContext = ((IObjectContextAdapter)this).ObjectContext;
MetadataWorkspace workspace = objContext.MetadataWorkspace;
return workspace.GetItems<EntityType>(DataSpace.CSpace).Any(e => e.Name == entityName);
}

Related

How to get dynamic IQueryable type

I have code like this in a base class which the type of its derived class is known. I would like to know if it is possible to apply IQueryable<DerivedType>.
I would like to know if I could switch out XXX for the type of class known by my EntityType variable. How could this be done with the result of picking the right entity table from dbEntities
BaseClass.cs
protected abstract Type EntityType {get;}
public virtual DataSourceResult populate([DataSourceRequest]DataSourceRequest request)
{
using (var dbEntities = new dbEntities())
{
IQueryable<XXX> entityResult = dbEntities.XXX;
DataSourceResult result = entityResult.ToDataSourceResult(request);
return result;
}
}
Example:
I want to get a IQueryAble<> of the type Type EntityType and also have to pick this type from dbEntities. So say I have a table in dbEntities called giraffe which is inherited from animal. Then the current code above belongs to the animal base class. EntityTpe knows that EntityType.Name is equal to giraffe. So I would the the with XXX to output in equivalence to IQueryable<giraffe> entityResult = dbEntities.giraffe
You can use generics for this. You will also need to use the DbSet<T> property of your context. Something like this should do:
public virtual DataSourceResult populate<T>([DataSourceRequest]DataSourceRequest request)
{
using (var dbEntities = new dbEntities())
{
IQueryable<T> entityResult = dbEntities.DbSet<T>();
DataSourceResult result = entityResult.ToDataSourceResult(request);
return result;
}
}
And call it like this:
var result = populate<MyEntity>(request);

How to query all tables that implement an interface

I have implemented an interface for some of my entity classes:
public partial class Order : IReportable
{
public string TableName { get { return "Order"; } }
}
public partial class Client: IReportable
{
public string TableName { get { return "Client"; } }
}
public interface IReportable
{
string TableName { get; }
}
Then I added this to the DbContext:
public virtual DbSet<IReportable> IReportable { get; set; }
When I try to query all the tables that implement this interface (as shown here):
var result = from reportabletable in db.IReportable
where reportabletable.TableName == table_name
select reportabletable
I get the following exception:
The type 'Report.DataAccess.IReportable' was not mapped. Check that
the type has not been explicitly excluded by using the Ignore method
or NotMappedAttribute data annotation. Verify that the type was
defined as a class, is not primitive or generic, and does not inherit
from EntityObject.
I would go for something like this:
Create this extension method
public static class DbContextExtensions
{
public static IEnumerable<T> SetOf<T>(this DbContext dbContext) where T : class
{
return dbContext.GetType().Assembly.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface)
.SelectMany(t => Enumerable.Cast<T>(dbContext.Set(t)));
}
}
And use it like this:
using (var db = new dbEntities())
{
var result = from reportabletable in db.SetOf<IReportable>()
where reportabletable.TableName == table_name
select reportabletable
}
EF doesn't like mapping interfaces directly to tables. You can get around this by making using a generic Repository, as outlined Here!
Then use repository method and supply the Type of the table(s) you want to query. Something like: myRepo.GetAll<myClient.GetType()>();
Get the classes that inherit that interface and run the query for all of them:
var types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes().Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)));
foreach (var mytype in types)
{ // aggregate query results }
Hope this helps! There is probably a more graceful solution
First of all MarcGravell comment is on the money. Its up to you to know which table to query.
Personally I go through list of poco types that implement an interface or have an custom attribute. But if you are keen to go via the DBContext only, here are some extensions that give you access to the "names". You will still need to access that part of the context afterwards one at a time.
Again you can do that via generics, but you can just go directly as you suggest.
You will need to iterate of a list of types.
eg:
ReportRespository : BaseRespository where t : IReport
Check the assembly for Certain types and attributes
eg
/// <summary>
/// POCOs that have XYZ Attribute of Type and NOT abstract and not complex
/// </summary>
/// <returns></returns>
public static List<Type> GetBosDirDBPocoList() {
var result = new List<Type>();
// so get all the Class from teh assembly that public non abstract and not complex
foreach (var t in Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.BaseType != null
&& t.IsClass
&& t.IsPublic
&& !t.IsAbstract
&& !t.IsComplexType()
&& t.GetMyAttribute() != null)) {
result.Add(t);
}
}
return result;
}
public static GetMyAttribute(this Type T) {
var myAttr= T.GetCustomAttributes(true)
.Where(attribute => attribute.GetType()
.Name == "XYZAttr").Cast<BosDir>().FirstOrDefault();
return myAttr;
}
Extensions
public static class DalExtensions {
// DbSet Names is the plural property name in the context
public static List<string> GetModelNames(this DbContext context) {
var propList = context.GetType().GetProperties();
return GetDbSetNames(propList);
}
// DbSet Names is the plural property name in the context
public static List<string> GetDbSetTypeNames<T>() where T : DbContext {
var propList = typeof (T).GetProperties();
return GetDbSetNames(propList);
}
// DBSet Types is the Generic Types POCO name used for a DBSet
public static List<string> GetModelTypes(this DbContext context) {
var propList = context.GetType().GetProperties();
return GetDbSetTypes(propList);
}
// DBSet Types POCO types as IEnumerable List
public static IEnumerable<Type> GetDbSetPropertyList<T>() where T : DbContext {
return typeof (T).GetProperties().Where(p => p.PropertyType.GetTypeInfo()
.Name.StartsWith("DbSet"))
.Select(propertyInfo => propertyInfo.PropertyType.GetGenericArguments()[0]).ToList();
}
// DBSet Types is the Generic Types POCO name used for a DBSet
public static List<string> GetDbSetTypes<T>() where T : DbContext {
var propList = typeof (T).GetProperties();
return GetDbSetTypes(propList);
}
private static List<string> GetDbSetTypes(IEnumerable<PropertyInfo> propList) {
var modelTypeNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.PropertyType.GenericTypeArguments[0].Name)
.ToList();
return modelTypeNames;
}
private static List<string> GetDbSetNames(IEnumerable<PropertyInfo> propList) {
var modelNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.Name)
.ToList();
return modelNames;
}
}
}
Accepted solution does not work in EF Core.
Here is my first working draft
public IEnumerable<T> SetOf<T>() where T : class
{
var firstType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
.FirstOrDefault(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface);
if (firstType == null) return new List<T>();
var dbSetMethodInfo = typeof(DbContext).GetMethod("Set");
var dbSet = dbSetMethodInfo.MakeGenericMethod(firstType);
IQueryable<T> queryable = ((IQueryable)dbSet.Invoke(this, null)).Cast<T>();
return queryable.ToList().Cast<T>();
}
Then you could use like this
_dbContext.SetOf<ISomeInterface>();
More info here Expose method DbContext.Set(Type entityType)

Dynamically creating objects in LINQ expression that inherit from abstract base class

I have an abstract class:
public abstract class Entity<T> where T : Tweet
{
protected string _name;
public abstract string Name { get; set; }
public abstract List<Tweet> tweets { get; set; }
}
Which is sub-classed by several different classes (their names are Person, Place and Organisation). I store lots of instances of the Entity class in a List<T> instance.
I have a separate method that is used to combine different List<Entity<Tweet>> instances into one another, joining them on Entity.Name and merging their containing List<Tweet>:
private static List<Entity<Tweet>> joinLists(List<Entity<Tweet>> list1, List<Entity<Tweet>> list2)
{
List<Entity<Tweet>> joined;
joined = list1.Union(list2)
.GroupBy(o => o.Name)
.Select(o => new Entity<Tweet> // this is wrong as class is abstract but at this point I don't know what concrete class o is?
{
Name = o.Key,
tweets =
o.SelectMany(x => x.tweets).ToList()
}).ToList();
return joined;
}
Unfortunately as Entity is an abstract class, I cannot create an instance of it, so how can I dynamically determine the actual concrete class of the o instance in the LINQ expression. I have a separate method that does this based on a string representation of an object's class name, but I would like to know how this would be achieved with LINQ (method below is my implementation for use elsewhere in my application):
private static Entity<Tweet> createEntity(string className, Tweet tweet, string value)
{
Entity<Tweet> entity = null;
Type t = Type.GetType("FinalUniProject.NERModels." + className);
entity = (Entity<Tweet>)Activator.CreateInstance(t);
entity.Name = value;
// Allow for inverted index by adding tweet to NamedEntist List<TweetModel>
if (entity.tweets == null) entity.tweets = new List<Tweet>();
entity.tweets.Add(tweet);
return entity;
}
Line of code below do not give any compile error for sure. Haven't checked all aspects. U sure this wont work ?
joined = list1.Union(list2)
.GroupBy(o => o.Name)
.Select(o =>
{
Entity<Tweet> entity = null;
string className = o.First().GetType().Name;
Type t = Type.GetType("FinalUniProject.NERModels." + className);
entity = (Entity<Tweet>)Activator.CreateInstance(t);
entity.Name = o.Key;
// Allow for inverted index by adding tweet to NamedEntist List<TweetModel>
entity.tweets = o.ToList().SelectMany(x => x.tweets).ToList();
return entity;
}).ToList();

Entity Wrapper - Custom

I would like find a workaround to accomplish a simple solution in order to automatize certain operation through EF.
What I need it's takeover during saving and retrieving process to modifying query result, but this class will be able to make that work for any type entities.
Example: I have a MyTestDb. So in my C# project I create a new entity model (MyTEstDbModel.edmx), with relative POCO class generation.
Well, a point of interest could be implementing a new custom class like following:
class Example
{
private ObjectContext _context;
private Example(ObjectContext obj) { _context = obj; }
public void Store(ObjectSet<???generic???> os)
{
// problem here: I dont't know the type contained in ObjectSet
// but if I Knew its type, I could make a work like this:
// -> foreach every instance in objectSet to check if exist some property
// via reflection, if i found them, then I set always the same values.
// Why this? Because all my db contains some common filed
// like (createdByUser, TimeToUpdate, and so on..), so it wold be boring
// setting all those fileds from any point of program.
}
public void Retrive(ObjectSet<???generic???> os)
{
// here problem too: all my queries will be filtered by one or more value
// fileds, but I cannot use lambaExpression cos I don't Know the type
// contained in objectSet<..>
}
//....
finally, by any point of program, the code should appear like following:
Example obj = new Example(myEntityContext); //-> global
var result = myEntityContext.ObjectSetTyped.Where(..lambaExpression..condition)
result.someProperty = "...";
obj.Store(result); // store method will fill all other boring filed automatically.
Can anyone give me some tips, help, suggestion about my issue?
Thanks in advance...
Update
Now, just only another problem. I'd to filter my ObjectSet through retrieve method like following:
public void Retrieve<TEntity>(IQueryable<TEntity> ooo) where TEntity : IC
{
ooo = ooo.Where(p => p.Filed == "MyDefaultValue");
}
But, from external method, not objectSet result is affect by my filter.
How so..?
MyEntities ent = new...
MyWrapper wrap = new MyWrapper();
wrap.Retrieve(ent.Users);
//problem here -> users objectSet is always the same..
Define interfaces which will allow you to do this. For example:
public interface IEntity
{
DateTime CreatedAt { get; set; }
string CreatedBy { get; set; }
}
You need to "implement" this interface in your entities. You can for example either modify T4 template generating entities or implement it in partial class. Both properties must be already defined in the model so the implementation is only declarative:
public partial class MyEntity : IEntity // That's all
{ }
Now you can define Store like:
public void Store<TEntity>(TEntity entity) where TEntity : IEntity
{
...
}
Same can be done with query but you can for example define custom extension method:
public static IQueryable<TEntity> GetUserEntities(this IQueryable<TEntity> query, string user)
where TEntity : IEntity
{
return query.Where(e => e.CreatedBy == user);
}
You will simply define your query like:
var result = myEntityContext.MyEntities.GetUserEntities("someName");
Other approach is defining simply GetQuery on your custom context:
public IQueryable<T> GetQuery<T>() where T : IEntity
{
var query = GetObjectSetSomehow;
return query.ApplyGlobalConditions(); // Just another extension with your conditions
}
I'm not a big fan of the repository pattern but generally what you are trying to do is close to generic repository so check for example this post. It is just some example which can be further extended.
yeah, I just want a generic approach, likewise I realized with dataset and datatable.. but it seems impossible to achieve..
..ehmm..however, let me show yuo following code snippet, dynamic keyword looks like something to hopeful.... maybe I'm colse to solution..?
public ObjectSet<dynamic> Retrieve(string entity, string context)
{
Type myObjectContextType = Type.GetType(context);
ConstructorInfo cs = myObjectContextType .GetConstructor(new Type[] { });
dynamic myObjContext = cs.Invoke(new object[] { });
Type t = Type.GetType(entity);
ConstructorInfo xi = t.GetConstructor(new Type[] { });
dynamic UserEntity = xi.Invoke(new object[] { });
!problem here!
ObjectSet<?????> os = myObjContext.UserEntity.Where(...)
return ...
}
I'm very surprised..EF is a great instruments to develope more efficiently ways but too little "generalizalbe"

LINQ Expression help with Func TEntity,TType

I have a repository method that accepts an order by parameter in the form:
public IEnumerable<TEntity> Get<TEntity>(Expression<Func<TEntity,string>> orderBy)
Now that works fine when trying to sort by a property of type string,
var entities = rep.Get(x => x.Name);
but what if i want to sort by double or int or any other type.
Doing something like var entities = rep.Get(x => x.Price); obviously throws a compile error saying I can't convert double to string.
How can I make this more generic so I can sort by any property in my entity, or at least the properties where the type implements IComparable or something similar?
The repository class itself should have a type parameter. Then you don't need to specify the entity type when accessing the repository members. Like this:
public interface IRep<TEntity>
{
IEnumerable<TEntity> Get<TOrderBy>(
Expression<Func<TEntity, TOrderBy>> orderBy
);
}
An example entity class:
public class MyEntity
{
public string Name { get; set; }
public decimal Price { get; set; }
}
An example repository implementation:
public class Rep<TEntity> : IRep<TEntity> { ... }
Now you can just consume it like this:
var a = new Rep<MyEntity>();
var b = a.Get(x => x.Name); // string
var c = a.Get(x => x.Price); // decimal
This is how Linq does it. :)
public IEnumerable<TEntity> Get<TEntity, TOrderBy>(Expression<Func<TEntity,TOrderBy>> orderBy)

Categories

Resources