web api : checking odata query - asp.net-web-api

I have a web api exposing a list of sessions. This is my code :
[RoutePrefix("api/data")]
public class SessionController : ApiController
{
[HttpGet]
[Route("sessions")]
[Queryable]
public IQueryable<Session> Get()
{
List<Session> list = new List<Session>();
list.Add(new Session { Id = 1, Name = "name 1", Place = "place 1", SessionOn = Convert.ToDateTime("1/1/2014") });
list.Add(new Session { Id = 2, Name = "name 2", Place = "place 2", SessionOn = Convert.ToDateTime("2/1/2014") });
list.Add(new Session { Id = 3, Name = "name 3", Place = "place 3", SessionOn = Convert.ToDateTime("3/1/2014") });
return list.AsQueryable();
}
}
public class Session
{
public int Id { get; set; }
public string Name { get; set; }
public string Place { get; set; }
public DateTime SessionOn { get; set; }
}
An user can request this api to see all sessions like this :
mydomain/api/data/sessions
I have added the oData to allow user querying and filtering those data like this :
mydomain/api/data/sessions?$filter=Name eq 'name1'
mydomain/api/data/sessions?$filter=Place eq 'place 1'
Everything is working well, the only problem remaining is that I would like to check the query given by the user to tell him that a value is not valid for example :
In my list of sessions, the possible values for the field 'Place' are :
place 1
place 2
place 3
place 4
If the user do the following request :
mydomain/api/data/sessions?$filter=Place eq 'placezzzzz 1'
He will just get an empty sets of Session. What I would like to do, is check in my backend code api the value given (that is to say 'placezzzzz 1') and returns a response to the user telling that this value is invalid.

You could change your method signature to include a ODataQueryOptions<T> parameter:
[EnableQuery]
public IQueryable<Session> Get(ODataQueryOptions<Session> options) {
//Do something fun with the filter:
var filter = options.Filter;
}
The options argument can be added to your Get method without changing the routing. It contains all kinds of info about the request, including the filter that was used. You can still return your IQueryable, just like you are used to.
I use the [System.Web.OData.EnableQuery] attribute instead of [Queryable] because QueryableAttribute is obsolete. (source msdn) I'm not sure this solution will work with the older version.

Related

How to query and return all documents in documentdb using linq

let problemDocument = documentClient.CreateDocumentQuery<ProblemDatabaseModel>("")
problemDocument
doesn't seem to work
(problemDocument.Select(fun problem -> problem))
doesn't seem to work
(problemDocument.Where(fun problem -> problem.id = problem.id))
doesn't seem to work either. Any ideas?
If you want to query all document in document db, please try to below code:
documentClient.CreateDocumentQuery<ProblemDatabaseModel>("").ToList();
Please note that, we can store different json entity in documentDB, if document property does not in your data model, it will give a default value. I have a simple test for this:
Data model:
public class Cred
{
[JsonProperty(PropertyName = "id")]
public string ID { get; set; }
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
[JsonProperty(PropertyName = "credits")]
public int Credits { get; set; }
[JsonProperty(PropertyName = "authordetails")]
public AuthDetail AuthInfo { get; set; }
}
If json data in documentDB is:
{
"id": "CDC103",
"title": "Fundamentals of database design",
"authordetails": {
"Name": "dave",
"Age": 33
},
"custom":"test"
}
client.CreateDocumentQuery<Cred>(UriFactory.CreateDocumentCollectionUri("jambordb", "jamborcols")).ToList();
Here is the result:
From the screenshot we know that, the property "custom" will not included in our data model. and the credits will give a default value 0.

Include() only specific property

Here I am retrieving items and including the creator of the item. The goal is to include only the first and last name from the creator, not the entire user model.
var items = _db.Items.Include("Creator")
The item model has Creator as a navigation property like this:
public User Creator { get; set; }
It works fine, but it loads the entire user model, when really I just want the first name and last name.
How do I specify I only want specific property returned from the user model?
You cannot do that using Include. You can use Select instead:
var items = _db.Items.Select(i => new { Item = i, Creator = new { i.Creator.FirstName, i.Creator.LastName } });
Update
If you need to return that query as method result you have to create a class which could hold the results:
public class ItemWithCreatorNames
{
public Item Item { get; set; }
public string CreatorFirstName { get; set; }
public string CreatorLastName { get; set; }
}
var items = _db.Items.Select(i => new ItemWithCreatorNames { Item = i, CreatorFirstName = i.Creator.FirstName, CreatorLastName = i.Creator.LastName });

MVC 3 dropdownfor populated but cannot get user's selection

I'm new to MVC 3. I want 3 dropdown lists. The user picks an item from each list and then retrieves the selections based on the matches. For my problem I'm just using 2 dropdowns and code snippets. I have all 3 dropdowns populated - the first two from Linq to Sql and the 3rd is an option list created with Razor (A-Z).
I'm missing the step of setting the values of what was chosen into MemberSetup.SelectedProgramID and MemberSetup.SelectedOrganizationID. I'd like the text stored in MemberSetup.SelectedProgramName and MemberSetup.SelectedOrganizationName respectively. I think if I can get the selected ids into the model, I can pass them through ActionLink and not deal with #Html.Hidden variables. However, no matter what is selection the model SelectedProgramID and SelectedOrganizationID are zero (expect 1, 2, 3 or 4 for Program ID and 1, 2, 5, or 6 for Organization ID).
Any help / corrections would be appreciated.
Model:
{
public int SelectedProgramID { get; set; }
public string SelectedProgramName {
get { return this._myProgramName; }
set { this._myProgramName = value; }
}
public IEnumerable<SelectListItem> ProgramList { get; set; }
public int SelectedOrganizationID { get; set; }
public string SelectedOrganizationName
{
get { return this._myOrganizationName; }
set { this._myOrganizationName = value; }
}
public IEnumerable<SelectListItem> OrganizationList { get; set; }
}
Controller:
{
var db = new STARDataContext();
MemberSetup setupModel = new MemberSetup();
setupModel.ProgramList = db.ProgramAlls.ToList()
.Select(p => new SelectListItem
{
Value = p.ProgramID.ToString(),
Text = p.ProgramName.ToString()
});
setupModel.OrganizationList = db.AreaAlls.ToList()
.Select(p => new SelectListItem
{
Value = p.AreaID.ToString(),
Text = p.AreaName.ToString()
});
return View(setupModel);
}
View:
{
#using ( Html.BeginForm() ) {
#Html.DropDownListFor(model => model.SelectedProgramID, Model.ProgramList)
#Html.DropDownListFor(model => model.SelectedOrganizationID, Model.OrganizationList, "Please select Organization", new { #class = "DropDownList" } )
#Html.Hidden("SelectedProgramID", Model.SelectedProgramID)
#Html.Hidden("SelectedProgramName", Model.SelectedProgramName)
#Html.Hidden("SelectedOrganizationID", Model.SelectedOrganizationID)
#Html.Hidden("SelectedOrganizationName", Model.SelectedOrganizationName)
#Html.ActionLink("Get Members", "Select", new {programID=Model.SelectedProgramID,orgID=Model.SelectedOrganizationID })
}
}
use hiddenFor instead of hidden,so your syntax should look like this
#Html.HiddenFor(a=>a.SelectedProgramName)
Since you have SelectedProgramID and SelectedOrganizationID defined, you won't need to use hidden field for it. To construct the dropdown, you should do something like this
Html.DropDownListFor(x=>x.ID,
new SelectList(Model.Products,"ID", "Sku", Model.ID), " select ")
For your reference: Use Html.DropDownListFor to get a selected value
The above class, controller and view are correct for presenting the drop-downs. This is why I was perplexed.
However, I had the SelectMember in the Get section. [HttpPost] and SelectMember can see what was selected.

EF5 code first with ASP.NET Web API: Update entity with many-to-many relationship

I'm trying to update a Customer in my database using ASP.NET Web API and Entity Framework 5 code-first, but it's not working. My entities look like this:
public class CustomerModel
{
public int Id { get; set; }
public string Name { get; set; }
// More fields
public ICollection<CustomerTypeModel> CustomerTypes { get; set; }
}
public class CustomerTypeModel
{
public int Id { get; set; }
public string Type { get; set; }
[JsonIgnore]
public ICollection<CustomerModel> Customers { get; set; }
}
Nothing all that special. I've built a web interface where users can add a customer by supplying the name and checking one or more customer types. When hitting the submit button, the data is sent to my Web API method:
public void Put([FromBody]CustomerModel customer)
{
using (var context = new MyContext())
{
context.Customers.Attach(customer);
context.Entry(customer).State = EntityState.Modified;
context.SaveChanges();
}
}
This updates the customer fields, but the related customer types are ignored. The incoming customer object does contain a list of CustomerTypes it should be associated with:
[0] => { Id: 1, Type: "Finance", Customers: Null },
[1] => { Id: 2, Type: "Insurance", Customers: Null }
[2] => { Id: 3, Type: "Electronics", Customers: Null }
But instead of looking at this list and adding/removing associated entities, EF just ignores it. New associations are ignored and existing associations remain even if they should be deleted.
I had a similar problem when inserting a customer into the database, this was fixed when I adjusted the state of these entities to EntityState.Unchanged. Naturally, I tried to apply this same magic fix in my update scenario:
public void Put([FromBody]CustomerModel customer)
{
using (var context = new MyContext())
{
foreach (var customertype in customer.CustomerTypes)
{
context.Entry(customertype).State = EntityState.Unchanged;
}
context.Customers.Attach(customer);
context.Entry(customer).State = EntityState.Modified;
context.SaveChanges();
}
}
But EF keeps displaying the same behavior.
Any ideas on how to fix this? Or should I really just do a manual clear to the list of CustomerTypes and then manually add them?
Thanks in advance.
JP
This is not really solvable by only setting entity states. You must load the customer from the database first including all its current types and then remove types from or add types to the loaded customer according to the updated types collection of the posted customer. Change tracking will do the rest to delete entries from the join table or insert new entries:
public void Put([FromBody]CustomerModel customer)
{
using (var context = new MyContext())
{
var customerInDb = context.Customers.Include(c => c.CustomerTypes)
.Single(c => c.Id == customer.Id);
// Updates the Name property
context.Entry(customerInDb).CurrentValues.SetValues(customer);
// Remove types
foreach (var typeInDb in customerInDb.CustomerTypes.ToList())
if (!customer.CustomerTypes.Any(t => t.Id == typeInDb.Id))
customerInDb.CustomerTypes.Remove(typeInDb);
// Add new types
foreach (var type in customer.CustomerTypes)
if (!customerInDb.CustomerTypes.Any(t => t.Id == type.Id))
{
context.CustomerTypes.Attach(type);
customerInDb.CustomerTypes.Add(type);
}
context.SaveChanges();
}
}
A cleaner solution would be:
public void Put([FromBody]CustomerModel customer)
{
using (var context = new MyContext())
{
var customerInDb = context.Customers.Include(c => c.CustomerTypes)
.Single(c => c.Id == customer.Id);
// Updates the Name property
context.Entry(customerInDb).CurrentValues.SetValues(customer);
// Remove types
customer.CustomerTypes.Clear();
// Add new types
foreach (var type in customer.CustomerTypes)
{
context.CustomerTypes.Attach(type);
customerInDb.CustomerTypes.Add(type);
}
context.SaveChanges();
}
}

ASP.NET MVC Populating DropDownListFor based on DB call

I am building a simple ASP.NET MVC 3 site, using Entity Framework to build the model based on some tables in an already existing Oracle database (in other words, I used the 'database first' methodology to have my model built). I now have a simple blog type site, which I am quite familiar with as I have been learning MVC in a number of languages.
I want to change some of the auto-generated views. One piece I would like to change in particular is that I have a field in one of my tables called 'Visible'. This is simply a numeric value (0 or 1) indicating whether or not a display application should use the row as display data. Currently, I have the simple text field that is auto-generated:
<div class="editor-field">
#Html.EditorFor(model => model.VISIBLE)
#Html.ValidationMessageFor(model => model.VISIBLE)
</div>
What I would like to do is replace this with a drop down box with string values like True and False. The application should display any entry with a 0 as false and vice versa. If a user wants to flip the toggle, the drop down should allow them to do that and understand to make the numeric update when clicking submit. How can this be done?
I have seen countless examples where the drop-down was going to be filled with more then just two values, and in those cases I understand that you can add logic to your controller that pulls all the distinct values, puts them in a list, then adds the list to the ViewBag. However, in my case with only two possible numeric values, it seems like there should be a simpler, more accepted way to do it.
Any help is greatly appreciated.
UPDATE
Following Quinton's answer, I am trying to place said code in my model. Here is my current model:
namespace CurrentActivityBlog
{
using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;
public partial class TBLCURRENTACTIVITY
{
public string TITLE { get; set; }
public string DESCRIPTION { get; set; }
public System.DateTime DATETIME { get; set; }
public short VISIBLE { get; set; }
public decimal ID { get; set; }
public IEnumerable<SelectedListItem> PossibleValues { get; set; }
public TBLCURRENTACTIVITY() {
PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" },
new SelectListItem { Value = "1", Text = "Visible" } };
}
}
}
I am unable to build this solution, but Visual Studio 2010 is telling me that
"the type or namespace name 'SelectedListItem' could not be found (are you missing a using directive or an assembly reference?)"
As you can see, I have
using System.Web.UI.controls
and have added the reference to System.Web. Is there anything I am forgetting, or anything I should know about (such as models generated using EF behaving differently then one might expect, etc.)?
Thanks again.
The one line solution to it would be:
#Html.DropDownList(Model.VISIBLE.ToString(), new [] {new SelectListItem { Value = "0", Text = "Hidden"}, new SelectListItem { Value = "1", Text = "Visible"}})
but you probably don't want domain logic in your view. So add the possible items to your Model (or from the controller):
public class MyModel {
public int VISIBLE { get; set; }
public IEnumerable<SelectListItem> PossibleValues { get; set; }
public MyModel() {
PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" }, new SelectListItem { Value = "1", Text = "Visible" } };
}
}
and then your razor code:
#Html.DropDownList(Model.VISIBLE.ToString(), Model.PossibleValues)
Obviously "Hidden" and "Visible" descriptions can be replaced with "False" and "True" or whatever.
You could also create and Editor and Display Template for that specific field. Checkout ScottGu's blog post here, search for "UI Helper Templating Support" and you'll see how to create a editor template and how to render a specific template by name.
EDIT:
If your model is not part of your MVC project, then referencing any classes that are in the MVC assemblies would require a explicit add reference. You can avoid this though by initializing any MVC assembly types in your model from your controller, like such:
public class MyModel {
public int VISIBLE { get; set; }
public IEnumerable<SelectListItem> PossibleValues { get; set; }
}
controller action method:
public ActionResult Edit(int Id) {
...
myModelInstance. PossibleValues = new[] { new SelectListItem { Value = "0", Text = "Hidden" }, new SelectListItem { Value = "1", Text = "Visible" } };
...
Return View(myModel);
}

Resources