1

I'm currently working on net core 2 application.

I created a "Goods" abstract class.

public abstract class Good : BaseEntity 
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Details { get; set; }
    public double Price { get; set; }
    public string IndexId { get; set; } 
    public string RefPintel { get; set; }
    public bool IsDiscountable { get; set; }
    public int ProductTypeId { get; set; }
    public bool IsEnabled { get; set; }
    public int DepartmentId { get; set; }
    public string Season { get; set; }
}

Two inherited classes Product and Batch.

public class Product : Good
{
    public string File { get; set; }
    public bool? IsDisplayedOnJCE { get; set; }
    public bool? IsBasicProduct { get; set; }
    public int? PintelSheetId { get; set; }


public class Batch : Good
{
    public ICollection<Product> Products { get; set; }

As you can see, a batch includes a collection of Products.

Everything works fine, EF creates a single table "Goods" using TPH inheritance model. A product within a batch has a BatchId (column created by EF), discriminator column works well (even though I do not have access to it in my code) etc...

My problem is that, when I perform a GetAll request, I get all batch products twice.

 {
        "products": [
            {
                **"id": 85,**
                "title": "500 Pièces LEGO",
                "details": "Boîte de 500 pièces LEGO pour construire des trucs mega top",
                "refPintel": "0002",
                "file": "lego-500pcs.pdf",
                "providerId": 2,
                "productType": "Jouet"
            },
            {
                "id": 86,
                "title": "1000 Pièces LEGO",
                "details": "Boîte de 1000 pièces LEGO pour construire des trucs mega top",
                "refPintel": "0001",
                "file": "lego-1000pcs.pdf",
                "providerId": 3,
                "productType": "Jouet"
            }
        ],
        "id": 78,
        "details": "Lot super LEGO 1500 pièces ! ",
        "title": "Lot LEGO TOP WOWOW",
        "price": 139.99,
        "indexId": "J",
        "refPintel": "0004",
        "isDiscountable": true,
        "productTypeId": 1,
        "isEnabled": true,
        "departmentId": 1,
        "season": "2018",
        "isBatch": true,
        "createdBy": "mathieu",
        "updatedBy": "mathieu"
    },
    {
        "file": "lego-500pcs.pdf",
        "isDisplayedOnJCE": true,
        "isBasicProduct": true,
        "pintelSheetId": 0,
        "providerId": 2,
        "id": 85,
        "details": "Boîte de 500 pièces LEGO pour construire des trucs mega top",
        "title": "500 Pièces LEGO",
        "price": 50,
        "indexId": "G",
        "refPintel": "0002",
        "isDiscountable": false,
        "productTypeId": 1,
        "isEnabled": true,
        "departmentId": 1,
        "season": null,
        "isBatch": false,
        "createdBy": "mathieu",
        "updatedBy": "mathieu"
    },
    {
        "file": "lego-1000pcs.pdf",
        "isDisplayedOnJCE": true,
        "isBasicProduct": true,
        "pintelSheetId": 0,
        "providerId": 3,
        "id": 86,
        "details": "Boîte de 1000 pièces LEGO pour construire des trucs mega top",
        "title": "1000 Pièces LEGO",
        "price": 100,
        "indexId": "I",
        "refPintel": "0001",
        "isDiscountable": false,
        "productTypeId": 1,
        "isEnabled": true,
        "departmentId": 1,
        "season": null,
        "isBatch": false,
        "createdBy": "mathieu",
        "updatedBy": "mathieu"
    },

As you can see, I have two products (id 85 and id 86) that are included in my batch AND included in my "Goods" query. They're the same, they should'nt exist outside a batch.

db screenshot

I managed to not remove them from my Goods query with a foreach

  foreach (var item in query)
        {

            if (item is Batch)
            {
                var batch = item as Batch;
                query = query.Except(query.Where(p => batch.Products.Any(b => b.Id == p.Id)));
            }
        }

And the result

 {
        "products": [
            {
                "id": 85,
                "title": "500 Pièces LEGO",
                "details": "Boîte de 500 pièces LEGO pour construire des trucs mega top",
                "refPintel": "0002",
                "file": "lego-500pcs.pdf",
                "providerId": 2,
                "productType": "Jouet"
            },
            {
                "id": 86,
                "title": "1000 Pièces LEGO",
                "details": "Boîte de 1000 pièces LEGO pour construire des trucs mega top",
                "refPintel": "0001",
                "file": "lego-1000pcs.pdf",
                "providerId": 3,
                "productType": "Jouet"
            }
        ],
        "id": 78,
        "details": "Lot super LEGO 1500 pièces ! ",
        "title": "Lot LEGO TOP WOWOW",
        "price": 139.99,
        "indexId": "J",
        "refPintel": "0004",
        "isDiscountable": true,
        "productTypeId": 1,
        "isEnabled": true,
        "departmentId": 1,
        "season": "2018",
        "isBatch": true,
        "createdBy": "mathieu",
        "updatedBy": "mathieu"
    },
    {
        "products": [
            {
                "id": 88,
                "title": "Puzzle 3d 850 pieces - poudlard la grande salle",
                "details": "Édifiez la célèbre École de Magie et de Sorcellerie avec Poudlard - Grande Salle, un puzzle 3D en mousse de 850 pièces. Vous y retrouverez plusieurs lieux connus du merveilleux monde d'Harry Potter tel que la grande salle, le bureau du directeur ainsi que plusieurs autres. Vous pouvez combiner ce puzzle avec Poudlard - Tour d'astronomie  (référence 04031480 vendue séparément) et obtenir un puzzle 3D de 1725 pièces. Livret d'instructions inclus. Assemblage à tenons et mortaises, produisant des structures et assemblages 3D des plus solides. Conception et reproductions d'illustrations de qualité supérieure. Pour des heures de divertissement et de plaisir en famille. Dim. : L.50 x l.38 x H.47 cm.",
                "refPintel": "0003",
                "file": "puzzle-Poudlards.pdf",
                "providerId": 2,
                "productType": "Jouet"
            }
        ],
        "id": 87,
        "details": "lot de produits harry potter",
        "title": "Lot Harry Potter",
        "price": 199.9,
        "indexId": "J",
        "refPintel": "0005",
        "isDiscountable": true,
        "productTypeId": 1,
        "isEnabled": true,
        "departmentId": 3,
        "season": "2018",
        "isBatch": true,
        "createdBy": "mathieu",
        "updatedBy": "mathieu"
    },

This works as intended, my Products 85 and 86 are not being displayed. But as you may know, performances are suffering from the foreach. With the foreach, it takes about 10 seconds to get the result on a 1200 rows table. Without, 0.2 seconds.

My Question is how can I send to my user all my Goods, meaning Products and Batches, without having the products who belong to batches sent twice (only inside a batch, not like a product outside a batch), without "filtering" my query with a foreach.

I've tried some other things like playing around my modelbuilder etc... without succeeding...

Maybe I'm missing something obvious there, since I'm new to this, but I'm currently stuck with that performance issue.

If you need any clarification, details or anything else, just let me know.

Thanks for reading me.

edit: added a DB screenshot to show how it looks like if it can help undertanding the problem.

edit 2: added the output of the foreach.

edit 3 : edited the question... a bit.

grin
  • 11
  • 3
  • So you don't want to get all products then? It's not really clear what you want your output to be. – DavidG Mar 08 '18 at 10:24
  • I do want to get all the products, but these products are displayed twice. For example, the product with the ID 86, is meant to be in a batch. Nowhere else. This product is a single entry in my DB, there isn't another product with the ID 86 in it (the id of a product is auto incremented). These 'batch products' should'nt 'exist' outside a batch and in fact they do not. But the request i perform duplicates them... I don't know if made it clear this time. Tell me if not :) – grin Mar 08 '18 at 10:34
  • @DavidG Added the output I got when using the foreac (which is the one I should have). – grin Mar 08 '18 at 10:52
  • And what is your query? If you are querying for Goods, of course Goods of type Product will be returned (because Product **is a** Good). If you want to get Batches only, then query for Batch only, not for Goods. – Ivan Stoev Mar 08 '18 at 11:56
  • @IvanStoev I want to get both types, Products and Batches, which I manage to. My query is simple,( var query = Repository.GetAll().AsQueryable();). I've got a generic repository for all my classes. I don't know if it's clear enough on my first post, but as you can see, some products are duplicated. The ones that are meant to only be in a batch are aswell displayed outside the batch. I want to display my batch products only inside a batch, not twice. Again, maybe I'm missing something obvious, but the fact is I'm sending some object twice. – grin Mar 08 '18 at 12:02
  • If you want to see them only inside the batch (which is your specific custom requirement), then you should filter the root query to exclude them. EF will not do that for you. e.g. `var query = Repository.GetAll();` – Ivan Stoev Mar 08 '18 at 13:03
  • @IvanStoev I want to see all my goods, each of them displayed once ! When I query all my goods, does EF make to queries : e. g. Repository.GetAll() and Repository.GetAll(); and then Contact them ? Which would explain why my products are duplicated, or does it do it on a single query ? It might be logical to you that when I request goods, I get duplicated goods (the ones who belong to a batch), but not for me. – grin Mar 08 '18 at 13:15
  • There is no such term "belongs to". EF DbSets correspond to relational database tables. So logical or not, this is how it works. The root DbSets return ALL items, and collection navigation properties return sub sets of the same items. And btw, you should filter on `IQueryable` level, not `IEnumerable` or `foreach`. – Ivan Stoev Mar 08 '18 at 13:42
  • @IvanStoev Alright, thanks for your answers. "Belongs to" was meant to illustrate a bit my question, but yeah I understand what you mean. I found a solution which is not the best but at least seems to perform better, I'll edit my first post if it does'nt cause any issue. I used the foreach because it did the job at the beggining when I had a very few goods. I know it performs poorly overral. I do use IQueryable. – grin Mar 08 '18 at 14:07

0 Answers0