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)
Related
I am actually struggling with generics :)
How can I access properties of a type passed as generic into a method? Do I need to use reflection in any case?
For example the following class
public class someClassA()
{
public int attributeA1;
public string attributeA2;
{
public class someClassB()
{
public int attributeB1;
public string attributeB2;
{
// Then a generic method
private TItem GetSomeData<TItem>(Guid Id) where TItem: new()
{
TItem instanceOfClass = new TItem();
// doing some data stuff
// ....
// finally group the result for better iteration
var groupedById = result.ToList().GroupBy(x =>
(x["SomeId"]))
.Select(grp => grp.ToList())
.ToList();
foreach (var item in groupedById)
{
// how can I access and set the properties of TItem -> instanceOfClass?
instanceOfClass.... = item.property1;
instanceOfClass.... = item.property2;
....
}
return instanceOfClass;
}
The above method is called like this for example
var data = GetSomeData<someClassA>(someId)
Any hint or advice is higly appreciated!
I have already tried with reflection but I am unsure wether this is usefull or not.
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);
}
For the first, I'm trying to avoid direct link to EntityFramework in my assembly, so I can't use System.Data.Entity namespace in client code, only in interface implementation class.
I have interface
public interface IEntitySource<T>
where T : class
{
...
IQueryable<T> WithProperties(params string[] properties);
...
}
and it's EF implementation:
public class EfEntitySource<T> : IEntitySource<T>
where T : class
{
public IQueryable<T> WithProperties(params string[] properties)
{
IQueryable<T> queryable = this.DbSet;
foreach (var property in properties)
{
queryable = this.DbSet.Include(property);
}
return queryable;
}
}
and client code:
public IEnumerable<ContentChangeHistory> GetContentActivityAtGroup(Guid groupId)
{
var groupActivity = this.ContentChangeHistorySource
.WithProperties("ContentPost", "ContentItem", "SystemUser")
.Where(cch => cch.ChangeGroupId == groupId);
return groupActivity;
}
but, the code, that executes GetContentActivityAtGroup method, returns ContentChangeHistory collection with only latest navigation property initialized, e.g. SystemUser.
some code modifications, like this:
public IQueryable<T> WithProperties(params string[] properties)
{
foreach (var property in properties)
{
this.DbSet.Include(property);
}
return this.DbSet;
}
gave no results
Change
queryable = this.DbSet.Include(property);
to
queryable = queryable.Include(property);
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();
I am endeavoring to write a test method to determine if a class has a superset of properties of a different class. This is for purposes of mapping from domain objects to view model objects and as such there is no relationship between the two classes.
As an example if I had the domain class Foo and the view model class FooMap I would want to test that Foo had the properties that FooMap expected it to.
public class Foo
{
public string Bar { get;set; }
public string Baz { get;set; }
public string NoOneCaresProp { get; set; }
public string GetBarBaz() { return Bar + Baz; }
}
public class FooMap
{
public string Bar { get; set; }
public string GetBarBaz { get; set; }
public string NotAFooProp { get; set; }
}
Given the properties of FooMap for the purposes of this test I want to ensure that class Foo has the Bar property and the GetBarBaz method. Additional methods or properties for either class should be ignored. I've written the following static method to perform such a test but am not happy with my implementation:
public static void ExpectedPropertiesExist<TSource, TDestination, R>(params
Expression<Func<TDestination, R>>[] exclude)
{
var excludedProperties = exclude.Select(e => (e.Body as
MemberExpression).Member.Name);
var mappedProperties = typeof(TDestination).GetProperties()
.Select(p => p.Name)
.Except(excludedProperties);
var sourceType = typeof(TSource);
var baseTypeNames = sourceType.GetProperties().Select(b => b.Name).ToList();
baseTypeNames.AddRange(sourceType.GetMethods().Select(b => b.Name));
Assert.IsTrue(new HashSet<string>(baseTypeNames)
.IsSupersetOf(mappedProperties));
}
The code to call the above is not nearly as succinct as I would like it to be:
// what my call to the function looks like now
TestExtensionFun.ExpectedPropertiesExist<Foo, FooMap,
object>(fm => fm.NotAFooProp);
// I'd prefer it to look like this
TestExtensionFun.ExpectedPropertiesExist<Foo, FooMap>(fm => fm.NotAFooProp);
Nor am I sure that the method is as proficient as it could be. What is the best mechanism to write a generic test method to ensure that a class has a subset of properties of a separate class?
Since you don't really care what the expressions return (you are passing in object as the type for R), you could just remove the generic type parameter R from the ExpectedPropertiesExist method:
public static void ExpectedPropertiesExist<TSource, TDestination>(params
Expression<Func<TDestination, object>>[] exclude)
{
var excludedProperties = exclude.Select(e => (e.Body as
MemberExpression).Member.Name);
var mappedProperties = typeof(TDestination).GetProperties()
.Select(p => p.Name)
.Except(excludedProperties);
var sourceType = typeof(TSource);
var baseTypeNames = sourceType.GetProperties().Select(b => b.Name).ToList();
baseTypeNames.AddRange(sourceType.GetMethods().Select(b => b.Name));
Assert.IsTrue(new HashSet<string>(baseTypeNames)
.IsSupersetOf(mappedProperties));
}
This way, you can call the method with your desired syntax (only two generic type parameters).