Implementing $select with WebApi and ODataQueryOptions - odata

I'm trying to implement some OData functionaility with a custom DAL using ODataQueryOptions.
My DAL uses design time generated typed data tables. By intercepting the SelectExpand property of ODataQueryOptions i can get our DAL to only load the columns required.
How do i then return just the data required.
I am currently tipping the data from our type datatables into a ListOf some typed data tranfer objects but then end up with lots of null data from the columns which aren't required.
I feel like i should be able do some LINQ query to select the just the columns I need straight from the typed datatable bypassing using typed DTOs altogether. Is this possible?

You need to do the same thing that SelectExpandQueryOption.ApplyTo does.
1) Optimize the query to the backend. Instead of getting the whole entity from the database, get only the properties the client asked for and wrap that result in an IEdmEntityObject. Return the collection as EdmEntityObjectCollection. This step is optional. You could choose to ignore this step and return IQueryable and still get $select to work.
2) Tell the OData formatter to serialize only the requested fields. This can be done by stuffing the SelectExpandClause on the Request object using the extension method Request.SetSelectExpandClause.
public class CustomersController : ODataController
{
public IEnumerable<Customer> Get(ODataQueryOptions<Customer> query)
{
Customer[] customers = new[] { new Customer { ID = 42, Name = "Raghu" } };
// Apply query
var result = customers;
// set the SelectExpandClause on the request to hint the odata formatter to
// select/expand only the fields mentioned in the SelectExpandClause.
if (query.SelectExpand != null)
{
Request.SetSelectExpandClause(query.SelectExpand.SelectExpandClause);
}
return result;
}
}

Related

Partial Lookup Lists

I'm trying to limit the amount of data coming across when implementing Lookup Lists in Breeze.JS. The Lookup Lists sample uses queries that results in full objects but I would like to project the entities to fewer properties (e.g. primary key, foreign keys, and a descriptor property) and still have Breeze.JS recognize the entity type on the client. I know how to do the projections from the client to get partials but how would I do that with Lookup Lists (either from the client or the server Web API)?
You might satisfy your intentions with a custom JsonResultsAdapter.
You're probably wondering "What is a JsonResultsAdapter?"
That's what breeze uses to interpret the JSON arriving from the server. You can read about here and here.
Perhaps more helpful is to look at the adapter in the Web API dataservice and at the example adapter from the "Edmumds" sample.
The Edmunds sample demonstrates translating a JSON source that you don't control into breeze entities.
In this case, your JsonResultsAdapter would look at each node of JSON and say "this is a Foo, this is a Bar, and that one is a Baz". Accordingly, for each of these nodes it would return { entityType: "Foo" }, return { entityType: "Bar" }, and return { entityType: "Baz" }
Now breeze knows what to do and creates corresponding entities out of the Lookups payload.
Remember to mark these entities as partial, in the same way you would if you had made a projection query that targeted a single entity type.
Fortunately, the Lookups query returns the container object that holds the Foo, Bar, and Baz collections. So you can iterate over these and mark them partial right there in the query success callback.
Once you wrap your head around THAT ... you'll want to know how to put your custom JsonResultsAdapter to work in the Lookups query ... and ONLY in the Lookups query.
You can enlist that JsonResultsAdapter exclusively for your Lookups query with a using clause.
Here's an example:
var jsa = new breeze.JsonResultsAdapter({
name: 'myLookupsJsa',
visitNode: function() {...}
});
query = query.using(jsa);
Is this overkill? Would you be better off making three trips?
Only you will know. I would like to hear from you when you try it ... and give us your suggestions on how we might make this easier in a general way.
In the lookup lists example the controller action looks like this:
[HttpGet]
public object Lookups() // returns an object, not an IQueryable
{
var regions = _contextProvider.Context.Regions;
var territories = _contextProvider.Context.Territories;
var categories = _contextProvider.Context.Categories;
return new {regions, territories, categories};
}
You could reduce the footprint using a server-side projection like this:
[HttpGet]
public object Lookups() // returns an object, not an IQueryable
{
var regions = _contextProvider.Context.Regions
.Select(x => new { id = x.RegionId, name = x.RegionName });
var territories = _contextProvider.Context.Territories
.Select(x => new { id = x.TerritoryId, name = x.TerritoryName });
var categories = _contextProvider.Context.Categories
.Select(x => new { id = x.CategoryId, name = x.CategoryName });
return new {regions, territories, categories};
}
This approach does not answer this part of your question:
and still have Breeze.JS recognize the entity type on the client
Not sure what the solution or use case is for this piece.

Web API OData Actions with Entity as parameter

I have a requirement to encapsulate pieces of business logic within a transaction in an OData Web API service. Some of these pieces will need to accept one or more entities.
An example use case might be StockProduct and this might accept a Product entity and a Location entity. It would create the product and update stock records for the Location.
The approach I've taken is to create an unbound OData action that accepts these entities so that both of these can be operated on in a single transaction. Unfortunately neither can Entities be used as an ODataActionParameter nor can they be part of a class and used as a complex parameter.
I can think of a two ways around this:
Create a DTO class that is not an entity that is a mirror of each of my mirror classes and convert from DTO to Model within my action. The problem here is that I already have a DTO for each Model eg. Product.cs and ProductDTO.cs and don't really want to have to create a third class. (Currently, the ProductDTO.cs is used for Posts, Puts, Patches and Deletes and the Product.cs is used for Gets).
Abandon OData actions and create a simple end point that accepts whatever I like. I'm not keen on going down the second route as I'd like to use OData exclusively.
Any thoughts or suggestions?
You can use the ActionConfiguration.EntityParameter() method to bind an entity as a parameter to your OData action method.
Here is an example:
ActionConfiguration validate = ModelBuilder.EntityType<TEntity>()
.Collection.Action("Validate");
validate.Namespace = "Importation";
validate.EntityParameter<TEntity>(typeof(TEntity).Name);
validate.CollectionParameter<string>("UniqueFields");
validate.Returns<ValidationResult>();
However, note that the ModelState will not check against the content of the supplied Entity and will set any missing properties to null and properties exceeding the StringLength(x) annotation in your model will still pass. If you wish to validate the entity itself after, use this bit of code in your action method:
[HttpPost]
public virtual IHttpActionResult Validate(ODataActionParameters parameters)
{
//First we check if the parameters are correct for the entire action method
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
//Then we cast our entity parameter in our entity object and validate
//it through the controller's Validate<TEntity> method
TEntity Entity = (TEntity)parameters[typeof(TEntity).Name];
Validate(Entity, typeof(TEntity).Name);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IEnumerable<string> uniqueFields = parameters["UniqueFields"] as IEnumerable<string>;
bool result = Importer.Validate(Entity, uniqueFields);
return Ok(result);
}
}
As for your StockProductDTO, it seems to me that this is an new Business Entity by itself and should be treated as such.
You can use a batch request to perform multiple operations within a single request. This allows you to use your existing controllers for inserting your two objects.
https://aspnetwebstack.codeplex.com/wikipage?title=Web+API+Request+Batching

mvc in asp.net doesnt satisfy programmers

I am learning MVC4 in Visual Studio and I have many questions about it. My first statement about MVC is that MVC's Model doesnt do what I expected. I expect Model to select and return the data rows according to the needs.
But I read many tutorial and they suggest me to let Model return ALL the data from the table and then eliminate the ones I dont need in the controller, then send it to the View.
here is the code from tutorials
MODEL
public class ApartmentContext : DbContext
{
public ApartmentContext() : base("name=ApartmentContext") { }
public DbSet<Apartment> Apartments { get; set; }
}
CONTROLLER
public ActionResult Index()
{
ApartmentContext db = new ApartmentContext();
var apartments = db.Apartments.Where(a => a.no_of_rooms == 5);
return View(apartments);
}
Is this the correct way to apply "where clause" to a select statement? I dont want to select all the data and then eliminate the unwanted rows. This seems weird to me but everybody suggest this, at least the tutorials I read suggest this.
Well which ever tutorial you read that from is wrong (in my opinion). You shouldn't be returning actual entities to your view, you should be returning view models. Here's how I would re-write your example:
public class ApartmentViewModel
{
public int RoomCount { get; set; }
...
}
public ActionResult Index()
{
using (var db = new ApartmentContext())
{
var apartments = from a in db.Apartments
where a.no_of_rooms == 5
select new ApartmentViewModel()
{
RoomCount = a.no_of_rooms
...
};
return View(apartments.ToList());
}
}
Is this the correct way to apply "where clause" to a select statement?
Yes, this way is fine. However, you need to understand what's actually happening when you call Where (and various other LINQ commands) on IQueryable<T>. I assume you are using EF and as such the Where query would not execute immediately (as EF uses delayed execution). So basically you are passing your view a query which has yet to be run and only at the point of where the view attempts to render the data is when the query will run - by which time your ApartmentContext will have been disposed and as a result throw an exception.
db.Apartments.Where(...).ToList();
This causes the query to execute immediately and means your query no longer relys on the context. However, it's still not the correct thing to do in MVC, the example I have provided is considered the recommended approach.
In our project, we will add a Data Access Layer instead of accessing Domain in controller. And return view model instead of Domain.
But your code, you only select the data you need not all the data.
If you open SQL Profiler you'll see that's a select statement with a where condition.
So if it's not a big project I think it's OK.
I can't see these tutorials but are you sure it's loading all the data? It looks like your using entity framework and entity framework uses Lazy laoding. And Lazy loading states:
With lazy loading enabled, related objects are loaded when they are
accessed through a navigation property.
So it might appear that your loading all the data but the data itself is only retrieved from SQL when you access the object itself.

Breeze EFContextProvider returning different data than standard DbContext

I have a breeze controller that is returning different JSON than a regular ole APIController.
My Breeze.js controller has a method that looks like this:
[HttpGet]
public IQueryable<Application> Applications()
{
var admin = _contextProvider.Context.Administrators.Include(i => i.Applications).Single(o => o.Name == User.Identity.Name);
return admin.Applications.AsQueryable();
}
I have a traditional ApplicationsController: ApiController that has a method like this:
public IEnumerable<Application> Get()
{
var admin = myDbContext.Administrators.Include(i => i.Applications).Single(o => o.Name == User.Identity.Name);
return admin.Applications.AsQueryable();
}
Basically, the code is identical. However, the response body values are not. The Breeze response body contains {$ref: "3"},{$ref: "4"} whereas the traditional WebAPI controller response is showing the proper object values.
Thoughts?
Dan
The [BreezeController] attribute on your ApiController changes the default JSON.net serialization settings so that breeze can serialize entity graphs without repeating the same entity multiple times. The default serializer does not do this. If you apply the [BreezeController] attribute to your api controller you should see the '$ref' values show up.
I was able to fix my issue by rewriting the LINQ to the following:
return from u in context.Administrators
where u.Name == administratorName
from d in u.Applications
select d;
I reread the documentation provided by Breeze and they emphasize not returning circular references. For those reading this, make sure your DbContext has the following in its constructor.
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
I also removed any "virtual" navigation properties.
My original LINQ had an Include statement that would return circular references. Before using a BreezeController, the standard APIController returned the data to the client accurately, just with repeated data. A BreezeController is tripped up by the circular reference and the data returned was just...well...weird and wrong.

How should I structure my ViewModel for this hierarchical data I need to display in ASP.NET MVC?

I have a view that will look like this:
I'm trying to figure out how I should represent my ViewModel for this view. Each "Agency" can have multiple "Business Units" and each "Business Unit" can have several "Clients".
In the database I easily represent this with a mapping table and foreign keys for the Agency, BusinessUnit and Client tables.
However, now I need to use LINQ to query this data out of the database and then construct a ViewModel object that represents this tree-like structure so my View can render it.
Can anyone give me tips on what data-structure I should use, or what my ViewModel might look like for this hierarchical structure in C# code? I need to create my ViewModel object to pass on to this view.
Any suggestions on how to represent the ViewModel are appreciated!
Just store a List instance in your view data?
public class Agency
{
public List<BusinessUnit> Units;
public string Name;
public int NumberOfAccounts
{
get
{
int ret = 0;
foreach(BusinessUnit unit in units)
ret += unit.NumberOfAccounts;
return ret;
}
}
// ... continue this strategy for other properties
}
public class BusinessUnit
{
public List<Client> clients;
public string Name;
public int NumberOfAccounts
{
get
{
int ret = 0;
foreach(Client client in clients)
ret += client.NumberOfAccounts;
return ret;
}
}
// ... continue this strategy for other properties
}
public class Client
{
public string Name;
public int NumberOfAccounts;
}
Assuming that your Linq2Sql implementation has the same relationships in it as the database (which if you did drag and drop to the designer, they definitely do), here's how I would approach it.
I would create a strongly typed partial view of type Agency that would represent each section (Agency, in your case), call it AgencyReportSection.ascx. This control will take an agency, iterate through its business units, which in turn iterate through its clients.
Wherever you are packaging up your data, do something like this:
DataContext context = new DataContext();
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Agency>(a => a.BusinessUnit);
options.LoadWith<BusinessUnit>(b => b.Client);
context.LoadOptions = options;
What this will give you is that when the context gets an agency, it will follow the defined relationships and give you those objects as well. So you get:
Agency a = context.Agency.FirstOrDefault();
IEnumerable<BusinessUnit> units = a.BusinessUnits;
IEnumerable<Client> clients = units.Clients;
Your view could do something like:
<% foreach(var agency in agencies)%{>
<% Html.RenderPartial("AgencyReportSection"); %>
<%}%>
The reason you are doing the data load option is to avoid lazy loading in the view, let the model pack up all the necessary data.
I hope I've understood your question correctly...
I have recently been using the "ADO .Net Entity Data Model" template to control connectivity to a MSSQL database that supports hierarchy data and it has been working well.
You can just tie your presentation layer directly to the data models.
If your keys are set correctly in the database its a snap to get up and running..I think it also requires ADO.Net 3.5
Creating ADO.NET Entity Framework Entity
MS information on entity

Resources