1
votes

I have a similar issue as in this question: linq question: querying nested collections

I have a Product with a ICollection<Category> and I want to get a Category by id. I everything through an IProductsRepository.

I have tried both products.SelectMany(p => p.Categories).Where(c => c.CategoryID == categoryId) and products.SelectMany(p => p.Categories).First(c => c.CategoryID == categoryId) and many other variations I've tried myself. However I'm not getting it to work.

At runtime I'm getting InvalidCastException. Both Category.CategoryID and categoryId are int.

Would it be better to create an ICategoriesRepository? Perhaps it also has performance benefits? I'm obviously new to LINQ so not sure how to do things properly.

EDIT (code samples):

public interface IProductsRepository
{
    IQueryable<Product> Products { get; }
}

public class ProductsController : Controller
{
    public int PageSize = 4;
    private IProductsRepository productsRepository;

    public ProductsController(IProductsRepository productsRepository)
    {
        this.productsRepository = productsRepository;
    }

    public ViewResult ListById(int categoryId, int page = 1)
    {
        Category cat;
        // What do I need here to get the Category with the categoryId regardless of which Product it is connected to?
        return List(cat, page);
    }

    private ViewResult List(Category category, int page = 1) { //this code works }
}

[Table(Name = "products")]
public class Product
{
    [HiddenInput(DisplayValue = false)]
    [Column(Name = "id", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
    public int ProductID { get; set; }

    [Column]
    public string Name { get; set; }

    [Column(Name = "info")]
    public string Description { get; set; }

    public float LowestPrice 
    {
        get { return (from product in ProductSubs select product.Price).Min(); }
    }

    private EntitySet<Category> _Categories = new EntitySet<Category>();
    [System.Data.Linq.Mapping.Association(Storage = "_Categories", OtherKey = "CategoryID")]
    public ICollection<Category> Categories
    {
        get { return _Categories; }
        set { _Categories.Assign(value); }
    }
}

[Table(Name = "products_types")]
public class Category
{
    [HiddenInput(DisplayValue = false)]
    [Column(Name = "id", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
    public int CategoryID { get; set; }

    [Column(Name = "id")] // Temp solution, real name is localized in a separate table
    public string Name { get; set; }

    private EntitySet<Product> _Products = new EntitySet<Product>();
    [System.Data.Linq.Mapping.Association(Storage = "_Products", OtherKey = "ProductID")]
    public ICollection<Product> Products
    {
        get { return _Products; }
        set { _Products.Assign(value); }
    }
}

EDIT:

I tried to do it through an ICategoriesRepository instead, but I get the same error. This is a full stack trace:

[InvalidCastException: Specified cast is not valid.] System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult) +1191 System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries) +118 System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query) +342 System.Data.Linq.Table1.System.Linq.IQueryProvider.Execute(Expression expression) +58 System.Linq.Queryable.First(IQueryable1 source, Expression1 predicate) +287 MaxFPS.WebUI.Controllers.ProductsController.ListById(Int32 categoryId, Int32 page) in d:\Filer\Documents\Dropbox\ZkilfinG\webbutveckling\MaxFPS\VS Projects\MaxFPS\MaxFPS\Controllers\ProductsController.cs:26 lambda_method(Closure , ControllerBase , Object[] ) +140 System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +14 System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +182 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) +27 System.Web.Mvc.Async.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41() +28 System.Web.Mvc.Async.<>c__DisplayClass81.b__7(IAsyncResult _) +10 System.Web.Mvc.Async.WrappedAsyncResult1.End() +50 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +32 System.Web.Mvc.Async.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33() +58 System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +225 System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult) +10 System.Web.Mvc.Async.WrappedAsyncResult1.End() +50 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +34 System.Web.Mvc.Async.<>c__DisplayClass2a.b__20() +24 System.Web.Mvc.Async.<>c__DisplayClass25.b__22(IAsyncResult asyncResult) +99 System.Web.Mvc.Async.WrappedAsyncResult1.End() +50 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27 System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +14 System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23 System.Web.Mvc.Async.WrappedAsyncResult1.End() +55 System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +39 System.Web.Mvc.Async.<>c__DisplayClass4.b__3(IAsyncResult ar) +23 System.Web.Mvc.Async.WrappedAsyncResult1.End() +55 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +29 System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10 System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +25 System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23 System.Web.Mvc.Async.WrappedAsyncResult1.End() +55 System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +31 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9629708 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

EDIT:

When categoryId is set to an id that is not in the database I get an empty sequence. This to me indicates that the compare code is correct, but I don't understand why or what the invalid cast is all about.

[InvalidOperationException: Sequence contains no elements] System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult) +1191 System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries) +118 System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query) +342 System.Data.Linq.Table1.System.Linq.IQueryProvider.Execute(Expression expression) +58 System.Linq.Queryable.First(IQueryable1 source, Expression1 predicate) +287 MaxFPS.WebUI.Controllers.ProductsController.ListById(Int32 categoryId, Int32 page) in d:\Filer\Documents\Dropbox\ZkilfinG\webbutveckling\MaxFPS\VS Projects\MaxFPS\MaxFPS\Controllers\ProductsController.cs:28 lambda_method(Closure , ControllerBase , Object[] ) +140 System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +14 System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +182 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) +27 System.Web.Mvc.Async.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41() +28 System.Web.Mvc.Async.<>c__DisplayClass81.b__7(IAsyncResult _) +10 System.Web.Mvc.Async.WrappedAsyncResult1.End() +50 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +32 System.Web.Mvc.Async.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33() +58 System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +225 System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult) +10 System.Web.Mvc.Async.WrappedAsyncResult1.End() +50 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +34 System.Web.Mvc.Async.<>c__DisplayClass2a.b__20() +24 System.Web.Mvc.Async.<>c__DisplayClass25.b__22(IAsyncResult asyncResult) +99 System.Web.Mvc.Async.WrappedAsyncResult1.End() +50 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27 System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +14 System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23 System.Web.Mvc.Async.WrappedAsyncResult1.End() +55 System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +39 System.Web.Mvc.Async.<>c__DisplayClass4.b__3(IAsyncResult ar) +23 System.Web.Mvc.Async.WrappedAsyncResult1.End() +55 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +29 System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10 System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +25 System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23 System.Web.Mvc.Async.WrappedAsyncResult1.End() +55 System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +31 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9629708 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously

1
Could you provide more details of exception (stack trace)?wlabaj
what do you want : the product with a category or a category from one product ? why not var res = products.Categories.Where(c => c.CategoryID == categoryId).FirstOdDefault();Cybermaxs
please provide a more complete code samplejeroenh
I want the category that matches regardless of which product it belongs to. That's why I've started thinking perhaps I need a category repository instead.Daniel Flöijer
I've added all the code I believe is relevant.Daniel Flöijer

1 Answers

0
votes

The error was simple once I figured it out. In my real database I have a LocalizedCategory to get localized category names. When implementing I thought I could skip using that to simplify things. So I used this code to give Name a value:

    [Column(Name = "id")]
    public string Name { get; set; }

However, since id is int in the database I got an exception. I'm now going to try to get it working with the LocalizedCategory straight away, though I'm getting another issue: Why do I get different values from my EntitySet depending on how I LINQ to it?