简体   繁体   中英

c# Type Inference - Error - There is no implicit reference conversion from

I have a method that loops through a list of guids and saves them to a database via DbContext. B is a DbSet collection of WebObjects (example: DbSet<MlaPerson> MlaPersons )

protected void AddRelatedWebObject<A, B>(A mlaObject, B inputObject, List<Guid> guids) 
    where A : WebObject 
    where B : DbSet<WebObject>
{
    foreach (Guid guid in guids)
    {
        mlaObject.RelatedWebObjects.Add(inputObject.Find(guid));
        _db.SaveChanges();
    }
}

usage :

foreach (ArticleRelationships item in articleRelationships)
{
    MlaArticle article = new MlaArticle();
    article = _db.MlaArticles.Include(m => m.WebSite).Where(m => m.Id == item.ArticleId).First();
    AddRelatedWebObject<MlaArticle, DbSet<MlaPerson>>(article, _db.MlaPersons, item.PersonIds);
}

_db.MlaPersons are defined as :

public class ECM2Context : DbContext
{
    public DbSet<MlaPerson> MlaPersons { get; set; }
}

and MlaPerson is defined as :

public class MlaPerson : WebObject, IValidatableObject
{
    ...
}

I thought that by inferring that B was DbSet<WebObject> would work because MlaPerson's base class is WebObject, but I'm wrong. I'm getting the error:

The type 'System.Data.Entity.DbSet<ExternalContentManager.Models.MlaPerson>' cannot be used as a type parameter 'B' in the generic type or method 'AddRelatedWebObjects'. There is not implicit reference conversion from 'System.Data.Entity.DbSet<ExternalContentManager.Models.MlaPerson>' to 'System.Data.Entity.DbSet<ExternalContentManager.Models.WebObject>'

I would really appreciate any and all help offered. Thanks for your help. B

You are making a common generics error- assuming that collections are covariant. That is, a an instance of List<Car> does not inherit from List<Vehicle> even though car inherits from vehicle. Likewise, DbSet<MlaPerson> does not inherit from DbSet<WebObject> even though MlaPerson inherits from WebObject.

What you need to do is something like this (I haven't tested this code):

protected void AddRelatedWebObject<A, B, O>(A mlaObject, B inputObject, List<Guid> guids) 
    where A : WebObject 
    where B : DbSet<O>
    where O : WebObject
{
    foreach (Guid guid in guids)
    {
        mlaObject.RelatedWebObjects.Add(inputObject.Find(guid));
        _db.SaveChanges();
    }
}

and use it thus:

foreach (ArticleRelationships item in articleRelationships)
{
    MlaArticle article = new MlaArticle();
    article = _db.MlaArticles.Include(m => m.WebSite).Where(m => m.Id == item.ArticleId).First();
    AddRelatedWebObject<MlaArticle, DbSet<MlaPerson>, MlaPerson>(article, _db.MlaPersons, item.PersonIds);
}

If you do it this way, you may be able to forgo the type specification ( <MlaArticle, DbSet<MlaPerson>, MlaPerson> ) because it should infer it.

A DbSet<MlaPerson> isn't a DbSet<WebObject> just because MlaPerson derives from WebObject . Search for "generic variance" on Stack Overflow to find lots of reasons why.

You might want to change your method parameters and constraints like this:

protected void AddRelatedWebObject<A, B>(A mlaObject, DbSet<B> inputObject,
                                         List<Guid> guids) 
    where A : WebObject 
    where B : WebObject

And then call it like this:

AddRelatedWebObject<MlaArticle, MlaPerson>(article, _db.MlaPersons,
                                           item.PersonIds);

That may work - it may even work with type inference to allow this:

AddRelatedWebObject(article, _db.MlaPersons, item.PersonIds);

I would also suggest that you rename your type parameters to something like TSource and TTarget to be clearer, and to follow conventions.

--EDIT - THIS ANSWER IS WRONG. SEE THE COMMENTS FOR MORE INFO--

Upcasting does not work with containers(unless you are upcasting the data structure, but that's not the case here). Imagine the following code(written with arrays for simplicity, but the same principles apply for all generic containers):

class A{}
class B:A{}

/*Inside a method*/
B[] arrayB=new B[10];
A[] arrayA=arrayB;//This line will produce a compile error
arrayA[0]=new A();

Now arrayB[0] contains an object of type A, even though it A is not a derived class of B . That's why upcasting does not work for containers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM