tags:

views:

171

answers:

5

I have an interface defined as:

namespace RivWorks.Interfaces.DataContracts
{
    public interface IProduct
    {
        [XmlElement]
        [DataMember(Name = "ID", Order = 0)]
        Guid ProductID { get; set; }
        [XmlElement]
        [DataMember(Name = "altID", Order = 1)]
        long alternateProductID { get; set; }
        [XmlElement]
        [DataMember(Name = "CompanyId", Order = 2)]
        Guid CompanyId { get; set; }
        ...
        [XmlElement]
        [DataMember(Name = "buttonPositionCSS", Order = 14)]
        string buttonPositionCSS { get; set; }
    }
}

I have a concrete implementation like:

namespace RivWorks.Model.Objects
{
    [DataContract(Name = "Product", Namespace = "http://rivworks.com/DataContracts/2009/01/15")]
    public class Product : IProduct
    {
        #region Declarations
        private Guid _productID;
        private long _altProductID;
        private Guid _companyId;
        ...
        private string _buttonPositionCSS;
        #endregion

        #region IProduct Members
        public Guid ProductID { get { return _productID; } set { _productID = value; } }
        public long alternateProductID { get { return _altProductID; } set { _altProductID = value; } }
        public Guid CompanyId { get { return _companyId; } set { _companyId = value; } }
        ...
        public string buttonPositionCSS { get { return _buttonPositionCSS; } set { _buttonPositionCSS = value; } }
        #endregion
    }
}

I have another interface defined as:

namespace RivWorks.Interfaces.Services
{
    public interface IProductManager
    {
        #region Products
        IProduct GetProductById(Guid productId);
        List<IProduct> GetProductByCompany(Guid companyId);
        int SaveProduct(IProduct product);
        int DeleteProduct(Guid productId);
        #endregion
    }
}

I have a class defined as:

namespace RivWorks.Controller
{
    public class ProductManager : IProductManager
    {
        #region Declare Models
        private static RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
        private static RivWorks.Model.NegotiationAutos.RivFeedsEntities _dbFeed = RivWorks.Model.Stores.FeedEntities(AppSettings.FeedAutosEntities_connString);
        #endregion

        #region Products
        public IProduct GetProductById(Guid productId)
        {
            // deleted for simplicity sake
            return product;
        }
        public List<IProduct> GetProductByCompany(Guid companyId)
        {
            var company = (from a in _dbRiv.Company where a.CompanyId == companyId select a).First();
            var companyDetails = from a in _dbRiv.AutoNegotiationDetails where a.CompanyId == companyId select a;
            // ################################################## //
            List<IProduct> productList = new List<RivWorks.Model.Objects.Product>();
            // ################################################## //
            // deleted for simplicity sake
            return productList;
        }
        public int SaveProduct(IProduct product)
        {
            return 0;  // stub
        }
        public int DeleteProduct(Guid productId)
        {
            return 0;  // stub
        }
        #endregion
    }
}

I am getting this error at compile time:

Cannot implicitly convert type 'System.Collections.Generic.List<RivWorks.Model.Objects.Product>' to 'System.Collections.Generic.List<RivWorks.Interfaces.DataContracts.IProduct>'

The system is a very Service (WCF, WebOrb, etc) oriented system and I wanted to expose Interfaces as my contracts. I have the Model & Controller in .NET and am using the Services as Views (proxies) to 3rd party consumers (the real View).

What am I missing or doing wrong?

+3  A: 

You are creating a list of Products, but it's expecting a list of IProducts. The two are not interchangable in this case (what if someone tries to add an IProduct to the list that isn't a Product?)

Change

List<IProduct> productList = new List<RivWorks.Model.Objects.Product>();

to

List<IProduct> productList = new List<IProduct>();
Anon.
Heh, nice timing.
Aequitarum Custos
One second in it ;)
Anon.
+3  A: 

You need to change this line:

List<IProduct> productList = new List<RivWorks.Model.Objects.Product>(); 

to

List<IProduct> productList = new List<IProduct>(); 

Since you're returning a list with the interface as the generic type, instantiate a list with the generic type.

You will be able to add classes that implement it.

Aequitarum Custos
+4  A: 

Try Changing:

List<IProduct> productList = new List<RivWorks.Model.Objects.Product>();

To:

List<IProduct> productList = new List<IProduct>();

Or (if you need to work with elements of the List as their concrete implementations rather than the Interface before you return the list):

List<RivWorks.Model.Objects.Product> productList = 
    new List<RivWorks.Model.Objects.Product>();

// Do some work here.

return productList.Cast<IProduct>().ToList();
Justin Niessner
While this would work, seems like unncessary work to cast it, when you could just start with a List<IProduct>
Aequitarum Custos
@Aequitarum - Depends on if he wants to work with items of the List as RivWorks.Model.Objects.Product instead of working with the Interface or not. He does have a section of code removed for simplicity...he could need methods specific to Product.
Justin Niessner
Good point, did not think of that. +1 for the realization.
Aequitarum Custos
Yes, I am doing a lot of work on the properties of IProduct - depending on what is available in the DB I can populate it from several different sources.
Keith Barrows
+1  A: 

You are assuming that the Generic List class understands that if you declare a List of IProduct it is the same as a List of Product. You need to declare the list as follows:

List<IProduct> = new List<Iproduct>();

Then you can add Product instances.

This is something that can only be done (under certain circumstances) from .net Faramework version 4 when co- and contravariance for generic types are introduced in the Framework.

Anton
Specifically, this will work in C# 4 on *interfaces* and *delegates* that are *generic*, *known to be safe for variance* and parameterized with *reference types*. List<T> is not an interface. IList<T> is not safe for variance. IEnumerable<T> fits the bill.
Eric Lippert
+1  A: 

Hello, The generics in C# just don't work in that way.

See here

If B is a subclass of A, C< B > isn't a subclass of C< A >.

Julien