Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing an ICollection<ISomething> with a concrete type to satisfy Entity Framework

So, this is kind of an obtuse question, but let me see if I can lay it out relatively simply. Lets say I have the following interface:

public interface IFoo
{
    ICollection<IBar> Bars { get; set; }
}

Which I then implement with:

public class Foo : IFoo
{
    public virtual ICollection<IBar> Bars { get; set; }
}

Only Entity Framework can't work with interfaces, so it pretty much completely ignores this navigation property. In order to get EF to recognize it, I need to change it to:

public virtual ICollection<Bar> Bars { get; set; }

Where Bar would be my implementation of IBar. Only, that fails to implement the interface, which wants IBar not Bar.

Now, consider a slightly different scenario, where I've just got a basic foreign key:

public interface IFoo
{
    IBar Bar { get; set; }
}

public class Foo : IFoo
{
    public virtual IBar Bar { get; set; }
}

Same issue, but here, I can solve it by adding:

public class Foo : IFoo
{
    public virtual Bar Bar { get; set; }
    IBar IFoo.Bar
    {
        get { return Bar; }
        set { Bar = (Bar)value; }
    }
}

EF is happy because it has a concrete type and the interface is happy because it has an implementation with IBar. The problem is that I can't figure out how to apply the same logic with an ICollection<IBar> because (ICollection<Bar>)value raises an exception saying "Cannot implicitly convert type ICollection<Bar> to ICollection<IBar>".

How should I properly make the cast?

UPDATE

So, I wasn't paying close enough attention to where the error was being generated. It was actually complaining about the get { return Bars; } bit. I was able to get rid of the error by changing it to:

public class Foo : IFoo
{
    public virtual ICollection<Bar> Bars { get; set; }
    ICollection<IBar> IFoo.Bars
    {
        get { return (ICollection<IBar>)Enumeration.Cast<IBar>(Bars); }
        set { Bars = (ICollection<Bar>)value; }
    }
}

That seems a little hokey to me though, like I'm only masking the error and creating a little time bomb for myself. I'd appreciate any thoughts or alternate solutions.

like image 637
Chris Pratt Avatar asked Nov 01 '22 09:11

Chris Pratt


1 Answers

To let the covariance/contravariance work I define navigation properties as enumerables in my interfaces:

public interface IFoo
{
    IEnumerable<IBar> Bars { get; }
}

public class Foo : IFoo
{
    public virtual ICollection<Bar> Bars { get; set; }
    IEnumerable<IBar> IFoo.Bars
    {
       return this.Bars;
    } 
}

This is still enough for EF LINQ queries built on interface types.

like image 177
Wiktor Zychla Avatar answered Nov 14 '22 19:11

Wiktor Zychla