2
votes

I am using Entity Framework and Breeze. For an Entity, there is a bit of associated data I would like to provide with the entity. Getting this data is most efficiently done by querying the Entity table and joining to other tables; this query includes a group by sub-query.

I am attempting to tack this extra data on by adding it as a [NotMapped] field to the entity:

[NotMapped]
public string NotMappedField { get; set; }

So then I basically want to replace this webapi controller method

[HttpGet]
public IQueryable<MyObject> MyObjects()
{
    return _contextProvider.Context.MyObjects;
}

With something like this:

public IQueryable<MyObject> MyObjectsWithExtraData()
{
   var query = from o in _contextProvider.Context.MyObjects
               // big complex query
               select new MyObject
               {
                   FieldA = o.FieldA,
                   FieldB = o.FieldB,
                   // all fields
                   NotMappedField = x.ResultFromComplexJoin
               }
   return query;
}

This gives me an error:

The entity or complex type 'MyObject' cannot be constructed in a LINQ to Entities query.

I've tried this a few ways and it seems to fight me both from the EF side and the Breeze side. I need to keep this as returning something like IQueryable so I can filter from the client through webapi because doing something like a ToList() here causes memory issues due to the dataset size.

So my question is - is there a best practices kind of way to accomplish what I am attempting or can anyone provide a solution?

Update:

I have found you can return extra data alongside of your entity and still have access to the entity as a queryable from Breeze:

public object MyObjectsWithExtraData()
{
    var query = from o in _contextProvider.Context.MyObjects
                // big complex query....
                select new
                {
                    theObject = MyObject,
                    NotMappedField = x.ResultFromComplexJoin
                };
    return query;
}

and then from the client breeze side you can do something like this:

var query = breeze.EntityQuery
           .from("MyObjectsWithExtraData")
           .where("theObject.FieldA", "Equals", 1)
           .expand("theObject.SomeNavigationalProperty")
           .orderBy("theObject.FieldB");

Still not exactly what I was looking for but it is actually pretty slick.

2
possible duplicate of LINQ group by on data servicenlips
Why not just return the extra data in the HTTPResponse and then grab it after you finish querying? Access it by passing a parameter into your .then(function (data) { console.log(data.httpResponse); }PW Kad

2 Answers

0
votes

Take a look at the EntityQuery.withParameters method.

// client side
var q = EntityQuery.from("CustomersStartingWith")
        .withParameters({ companyName: "C" });


// server side
[HttpGet]
public IQueryable<Customer> CustomersStartingWith(string companyName) {
  var custs = ContextProvider.Context.Customers.Where(c => c.CompanyName.StartsWith(companyName));
  return custs;
}

You can also mix and match a combination of regular query predicates with these custom parameters.

0
votes

LINQ to entity can only construct pur "Data Transfert Object" : class containing only public properties with trivial getter and setter and without constructor. See my answer to a similar question here : https://stackoverflow.com/a/21174654/3187237

I specify my answer

An Entity class can't be instanciated in a LINQ to Entities query. If you want to construct similar (or almost similar) in the query you have to define an other class.

In your case you want to return object almost similar to your MyObject. So you have to define a class:

public class MyObjectExtended
{
    public string FieldA { get; set; }
    public string FieldB { get; set; }
    // ... all other MyObjetc fields
    public string ExtraFieldA { get; set; }
    public string ExtraFieldB { get; set; }
}

Now, your service can return a IQueryable<MyObjectExtended>:

public IQueryable<MyObjectExtended> MyObjectsWithExtraData() {
    var myQuery = from o in _contextProvider.Context.MyObjects
        // big complex query....
        select new MyObjectExtended {
           FieldA = o.FieldA,
           FieldB = o.FieldB,
           //... all fields ...
           ExtraFieldA = x.ResultFromComplexJoinA,
           ExtraFieldB = x.ResultFromComplexJoinB
    };
    return myQuery;
}

I hope this is what you are looking for.