0

I'm trying to implement jqgrid search on MVC, following the interesting answer by @Oleg, regarding the question: ASP.NET MVC 2.0 Implementation of searching in jqgrid.

Actually I have a data repository based on EF & DBContext. Moreover, I have an entity with 'calculated' fields, I mean properties in DbSets that are calculated on the base of other fields.

I have two main problems, implementing the solution described in the first answer of the above link:

1st problem) The solution is based on ObjectQuery. I believe I have solved by creating a IObjectContextAdapter of my context and then casting with (ObjectQuery)...in my ignorance, I do not exactly know if this solution may be regarded as scalable or if there is a better solution...I am sure it exists, but it is beyond my knowledge!

2nd problem) At first query, the following EntitySqlException is raised: 'Calculated' is not a member of type 'Models.Ticket' in the currently loaded schemes

May you give me some kind of help or suggestion to above problems, please?

Here I put some parts of code I think could clarify:

PUBLIC ENUM public enum StatiTT : int { A = 1, B = 2, C = 3, D = 4, E = 5, F = 6, G = 7 };

'TICKET' ENTITY

 public class Ticket : IValidatableObject
 {

  public DateTime Data1 { get; set; }    
  public int StatoTicketID { get; set; }
....
  public int Calculated  // here's the problem...this is not a real field, it's a calculated property, as you see...
   {
      get
       {
           int mm=0;

           DateTime Ora = DateTime.Now;

           mm = (Data1 - Ora).Days*1440 + (Data1 - Ora).Hours * 60 + (Data1 - Ora).Minutes;

           if (StatoTicketID > (int)StatiTT.DI && mm < 0) mm = 10000000; 

           return mm;
       }
   }

CONTEXT

public class dbContext : DbContext
   {

       public DbSet<Ticket> Tickets{ get; set; }
     ........

**REPOSITORY (actually not used in the above solution) **

public class myRepository : ImyRepository, IDisposable
{
    private dbContext context;

    public myRepository(dbContext context)
    {
        this.context = context;
    }

    public IQueryable<Ticket> ListTicketsQ()
    {
        return (from e in context.Tickets select e);
    }
    ..........

CONTROLLER

     public JsonResult jqIndex(string sidx, string sord, int page, int rows, bool _search, string filters)
    {
        var context = new dbContext();
        var objectContext = ((IObjectContextAdapter)context).ObjectContext;
        var set = objectContext.CreateObjectSet<Ticket>();

        var serializer = new JavaScriptSerializer();
        Filters f = (!_search || string.IsNullOrEmpty(filters)) ? null : serializer.Deserialize<Filters>(filters);
        ObjectQuery<Ticket> filteredQuery =
            (f == null ? (ObjectQuery<Ticket>)set : f.FilterObjectSet((ObjectQuery<Ticket>)set));
        filteredQuery.MergeOption = MergeOption.NoTracking; // we don't want to update the data
        var totalRecords = filteredQuery.Count();

        var pagedQuery = filteredQuery.Skip("it." + sidx + " " + sord, "@skip",
                                            new ObjectParameter("skip", (page - 1) * rows))
                                     .Top("@limit", new ObjectParameter("limit", rows));
        // to be able to use ToString() below which is NOT exist in the LINQ to Entity
        var queryDetails = (from item in pagedQuery
                            select new {     
                                            item.Calculated, // << THIS 'property' RAISES EntitySqlException
                                            }).ToList();
         .....

Any help would be appreciated. Thank you very much!

Community
  • 1
  • 1
Larry
  • 573
  • 5
  • 14
  • 31
  • I'm not really an EF person, but I think you would use a `DefiningQuery`. http://blogs.microsoft.co.il/blogs/gilf/archive/2008/05/14/how-why-use-definingquery-element.aspx – Ryan Jan 27 '12 at 01:12
  • Thanks @Ryan, but I really do not understand why I should use DefiningQuery...moreover, I am not using EDM XML and I apologize for not having specified, I am working with EF Codefirst...could you be more clear, please? THX – Larry Jan 27 '12 at 09:12
  • Sorry I don't know enough about EF code first to help. – Ryan Jan 27 '12 at 16:42
  • Thanks again @Ryan, I hope someone could help me...I am just a beginner...regards – Larry Jan 27 '12 at 17:11
  • 1
    To tell the trust I don't understand why you defines the calculated property inside of your C# object (in `Ticket` class). It seems me more logical either to define the calculated column in SQL or define calculated column in JavaScript code (in jqGrid). If you do prefer to do this in C# you can include the corresponding calculation of the property more directly instead of `item.Calculated`. In the case you will be free from encapsulation of the property insode of `ObjectQuery` or inside of Entity object. I think simple code in nice enough. The simpler the better. – Oleg Jan 28 '12 at 21:22
  • Thank you very much @Oleg, your help is precious. You are right. I made several calc fields, because I feel better with C# and I can have a better control of code. I have the entire class under the help of Intellisense and this spoiling me and help me and make my life easier. You're right about the simplicity, and this method makes things clearer to me... ObjectQuery apart. Sorry, when you tell "...you can include the corresponding calculation of the property more directly...", what do you mean? Could you explain me or put some suggestion, please? I am newbie. Thank you very much! – Larry Jan 30 '12 at 00:35

2 Answers2

1

It seems to me that you have some pure Entity Framework problems. I think that you can solve the problem by moving the calculation of the Ticket.Calculated property directly in the following statement

var queryDetails = (from item in pagedQuery
                    select new {
                        ...
                        (item.Amount + item.Tax), // Calculated directly
                        ...
                    }).ToList();

In the case the calculation of the property will not use Entity SQL and so you will have no EntitySqlException exception. Such approach should work. You can encapsulate the calculation of the property in any function if needed.

Another way can be the usage the calculation of the additional property directly in JavaScript code on the client side. For example if you need to display tree dependent columns in the grid: amount, tax and the total amount which is just the sum of amount and tax you can do this on the client side in the JavaScript code. jqGrid has beforeProcessing callback function which will be called before the data returned from the server will be processed. So You can enumerate items in the data.rows and set total property of every item as the sum of amount and tax (converted from String to Number). In the way you will reduce the size of data which will be send between server and the client.

Oleg
  • 220,925
  • 34
  • 403
  • 798
  • Thanks again @Oleg. The problem is that calculations made inside the properties are more complex than a simple operation. Inside property I have some conditional blocks too, such if and case and in some cases I change the returned value with specific enums or strings... – Larry Jan 31 '12 at 07:31
  • 1
    @Larry: You are welcome! The calculation can be done in any way in place of `(item.Amount + item.Tax)`. It doesn't important how complex is the calculation and from how many other properties of the object it's depend. If you can make the calculation inside of `Calculated` property you will be able to do the same in place of `(item.Amount + item.Tax)`. I don't see why not. – Oleg Jan 31 '12 at 07:51
  • THX. I added detailed code of `Calculated` property. Could you take it a look, giving me a LINQ example, if possible? I have other calculated properties more complex than this. Thanks again for your precious help! – Larry Jan 31 '12 at 15:27
  • @Larry: The code of the property `Calculated` contains too many unclear references: `Data1`, `StatoTicketID`, `StatiTT`. Are exists the same properties in the `pagedQuery`? I mean: can I use `item.Data1`, ... in the `from item in pagedQuery ...` ? – Oleg Jan 31 '12 at 16:42
  • You're right, sorry. I modified the question too, claryfing the references: `Data1` and `StatoTicketID` are properties of `Ticket` entity, while `StatiTT` is an enum. I hope to have clarified...thanks again for your help. – Larry Jan 31 '12 at 20:51
  • @Larry: You don't answer on my last question: can one uses `item.Data1`, `item.StatoTicketID` where the `item` is from the statement `from item in pagedQuery select {...}` – Oleg Feb 01 '12 at 07:09
  • Pls forgive me, I'm not sure I understand the question. I try to answer: the properties `Data1` and `StatoTicketID` are correctly accessible and usable even within the pagedQuery statement you specified... just accessing the calculated properties in question I get the exception...I hope you understand and have been sufficiently clear, THX again @Oleg – Larry Feb 01 '12 at 19:18
  • Moreover @Oleg: sorry, don't you think is should be possible to override dataset property with the calculated field? I mean to declare property as part of dbset (partial?) and then use calculated property in place of the stored one in dbset...after all, the exception talks 'Calculated' is not a member of type 'Models.Ticket' in the currently loaded schemes...if it were possible to 'cheat', making believe that you are using the entity in the scheme, you may be able to resolve the issue ... I apologize if the question may be silly, I'm just a beginner. THX! – Larry Feb 01 '12 at 19:44
  • @Larry: If you define calculated property on the low level like dbset the property should be understandable for all above layers like `ObjectQuery` and LINQ to Entity. LINQ to Entity have many restrictions. The exception which you receive shows that the calculated property really is a problem for LINQ to Entity. My suggestion is to calculate the same property *after* you select all properties from which 'Calculated' depend and calculate if from the properties of `pagedQuery`. – Oleg Feb 01 '12 at 20:32
  • @Larry: Another way which you can try is to call `SqlFunctions.StringConvert((double)item.Calculated)` instead of `item.Calculated`. Probably your problem is `ToString()` only? – Oleg Feb 01 '12 at 20:39
  • THX @Oleg...based on the lessons so far and following your advice, I decided to do as you say and to calculate the properties acquired after the other by which they depend ... But I do not know how to separate the properties calculated to be excluded in the `FilterObjectset`... – Larry Feb 01 '12 at 20:45
  • ... Basically, I have two problems: 1) The calculated properties must be filtered from jqGrid as other fields 2) This means, you will agree with me however, that when invoked `serializer.Deserialize (filters)`, these properties must be properly split off from those actually present in the schema and only later re-used (possibly with a `let` clause), to make any further operation with the set filters... – Larry Feb 01 '12 at 20:46
  • ...Would you have any advice or guidance on the methodology to be adopted in order to obtain the desired behavior? In addition to the above, I would suggest you to read [this topic](http://stackoverflow.com/questions/4683424/entity-framework-and-pure-poco-update) ... who knows you with your experience and knowledge can not derive some benefit, compared to the previous question. Thank you, you're really kind, as well as the competent. – Larry Feb 01 '12 at 20:47
  • About SqlFunctions.StringConvert hint, unfortunately it is not the a string conversion problem. In fact, even tried to make a subquery, making a `select new **mymodel** { }`, but I get exception during subquery execution. Moreover, I do not arrive to convert that value to string, cause exception is raised after `pagedQuery`... – Larry Feb 01 '12 at 21:00
  • @Larry: I don't full understand you. Did you just tried to use `SqlFunctions.StringConvert((double)item.Calculated)` instead of `item.Calculated` in your query? It can be that `EntitySqlException` exception you have just because of the problem to convert int to string. – Oleg Feb 01 '12 at 21:02
  • @Larry: Do you can make `(from item in pagedQuery select new { item.Data1, StatoTicketID})ToList()` or you gat an exception too? – Oleg Feb 01 '12 at 21:05
  • About last question, if I make `(from item in pagedQuery select new { item.Data1, StatoTicketID})ToList()` I get an exception too. I did debug and trying to expand results of `pagedQuery`, the problem is already present. The problem is caused by the fact that **`Calculated` is the default order field! Then `sidx` contains it**...I am going to remove it as default and follow your hints again... – Larry Feb 01 '12 at 21:33
  • 1
    @Larry: I think that you try to start with too complex solution. Just make a grid which work and then make it more complex. If you will see that your main problem will be the restrictions of the Entity Framework you can just go to the `SQLCommand` and `SqlCommand.ExecuteReader` and work just with `SqlDataReader`. If you knows good SQL language it could be good alternative. In one project which I implemented for the customer I did this. Of cause the approach based on old technology and not perfect from design point of view, but it get you the best performance and all will sure work. – Oleg Feb 01 '12 at 21:45
  • OK. I think you are right. **Anyway the grid worked fine, even with calculated fields**. The problem was introduced when I wanted to implement search. I liked your solution, but evidently `ObjectContext` is not compatible with properties outside schema...I'll make other tries, then switch to `SqlCommand` if necessary. – Larry Feb 01 '12 at 21:52
  • 1
    @Larry: OK! Another way will be just to use Dynamic LINQ extension. In the case you don't use some advantages of `ObjectContext`, but LINQ have less restrictions inclusive `ToString()` methods for example. To do this you need just convert `pagedQuery` to `IQueryable` and then use Dynamic LINQ extension. I recommend you just be more pragmatic and do not look only the things which are nice. – Oleg Feb 01 '12 at 22:00
0

The best working solution to my problem has been resolved following the precious hints of Mr @Oleg: I moved the calculated properties into SQL Server, creating Computed Columns for each property. Now it works fine and it is really fast!

I lost more time trying to get working the calculated properties with ObjectSet, than create new computed columns directly in the db! As rightly pointed by Oleg, simple things are always the best!

Just another hint, for who's using EF Codefirst: if you want to use computed properties, you must DROP COLUMNS after db creation and putting [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribute above property, as specified in here too.

Thank you very much Oleg! I hope this solution may help other people!

Community
  • 1
  • 1
Larry
  • 573
  • 5
  • 14
  • 31