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);
Related
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);
}
public class TestBase
{
// implementation
}
public class Test : ICollection<TestBase>
{
// implementation
}
Somewhere else I have a property of the Test type:
public Test Test {get;set;}
How can I get the underlying type of the ICollection that the Test inherits from?
What you are probably looking for is GetGenericArguments().
var type = typeof(Test);
var collInterface = type.GetInterfaces()[0];
var generic = collInterface.GetGenericArguments()[0];
Let x be an instance of a class that implements interfaces. You can get its iterface types as follows:
var type = x.GetType();
var interfaces = type.GetInterfaces();
Edit: I had your question wrong: Follow up with "GetGenericArguments()" as suggested.
the GetInterfaces method returns all implemented interfaces in current type and its GetGenericArguments method returns list of generic arguments witch you can get its base type by BaseType property.
this example returns class hierarchy of the generic argument of base interface
implemented in current class:
var myType = this.GetType().GetInterfaces().First().GetGenericArguments().First();
while (myType != null)
{
Console.WriteLine(myType.Name);
myType = myType.BaseType;
}
Just a different approach of getting the used type in a base interface:
private readonly IObjectProvider<Interface> _repositoryInterfaces =
Interfaces().That().ImplementInterface(typeof(IRepository<>)).As("Repository Interfaces");
[Fact]
public void RepositoryInterfaces_ShouldBeInTheCorrectNamespace()
{
// Arrange
var repos = Interfaces().That().Are(_repositoryInterfaces).GetObjects(Architecture);
// Act
// Assert
foreach (var repo in repos)
{
var entityType = repo.GetImplementsInterfaceDependencies().Single().TargetGenericArguments.Single().Type;
repo.ResidesInNamespace($"Domain.AggregateModels.{entityType.Name}Aggregate").Should().BeTrue();
}
}
my interfaces look like this:
public interface IExampleRepository : IRepository<ExampleEntity>
{ //more methods here }
Type t = typeof(Test);
MethodInfo mi = t.GetMethod("MyMethod");
Type returnvalue = mi.ReturnType;
Type answer = Nullable.GetUnderlyingType(returnvalue);
I hope it helps.
I have the following code (example):
public dynamic GetData(string name)
{
using(var ctx = GetObjectContext())
{
switch (name)
{
case "entity1":
return ctx.entity1.ToList();
case "entity2":
return ctx.entity2.ToList();
......
default:
return null;
}
}
}
I want to avoid switch in this sample. How can I find needed entity class by name, call the ToList() method and return data? Can I do this using reflection?
You can do it using reflection, however you will also need to use generics because the type of list returned by the ToList() method is different for each entity type.
You can access a property getter through reflection like so:
var enumerable = typeof([ClassNameOfContext]).GetProperty(name).GetValue(ctx, null);
Whereas [ClassNameOfContext] is the name of the class that ctx is an instance of. This is not obvious from your code, but you know it :-)
The problem is that enumerable will be an object and has to be casted to IEnumerable<EntityType> where EntityType is the type of entity you are accessing. In other words, it depends on the name you are passing. If you use generics to determine the type, you will be able to properly cast the object and don't have to return a dynamic even.
public TEntity Get<TEntity>(string name)
{
...
and transform the line from above:
var enumerable = (IEnumerable<TEntity>)(typeof([ClassNameOfContext]).GetProperty(name).GetValue(ctx, null));
return enumerable.ToList();
here you go!
Addendum: You could, conceivably, get rid of the string parameter, too - having names of types or properties in strings should be avoided where possible because it is not type safe. The compiler does not recognize it, and IDE features such as refactorings don't account for it. The problem here is that the property names are usually the pluralized form of the entity type names. But you could use reflection to find the property whose type matches the TEntity. I leave this as an exercise :-)
You can use code like this
private IEnumerable<TEntity> GetList<TEntity>(string connectionString, Func<object, T> caster)
{
using (var ctx = new DbContext(connectionString))
{
var setMethod = ctx.GetType().GetMethod("Set").MakeGenericMethod(typeof(T));
var querable = ((DbSet<object>)setMethod
.Invoke(this, null))
.AsNoTracking()
.AsQueryable();
return querable
.Select(x => caster(x))
.ToList();
}
}
To call like this:
var branchList = GetList<Branch>("connectionStringName", x => (Branch)x);
You can remove .AsNoTracking() and remove .ToList(), then you will get pure IQueryable which you can query further.
I've created a method to include all related entities with some help of the great answer of #chiccodoro.
using the entity "Product" which has 7 navigation properties
public static IQueryable<T> IncludeAllEntities<T>(this DbSet<T> entity, DataContext context) where T : class
{
var querable = entity.AsQueryable();
var type = typeof(T);
var entityType= context.Model.FindEntityType(type);
var navs = entityType?.GetNavigations();
if (navs==null)
{
return null;
}
List<string> navNames = new List<string>();
foreach (var nav in navs)
{
navNames.Add(nav.Name);
}
try
{
var agg = navNames.Aggregate(querable, (acc, name) => acc.Include(name));
return agg;
}
catch (Exception ex)
{
throw;
}
}
here I am getting the type of the entity to get the navigation properties, then adding the name of each one to a list of string, then aggregating over the list to include each entity.
then we can use this extension method like this:
var record= await _context.Products.IncludeAllEntities(_context).FirstOrDefaultAsync(x => x.Id == key);
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"
I'm trying to writing a generic method that will load a record of a specific type, with a specific ID. Here's one way that works:
public abstract class LinqedTable<T> where T : LinqableTable {
public static T Get(long ID) {
DataContext context = LinqUtils.GetDataContext<T>();
var q = from obj in context.GetTable<T>()
where obj.ID == ID
select obj;
return q.Single<T>();
}
}
public abstract class LinqableTable {
public abstract long ID { get; set; }
}
You can ignore the call to LinqUtils.GetDataContext<T>(); that's a utility function I've got to deal with the fact that I have multiple data contexts in my program. The point is that now I can declare any of my classes as subclasses of LinqableTable, and I can easily instantiate a record of that table just by calling LinqedTable<MyType>.Get(ID).
This has some limitations, however. Firstly, it forces all of my tables to have an I
identity field of type long, named ID. Secondly, because I'm using an abstract method, I am forced to go to the O/R designer and change the inheritance property of every ID field in my system to "override".
I want more flexibility than that. So naturally, I tried reflection, and came out with the following:
public abstract class LinqedTable<T> where T : LinqableTable {
public static T Get(long ID) {
DataContext context = LinqUtils.GetDataContext<T>();
var q = from obj in context.GetTable<T>()
where obj.IDValue == ID
select obj;
return q.Single<T>();
}
}
public abstract class LinqableTable {
internal long IDValue {
get { return (long)IDProperty.GetValue(this, null); }
set { IDProperty.SetValue(this, value, null); }
}
internal PropertyInfo IDProperty {
get { return this.GetType().GetProperty(IDPropertyName); }
}
internal protected virtual string IDPropertyName {
get { return "ID"; }
}
}
Theoretically, this allows me to override the ID column name, the cast to long should be OK with any integral data type, and I don't need to go defining all my ID columns as overrides.
BUT
Linq doesn't like this. On the call to q.Single<T>(); I get a runtime error:
The member 'EISS.Utils.LinqableTable.IDValue' has no supported translation to SQL.
OK, today I learned that Linq does some kind of magic on the back end; it doesn't instantiate obj and just read the IDValue property. So must be there's some attribute that needs to be set on the IDValue property that lets Linq do its thing.
But what?
Linq to SQL tries to translate your linq-query into SQL, but it does not know how to translate your property to a column name in the DB.
A good explanation can be found here on SO:
simple linq to sql has no supported translation to SQL
But how to solve it, is another matter. I have with succes used the apporoach from this thread:
http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/df9dba6e-4615-478d-9d8a-9fd80c941ea2/
Or you can use dynamic query as mentioned here by scott guthrie:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Having read these posts: Generic Data Access using LINQ to SQL and C#,
LINQ-to-SQL: Generic Primary Key function and
Calling a generic method with Type
My colleague and I came up with the following digest:
We added the following method to our datacontext (in a partial class).
public T GetInstanceByPrimaryKey<T>(object primaryKeyValue) where T : class
{
var table = this.GetTable<T>();
var mapping = this.Mapping.GetTable(typeof(T));
var pkfield = mapping.RowType.DataMembers.SingleOrDefault(d => d.IsPrimaryKey);
if (pkfield == null)
throw new Exception(String.Format("Table {0} does not contain a Primary Key field", mapping.TableName));
var param = Expression.Parameter(typeof(T), "e");
var predicate =
Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Property(param, pkfield.Name), Expression.Constant(primaryKeyValue)), param);
return table.SingleOrDefault(predicate);
}
Then, where we need to instanciate from the type name and primary key value:
string name = "LinqObjectName";
int primaryKey = 123;
var dc = new YourDataContext();
Type dcType = dc.GetType();
Type type = dcType.Assembly.GetType(String.Format("{0}.{1}", dcType.Namespace, name));
MethodInfo methodInfoOfMethodToExcute = dc.GetType().GetMethod("GetInstanceByPrimaryKey");
MethodInfo methodInfoOfTypeToGet = methodInfoOfMethodToExcute.MakeGenericMethod(name);
var instance = methodInfoOfTypeToGet.Invoke(dc, new object[] { primaryKey });
return instance;
Hope this helps!
Since LINQ statements referred to a LINQ-to-SQL IQueryable are translated to SQL queries, you will have to use the AsEnumerable extension (which will in turn cause a read of all the items in the database) and do reflection-related stuff on that IEnumerable.
EDIT
As required here's a clarificationAs specified in a comment, what I meant was something like:
(from obj in context.GetTable<T>() select obj).AsEnumerable().Where(x => x.IDValue == ID)
Unlike a query executed on an IQueryable, which can be perfectly translated to SQL such as
context.GetTable().Where(x => x.Text == "Hello")
which gets converted to something similar to
SELECT * FROM TABLE_MAPPED_TO_TYPE_T WHERE Text = 'Hello'
a query executed against an IEnumerable - in your case - will be executed by fetching all the entries of your table and then applying code-wise the specified filter.