无法循环访问A和B都实现相同接口的泛型类型B的列表A(icollection) [英] Cannot loop through list A(icollection) of generic type B where A and B both implement same interface
问题描述
我有这个接口
public interface IEFEntity
{
object GetPKValue();
bool PKHasNoValue();
}
我所有的自动生成的EF类都实现了这个接口并实现了两种方法。这是通过使用部分类的单独文件完成的,因此在重新生成EF类时实现不会消失。这对问题并不重要,但我已经举了两个EF类的设置示例, debiteur
和 schakeling
下面。所有的都在同一个命名空间offcourse:
$ b
public partial class debiteur
{
public debiteur()
{
this.schakeling = new HashSet< schakeling>();
}
public int id {get;组; }
公共字符串naam {get;组; }
公共虚拟ICollection< schakeling> schakeling {get;组; }
}
public partial class schakeling
{
public schakeling()
{
this.debiteur = new HashSet< debiteur>();
}
public int id {get;组; }
public string omschrijving {get;组; }
公共虚拟ICollection< debiteur> debiteur {get;组; }
}
我有这2个额外的部分实现了接口。
public partial class debiteur:IEFEntity
{
公共对象GetPKValue(){
return this.id;
}
public bool PKHasNoValue(){
return this.id == 0;
$ / code $ / pre
public partial class schakeling:IEFEntity
{
public object GetPKValue(){
return this.id;
}
public bool PKHasNoValue(){
return this.id == 0;
$ b $ p $(只是为了清除:其他EF生成的类可能有一个PK属性,其名称不是'id',或者甚至有一个PK类型的字符串和一个用户名,但这对于这个问题并不重要!也不是我使用这些方法的唯一原因,请继续阅读。 ..)
我有一个使用两个泛型类型参数的GenManager类。这是没有实现的类定义。 (我知道,这只适用于具有单个PK而不具有组合键的实体/表格,如辩论 here )
public class GenManager< Entity,EntityKey>其中Entity:class,IEFEntity {}
这意味着Entity类型参数必须是一个实现界面IEFEntity。因此,在我的应用程序的任何地方,让我们说一个控制器,我现在可以使用以下方法创建实例:私人GenManager< debiteur,int> DebiteurManager;
私人GenManager< schakeling,int> SchakelingManager;
但是,例如:
private GenManager< anotherClass,int> AnotherClassManager;
会失败,因为 anotherClass
没有实现IEFEntity 。但是回到GenManager类。这是第一个没有实现的概述
public class GenManager< Entity,EntityKey>实体:class,IEFEntity
{
public IQueryable< Entity> Items {get {return repo.Items; }}
private IGenRepo< Entity,EntityKey>回购;
public GenManager(IGenRepo< Entity,EntityKey> repo){this.repo = repo; }
public Entity find(EntityKey pk){return repo.find(pk); }
public RepoResult delete(EntityKey pk){return repo.delete(pk); }
public RepoResult create(Entity item){return repo.create(item); }
public RepoResult update(Entity item){return repo.update(item); }
public bool isInFKEntity< FKEntity,FKEntityKey>(EntityKey pk,FKEntityKey fk)其中FKEntity:class,IEFEntity {}
$ b $ public List< FKEntity>其中FKEntity:FKEntityKey>(EntityKey pk)其中FKEntity:IEFEntity {}
public RepoResult removeFKEntities< FKEntity,FKEntityKey>(EntityKey pk,FKEntityKey [] fkList)其中FKEntity:IEFEntity {}
,b
public RepoResult addFKEntities< FKEntity,FKEntityKey>(EntityKey pk,FKEntityKey [] fkList)其中FKEntity:IEFEntity {}
public RepoResult resetFKEntities< FKEntity,FKEntityKey>(EntityKey pk,FKEntityKey [ ] FKEntity:IEFEntity {}
public RepoResult resetFKEntities2< FKEntity,FKEntityKey>(EntityKey pk,FKEntityKey [] fkList)其中FKEntity:IEFEntity {}
public RepoResult FKEntity:FKEntityKey>(EntityKey pk,FKEntityKey [] fkList)其中FKEntity:IEFEntity {}
public RepoResult clearFKEntities< FKEntity>(EntityKey pk)其中FKEntity:IEFEntity {}
}
取一个其次看看这个。就像 GenManager
类的第一个泛型类型 Entity
必须是一个实现 IEFEntity的类
,这也必须是方法中使用的泛型类型 FKEntity
的情况。所以在我的控制器中,我现在可以使用这个语句:
bool b = DebiteurManager.isInFKEntity< ; schakeling,int>(debiteur_id,schakeling_id);
但又是一个例子:
bool b = DebiteurManager.isInFKEntity< anotherEntity,int>(debiteur_id,anotherEntity_id);
会失败,因为 anotherEntity
不会实现IEFEntity 。
在这些方法中,必须使用c#反射来发生魔法,并且我面临一个问题。我给你实现了未完成的方法isInFKEntity,其中包含一些测试代码语句:
public bool isInFKEntity< FKEntity,FKEntityKey>(EntityKey pk,FKEntityKey fk)其中FKEntity:class,IEFEntity
{
实体dbEntry = find(pk);
if(dbEntry == null)throw new InvalidOperationException(RepoMessages.dbEntryIsNull(new List< object> {pk}));
// ----------对于问题不重要!只是为了清除事情---------- \\
var v1 = typeof(Entity).GetProperty(naam)。GetValue(dbEntry); //将v1设置为一个字符串值
var v1Copy = new debiteur()。GetType()。GetProperty(naam)。GetValue(dbEntry); //只是为了测试和清除上面的语句做什么,变量具有相同的值!
// ---------- End ---------- \\
var FKEntityList = typeof(Entity).GetProperty(typeof (FKEntity).Name).GetValue(dbEntry); //在运行时返回一个列表,但是我不能遍历它们
var FKEntityListCopy = new debiteur()。GetType()。GetProperty(schakeling ).GetValue(dbEntry); //只是为了测试和清除上面的语句做什么,变量具有相同的值!
var FKEntityListWithCast = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry)as List< FKEntity> ;; //在运行时返回NULL值,但智能!
var FKEntityListWithCastCopy = new debiteur()。GetType()。GetProperty(schakeling)。GetValue(dbEntry)as List< FKEntity>; //只是为了测试和清除上述语句的作用,值!(也是NULL)
返回false;
}
我想循环 FKEntityList
但如果我尝试使用foreach来做到这一点,我收到一条消息:foreach语句不能对'object'类型的变量进行操作,因为'object'不包含'GetEnumerator'的公共定义。另外,使用intellisense只显示4个标准方法Equals(object obj),GetHashCode(),GetType()和ToString()。请参阅图片
所以我尝试使用作为List< FKEntity>
并将结果赋给 FKEntityListWithCast
,并且现在我不会收到错误消息使用foreach。
所以我想,非常好,我只是使用这个return语句,它的工作原理:
return FKEntityListWithCast.Any(item => item.GetPKValue()。ToString()== fk.ToString());
上面的返回语句不会给出错误并且编译所有内容,但问题是在运行时 FKEntityListWithCast
不会得到一个值。它保持NULL。所以问题是:
GETS VALUE在运行时,但不能循环:
var FKEntityList = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry);
在运行时GETS NO VALUE(NULL),但CAN循环:
var FKEntityListWithCast = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry)as List< FKEntity> ;
任何帮助都会大大增加!
解决方案
ICollection< FKEntity> FKEntityList = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry)作为ICollection< FKEntity>;
仍然无人能够回答这个问题
The intro is a bit tedious, but is just for clearity! Some might even learn something from it or get some handy insights. My question is found at the bottom. I really hope someone can help me!
I have this interface
public interface IEFEntity
{
object GetPKValue();
bool PKHasNoValue();
}
All my automatic genereted EF classes implement this interface and implement the 2 methods. This is done via a separate file using partial classes so the implementation is not gone when regenerating the EF classes. This is not important for the question but I have put an example of the setup of two EF classes, debiteur
and schakeling
below. All are in the same namespace offcourse:
public partial class debiteur
{
public debiteur()
{
this.schakeling = new HashSet<schakeling>();
}
public int id { get; set; }
public string naam { get; set; }
public virtual ICollection<schakeling> schakeling { get; set; }
}
public partial class schakeling
{
public schakeling()
{
this.debiteur = new HashSet<debiteur>();
}
public int id { get; set; }
public string omschrijving { get; set; }
public virtual ICollection<debiteur> debiteur { get; set; }
}
And I have this 2 extra partials that implement the interface. Nothing special here.
public partial class debiteur : IEFEntity
{
public object GetPKValue() {
return this.id;
}
public bool PKHasNoValue() {
return this.id == 0;
}
}
public partial class schakeling : IEFEntity
{
public object GetPKValue() {
return this.id;
}
public bool PKHasNoValue() {
return this.id == 0;
}
}
(Just to clearify: Other EF generated classes might have a PK property who's name is not 'id' or even have a PK of type string and a name of username, but this is not important for the question! And also not the only reason I use these methods, keep reading...)
I have a GenManager class which uses two generic type parameters. Here's the class definition without implementation. (And I know, this only works for entities/tables with a single PK not with composite keys, as debated here)
public class GenManager<Entity, EntityKey> where Entity : class, IEFEntity {}
This means that the Entity type parameter must be a class which implement interface IEFEntity. So anywhere in my application, lets say a controller I can now create an instance by using:
private GenManager<debiteur, int> DebiteurManager;
private GenManager<schakeling, int> SchakelingManager;
But for example:
private GenManager<anotherClass, int> AnotherClassManager;
will fail because anotherClass
does not implement IEFEntity. But back to the GenManager class. Here's the first outlining without all the implementation
public class GenManager<Entity, EntityKey> where Entity : class, IEFEntity
{
public IQueryable<Entity> Items { get { return repo.Items; } }
private IGenRepo<Entity, EntityKey> repo;
public GenManager(IGenRepo<Entity, EntityKey> repo) { this.repo = repo; }
public Entity find(EntityKey pk) { return repo.find(pk); }
public RepoResult delete(EntityKey pk) { return repo.delete(pk); }
public RepoResult create(Entity item) { return repo.create(item); }
public RepoResult update(Entity item) { return repo.update(item); }
public bool isInFKEntity<FKEntity, FKEntityKey>(EntityKey pk, FKEntityKey fk) where FKEntity : class, IEFEntity { }
public List<FKEntity> GetFKEntities<FKEntity, FKEntityKey>(EntityKey pk) where FKEntity : IEFEntity { }
public RepoResult removeFKEntities<FKEntity, FKEntityKey>(EntityKey pk, FKEntityKey[] fkList) where FKEntity : IEFEntity { }
public RepoResult addFKEntities<FKEntity, FKEntityKey>(EntityKey pk, FKEntityKey[] fkList) where FKEntity : IEFEntity { }
public RepoResult resetFKEntities<FKEntity, FKEntityKey>(EntityKey pk, FKEntityKey[] fkList) where FKEntity : IEFEntity { }
public RepoResult resetFKEntities2<FKEntity, FKEntityKey>(EntityKey pk, FKEntityKey[] fkList) where FKEntity : IEFEntity { }
public RepoResult resetFKEntities3<FKEntity, FKEntityKey>(EntityKey pk, FKEntityKey[] fkList) where FKEntity : IEFEntity { }
public RepoResult clearFKEntities<FKEntity>(EntityKey pk) where FKEntity : IEFEntity { }
}
Take a second to look at this. Just as the first generic type Entity
of the GenManager
class must be a class that implements IEFEntity
, this must also be the case with generic type FKEntity
used in the methods. So in my controller for example I can now use this statement:
bool b = DebiteurManager.isInFKEntity<schakeling, int>(debiteur_id, schakeling_id);
But again an example:
bool b = DebiteurManager.isInFKEntity<anotherEntity, int>(debiteur_id, anotherEntity_id);
will fail because anotherEntity
does not implement IEFEntity.
And it is in these methods where the magic must happen using c# reflection and I face a problem. I give you the implementation of the unfinished method isInFKEntity with some test code statements including comments:
public bool isInFKEntity<FKEntity, FKEntityKey>(EntityKey pk, FKEntityKey fk) where FKEntity : class, IEFEntity
{
Entity dbEntry = find(pk);
if (dbEntry == null) throw new InvalidOperationException(RepoMessages.dbEntryIsNull(new List<object> { pk }));
//----------Not important for question! Just to clearify things----------\\
var v1 = typeof(Entity).GetProperty("naam").GetValue(dbEntry);//Sets v1 to a string value
var v1Copy = new debiteur().GetType().GetProperty("naam").GetValue(dbEntry);//Just to test and clearify what the above statement does, vars have the same value!
//----------End----------\\
var FKEntityList = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry);//Gives a list back at runtime, but I can't loop through them
var FKEntityListCopy = new debiteur().GetType().GetProperty("schakeling").GetValue(dbEntry);//Just to test and clearify what the above statement does, vars have the same value!
var FKEntityListWithCast = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry) as List<FKEntity>;//Gives a NULL value back at runtime, but intellisense!
var FKEntityListWithCastCopy = new debiteur().GetType().GetProperty("schakeling").GetValue(dbEntry) as List<FKEntity>;//Just to test and clearify what the above statement does, vars have the same value!(also NULL)
return false;
}
I want to loop through FKEntityList
but if I try to do that using a foreach I get a message: foreach statement cannot operate on variables of type 'object' because 'object' does not contain a public definition for 'GetEnumerator'. Also, using intellisense only shows the 4 standard methods Equals(object obj), GetHashCode(), GetType() and ToString(). See image
So I try to cast it succesfully using as List<FKEntity>
and assign the result to FKEntityListWithCast
and now I get no error message using a foreach.
So I thought, very nice I just use this return statement and it works:
return FKEntityListWithCast.Any(item => item.GetPKValue().ToString() == fk.ToString());
The above return statment doesn't give an error and everything compiles, but the problem is that at runtime the FKEntityListWithCast
doesn't get a value. It stays NULL. So the problem is:
GETS VALUE at runtime but CANNOT loop through:
var FKEntityList = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry);
GETS NO VALUE(NULL) at runtime but CAN loop through:
var FKEntityListWithCast = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry) as List<FKEntity>;
Any help would be greatly appiciated!
解决方案 ICollection<FKEntity> FKEntityList = typeof(Entity).GetProperty(typeof(FKEntity).Name).GetValue(dbEntry) as ICollection<FKEntity>;
Still a bummer nobody could answer this
这篇关于无法循环访问A和B都实现相同接口的泛型类型B的列表A(icollection)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!