0
votes

I have a situation when I needed to bring custom data for child entities along with some properties from their parents..Here is my Code:

Models:

public class InvoiceHeader
{

    public int Id { get; set; }
    public short LocationId { get; set; }
    public DateTime EntryDate { get; set; }

    // some other properties

     public Location Location { get; set; }

}

public class InvoiceItemsDto
{

    public int Id { get; set; }
    public int InvoiceHeaderId { get; set; }

    public string ProductName { get; set; } // Not mapped

    // some other properties
    // Added to get the headers entry date and location:

    public short LocationId { get; set; } // Not mapped
    public DateTime EntryDate { get; set; } // Not mapped
}

Controller:

[HttpGet]
    public IQueryable<InvoiceItemsDto> InvoiceItemsHeaders()
    {
        return _contextProvider.Context.InvoiceItems.Select(a => new InvoiceItemsDto()
        {
            Id = a.Id,
            HeaderId = a.HeaderId,
            // Some Other properties
             ProductName = a.Product.ProductName,
            EntryDate = a.InvoiceHeader.EntryDate,
            LocationId = a.InvoiceHeader.LocationId
        });
    }

Javascript:

metadataStore.registerEntityTypeCtor(
        'InvoiceItems',InvoiceItemInit , InvoiceItemInitializer);

// Here goes the unmapped properties
function InvoiceItemInit() {
        this.ProductName = "";
        this.LocationId = 0;
        this.EntryDate = new Date();

    }

    //   For Css styling only:
     function InvoiceItemInitializer(Item) {
         Item.isSelected = ko.observable(false);
        Item.isDeleted = ko.observable(false);
        Item.focused = ko.observable(false);
     }


// Action:
var locationId_p = Predicate.create('LocationId', '==', locationId());

var dateFrom_p = Predicate.create('EntryDate', '>=',  dateFrom().toJSON()); // throws an error.

var dateTo_p = Predicate.create('EntryDate', '<=', dateTo().toJSON()); //throws an error.

var ProductName_p = Predicate.create('ProductName', 'contains', productName()); // throws an error.

var whereClause = locationId_p.and(dateFrom_p).and(dateTo_p).and(ProductName_p);

var query = EntityQuery.from('InvoiceItemsHeaders').toType(entityNames.invoiceItem)
               .where(whereClause)
               .skip(skipPage * 50)
               .take(50)
              .orderBy('headerId')
              .inlineCount(true);
        ;

The errors are JSON serialization stuff (e.g. ProductName is not surrounded with %27% which I believe it's a string indicator)

Changing the code for predicates to:

 var dateFrom_p = Predicate.create('EntryDate', '>=', "datetime'" + dateFrom().toJSON() + "'");

var dateTo_p = Predicate.create('EntryDate', '<=', "datetime'" + dateTo().toJSON() + "'");

var ProductName_p = Predicate.create('ProductName', 'contains', "'" + productName() + "'");

works.. However, that does not apply to mapped properties as they are being JSON'd correctly..

Is there any other way to work around this unmapped dates and strings?

EDIT:

I have edited the original post for correcting (ONLY) the Pascal and camel cases (my bad)

To clarify:

  1. All the mentioned properties within the predicates are unmapped i.e LocationId, ProductName and EntryDate that's why they have to be in PascalCase.

  2. The problem I get is with predicates on those properties (Except for LocationId (int type))

  3. locationId() , dateFrom(), dateTo() and productName() are the observables' values which the user types/chooses

  4. Executing the query with the following predicates:

    Predicate.create('EntryDate', '>=', dateFrom());
    
    Predicate.create('EntryDate', '<=', dateTo());
    

results in the following error:

"')' or operator expected at position 65 in '(((LocationId eq 1) and (EntryDate ge Mon Feb 02 2009 00:00:00 GMT+0300 (Arab Standard Time))) and (EntryDate le Sun Jan 18 2015 04:04:46 GMT+0300 (Arab Standard Time))'."

URL Generated:

http://localhost:8743/breeze/breeze/InvoiceItemsHeaders?$filter=(((LocationId%20eq%201)%20and%20(EntryDate%20ge%20Mon%20Feb%2002%202009%2000%3A00%3A00%20GMT%2B0300%20(Arab%20Standard%20Time)))%20and%20(EntryDate%20le%20Sun%20Jan%2018%202015%2003%3A18%3A28%20GMT%2B0300%20(Arab%20Standard%20Time))&$orderby=HeaderId&$top=50&$inlinecount=allpages

P.S. I don't get this error on a mapped property of type datetime which all (mapped or unmapped) properties values come from the known jquery calender for the user to choose the desired dates.

So I had to use the toJson() to fix the datetime format in an appropriate Json format:

console.log(dateFrom());// Mon Feb 02 2009 00:00:00 GMT+0300 (Arab Standard Time)

console.log(dateFrom().toJSON());// 2009-02-01T21:00:00.000Z

I've cheated the correct url that comes from a predicate on a mapped property of type datetime then I had to come up with the following in order to make the query work:

Predicate.create('EntryDate', '>=', "datetime'" + dateFrom().toJSON() + "'");

Predicate.create('EntryDate', '<=', "datetime'" + dateTo().toJSON() + "'");

As for the other unmapped property ProductName which is of type string the scenario is a bit different, running with the following predicate:

Predicate.create('ProductName', 'contains', productName());

Results in:

Error message:

"Could not find a property named 'abc' on type 'BreezeProj.InvoiceItemsDTO'"

url:

http://localhost:8743/breeze/breeze/InvoiceItemsHeaders?$filter=(substringof(abc%2CProductName)%20eq%20true)

'abc' here is a value passed to ProductName()

Again, I had to come up with the following for the query to work:

Predicate.create('ProductName', 'contains', "'" + productName() + "'");

Any thoughts? Thanks

My response to Ward's second edit:

InvoiceItems is the entityType, that's how it's named in the database, I used InvoiceItemsDto as a view to get the properties needed. I could've simply expanded the InvoiceHeader for EntryDate and Location or Product for ProductName but I would find myself expanding many other navigation properties many times at the client side, which is troublesome.

EntryDate - for instance- is a property that is mapped to InvoiceItemsDto but not to the acrual entity InvoiceItems, hence, entityNames.invoiceItem is InvoiceItems

Posting the whole code above does not mean I want you to get sucked into the vortex of my application.

Perhaps I lack the ability to clear out my intentions, but my question is indeed small and focused "Why Breeze didn't realize the data types of the "unmapped" properties with predicates when it does realize them in query results? "

Anyways, here is a link to the metadata you've requested:

http://pastebin.com/KGZKFLUU

again, Sorry for inconvience.

1

1 Answers

1
votes

There are too many moving parts for my head to wrap around here. You're talking about multiple, unspecified errors. You've got predicates that touch multiple properties and I can't tell which are problematic. You're claiming a serialization error for one of the mapped properties (productName) but not saying what that that error is ("JSON serialization stuff" does not count).

It would be ideal if you could break this down to a tiny, executable (e.g., plunker) sample that focuses on one problem. Get rid of everything that doesn't matter (e.g, InvoiceItemInitializer, the distracting clauses in the query).

A few things leap off the page:

  • The InvoiceItemInit defines a ProductName (PascalCase) but your query predicate shows it as productName (camelCase) and the other properties appear to be camel.

  • If a property on the server is unmapped, I wouldn't expect that Breeze would allow a query that specified that property (not certain about that but it seems problematic).

  • You didn't show us the pertinent EntityType metadata on the client for the entities of interests so we can't really tell what's going on.

  • Why the .toJson calls in the predicate comparison values? Where have you seen that done before? I've neither done it myself nor seen it in any example code.

  • Why are the comparison values coming from functions? Why productName() instead of productName or 'foo'?

When you do highly unusual things, it's hard for us to guess where you stepped off the path. An executable repro may be your best chance for getting an answer.

My response to your Jan 16 response

That's better, thanks.

But many things still don't make sense to me. The most important gap: I can't see your client-side metadata so I don't know what Breeze thinks are the EntityTypes involved.

Please, whatever you do, do not paste your metadata here in full. Put it in a pastebin or a gist and reference it in your question.

You register a ctor for 'InvoiceItems' but that's not an EntityType as far as I can tell. 'InvoiceItems' looks more like a resource name, a target for a query; it's not obviously a type name (type names are typically singular, btw).

I see you are casting the query: .toType(entityNames.invoiceItem). What is entityNames.invoiceItem?

I don't see why you expect 'InvoiceItems' to be treated as an EntityType. I kind of get that it relates to InvoiceItemsDto but I don't see how Breeze could know that.

I don't know why you created the properties as unmapped in the first place when they are clearly mapped to the properties of InvoiceItemsDto. There is something fundamentally disconnected about InvoiceItemsDto and InvoiceItems.

I confess I also don't understand why Breeze didn't realize the data types of the "unmapped" properties defined in your ctor. But with everything else amiss I'm not sure what is going on.

Perhaps you can step back and tell me why you have the DTO in the first place and why you're trying to both hide and show properties that are actually on your InvoiceHeader business class.

I'm half afraid to hear the answer because I feel myself being sucked into the vortex of your application. That's not what we do here on S.O. We answer small focused questions; we don't wade through people's applications.

That's why it is so important for you to provide a working sample that is limited to one problem at a time.

Forgive my abrupt language. I do not mean to be so brusque. It's late for me too and I'm frustrated that I can't help when there are so many moving and missing pieces.

You may want to consider a short professional services engagement with IdeaBlade to help you get back on track.