odata v4, List<> Property without expand - asp.net-web-api

Is it possible in odata4 to create a model such as:
public class PuppyDogs
{
public string Name { get; set; }
public virtual IList<Bone> Bones { get; set; }
}
public class Bone
{
public string ChewType { get; set; }
public int Numberofchews { get; set; }
}
And the controller class looks like
public class PuppyDogController : ODataController
{
List<PuppysDog> mydogs = new List<PuppysDog>();
private PuppyDogController()
{
if (mydogs.Count == 0)
{
PuppysDog mydog = new PuppysDog();
mydog.Name = "Fido";
mydog.Bones = new List<Bone>()
{
new Bone{ ChewType = "Soft", Numberofchews=1 },
new Bone{ ChewType = "Hard", Numberofchews=2 }
};
mydogs.Add(mydog);
}
}
[EnableQuery]
public IQueryable<PuppysDog> Get()
{
return mydogs.AsQueryable();
}
}
Can I include the Bones property of PuppyDogs without using expand? By default Bones is not returned to the client.

There are several things nor clear in your code, for example, the entity set PuppyDogs don't have a key, the naming convention in the controller is a little wired and etc. With the following code, it can work perfectly, please take a look
PuppyDog.cs
public class PuppyDog
{
[Key]
public string Name { get; set; }
public virtual IList<Bone> Bones { get; set; }
}
Bone.cs
public class Bone
{
public string ChewType { get; set; }
public int Numberofchews { get; set; }
}
PupyyDogsController.cs
public class PuppyDogsController : ODataController
{
List<PuppyDog> mydogs = new List<PuppyDog>();
private PuppyDogsController()
{
if (mydogs.Count == 0)
{
PuppyDog mydog = new PuppyDog();
mydog.Name = "Fido";
mydog.Bones = new List<Bone>()
{
new Bone {ChewType = "Soft", Numberofchews = 1},
new Bone {ChewType = "Hard", Numberofchews = 2}
};
mydogs.Add(mydog);
}
}
[EnableQuery]
public IQueryable<PuppyDog> Get()
{
return mydogs.AsQueryable();
}
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<PuppyDog>("PuppyDogs");
config.MapODataServiceRoute("odata", null, builder.GetEdmModel(), new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
config.EnsureInitialized();
}
}
Then when try http://localhost:21830/PuppyDogs, I can successfully got the payload as
{
"#odata.context": "http://localhost:21830/$metadata#PuppyDogs",
"value": [
{
"Name": "Fido",
"Bones": [
{
"ChewType": "Soft",
"Numberofchews": 1
},
{
"ChewType": "Hard",
"Numberofchews": 2
}
]
}
]
}

Related

How to Change the Response of the API in OnActionExecuted method of Controller in .Net Core

Json response:
{
"ID": 1,
"Value": 10,
"User": null
}
I need to change the User value in each response in OnActionExecuted method.
public override void OnActionExecuted(ActionExecutedContext context)
{
var response = context.Result;
}
But unable to read the Result and Update details.
You just need to do some casting, I think. This code needs some error handling but otherwise demonstrates what you want:
public class ViewModel
{
public int ID { get; set; }
public int Value { get; set; }
public string User { get; set; }
}
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IActionResult Get()
{
var vm = new ViewModel()
{
ID = 1,
Value = 10
};
return Ok(vm);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
var result = context.Result as OkObjectResult;
var vm = result.Value as ViewModel;
vm.User = "ardalis";
}
}
The result:
{"id":1,"value":10,"user":"ardalis"}

Bar Chart of ChartJS does not Render

I am having trouble displaying bar chart from my ASP.NET-MVC-5 application. I witnessed the JSON comes out correctly (sample is applied below), and as per documentation I had included all, yet the output comes as below:
HTML:
<div class="jumbotron">
<h1>Another Chart</h1>
<canvas id="barChartLoc" width="600" height="400"></canvas>
</div>
This is the Script which calls the Controller, which returns a JSON:
<script type="text/javascript">
function chartFYRevenue() {
$.ajax({
url: '#Url.Action("GetLast5FYRevenueAnalysis", "Utility")',
cache: true
})
.done(function (data) {
var mybarChartLoc = new Chart(document.getElementById("barChartLoc").getContext("2d")).Bar(data);
})
.fail(function () {
alert("Ajax failed to fetch data");
});
}
$(document).ready(function () {
//auto load on page load
chartFYRevenue();
});
</script>
This is the Controller which returns a JSON. I have tested this and things are fine here as well:
public JsonResult GetLast5FYRevenueAnalysis()
{
Models.Chart.BarChartDBContext chartDB = new Models.Chart.BarChartDBContext();
return Json(chartDB.Test, JsonRequestBehavior.AllowGet);
}
This is the Modeler class where I build the Chart Data dynamically:
public class ChartDataSets
{
public string label { get; set; }
public string fillColor { get; set; }
public string highlightFill { get; set; }
public string highlightStroke { get; set; }
public string strokeColor { get; set; }
public string data { get; set; }
}
public class BarChartModel
{
public string labels { get; set; }
public List<ChartDataSets> datasets { get; set; }
}
public class BarChartDBContext : Models.DBHelper.DBHelperClass
{
public BarChartModel GetLast5FInancialYearRevenue
{
get { return getLast5FinancialYearRevenue(); }
}
public BarChartModel Test
{
get { return test(); }
}
private BarChartModel test()
{
List<ChartDataSets> _datasets = new List<ChartDataSets>();
BarChartModel _barChartModel = null;
_datasets.Add(new ChartDataSets()
{
data = string.Format("[{0}]", "10,5,25,35"),
fillColor = "rgba(220,220,220,0.5)",
highlightFill = "rgba(220,220,220,0.75)",
highlightStroke = "rgba(220,220,220,1)",
strokeColor = "rgba(220,220,220,0.8)",
label = "s1"
});
_barChartModel = new BarChartModel();
_barChartModel.labels = string.Format("[{0}]", "c1,c2,c3,c4");
_barChartModel.datasets = _datasets;
return _barChartModel;
}
}
JSON Data Sample:
{
"labels": "[c1,c2,c3,c4]",
"datasets": [
{
"label": "s1",
"fillColor": "rgba(220,220,220,0.5)",
"highlightFill": "rgba(220,220,220,0.75)",
"highlightStroke": "rgba(220,220,220,1)",
"strokeColor": "rgba(220,220,220,0.8)",
"data": "[10,5,25,35]"
}
]
}
Update:
I modified my ChartDataSet and BarChartModel Class to the following:
public class ChartDataSets
{
public string label { get; set; }
public string fillColor { get; set; }
public string highlightFill { get; set; }
public string highlightStroke { get; set; }
public string strokeColor { get; set; }
public string[] data { get; set; }
}
public class BarChartModel
{
public string[] labels { get; set; }
public List<ChartDataSets> datasets { get; set; }
}
Your JSON data generated is incorrect. The right output should be
"{
"labels": ["c1","c2","c3","c4"],
"datasets": [
{
"label":"s1",
"fillColor":"rgba(220,220,220,0.5)",
"highlightFill":"rgba(220,220,220,0.75)",
"highlightStroke":"rgba(220,220,220,1)",
"strokeColor":"rgba(220,220,220,0.8)",
"data":[10,5,25,35]
}
]
}"

insert data in two class(table) in Entity Framwork by rerlation many to many

i create two class in my model and create relation many to many by Entity
in sql my classes is created tables Properly
when i try to insert data in this table get show error "Object reference not set to an instance of an object." my cod is:
public class News
{
public int ID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime Date { get; set; }
public virtual Picture Picture { get; set; }
public virtual NewsType NewsType { get; set; }
public ICollection<Tag> Tag { get; set; }
public News(int id, string title, string content, DateTime date)
{
this.ID = id;
this.Title = title;
this.Content = content;
this.Date = date;
}
public News()
{
}
}
public class Tag
{
public int ID { get; set; }
public string Title { get; set; }
public ICollection<News> News { get; set; }
public Tag()
{
}
}
public class DatabaseContext : DbContext
{
public DatabaseContext()
: base("News")
{
}
static DatabaseContext()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DatabaseContext>());
}
public DbSet<News> newsInfo { get; set; }
public DbSet<Picture> pictures { get; set; }
public DbSet<NewsType> Types { get; set; }
public DbSet<Tag> Tags { get; set; }
}
[HttpPost]
public ActionResult AddNews(NewsViewModel newsInfo)
{
using (Models.DatabaseContext dbContext = new DatabaseContext())
{
ViewData["Type"] = new SelectList(dbContext.Types.ToList(), "Id", "Title");
}
if (!ModelState.IsValid)
{
return View();
}
else
{
Models.DatabaseContext dbContext = new Models.DatabaseContext();
Models.News news = new Models.News();
news.Title = newsInfo.Title;
news.Content = newsInfo.Content;
news.Date = DateTime.Now;
string newsinput = newsInfo.Tag.cleanTag();
string[] tags = new string[] { };
if (newsinput != null)
{
tags = newsinput.Split(',');
}
foreach (string item in tags)
{
Tag findTag = dbContext.Tags.Where(x => x.Title == item).FirstOrDefault();
if (findTag != null)
{
news.Tag.Add(findTag)
////////////////////////show error in this line
}
}
news.NewsType = dbContext.Types.Find(Convert.ToInt32(Request.Form["rdb"]));
dbContext.newsInfo.Add(news);
dbContext.SaveChanges();
return View();
}

knockoutjs with mvc collection model binding

I'm using knockoutjs to render a collection of items. After allowing the user to do some inline editing I need to post the collection back to the server. However, the collection isn't being populated on the server because I'm not using the name="[0].Blah" naming convention. Does anyone know how to either render name attributes like this using knockoutjs OR how to create a model binder that will allow me to extract the values from the ValueProvider?
You can see a screenshot of the ValueProvider during debugging below.
http://i.imgur.com/zSU5Z.png
Here is my managed ViewModel:
public class FundLevelInvestmentUploadResult
{
public string FileName { get; set; }
public IList<FundLevelInvestmentViewModel> Items { get; set; }
public int NumberOfErrors { get; set; }
public bool ShowErrorsOnly { get; set; }
public FundLevelInvestmentUploadResult()
{
Items = new List<FundLevelInvestmentViewModel>();
}
}
Here is the managed class for "Items":
public class FundLevelInvestmentViewModel
{
private string _fund;
private string _fundType;
private string _date;
private string _netOfWaivedFees;
private string _waivedFees;
private string _bcip;
private string _fxRate;
public uint RowIndex { get; set; }
public int? DealCode { get; set; }
public bool DealCodeIsValid { get; set; }
public string Fund
{
get { return _fund; }
set { _fund = GetString(value); }
}
public bool FundIsValid { get; set; }
public string FundType
{
get { return _fundType; }
set { _fundType = GetString(value); }
}
public bool FundTypeIsValid { get; set; }
public string DateOfInvestment
{
get { return _date; }
set { _date = GetString(value); }
}
public bool DateOfInvestmentIsValid { get; set; }
public string NetOfWaivedFees
{
get { return _netOfWaivedFees; }
set { _netOfWaivedFees = GetString(value); }
}
public bool NetOfWaivedFeesIsValid { get; set; }
public string WaivedFee
{
get { return _waivedFees; }
set { _waivedFees = GetString(value); }
}
public bool WaivedFeeIsValid { get; set; }
public string BCIP
{
get { return _bcip; }
set { _bcip = GetString(value); }
}
public bool BCIPIsValid { get; set; }
public string ExchangeRateToUSD
{
get { return _fxRate; }
set { _fxRate = GetString(value); }
}
public bool ExchangeRateToUSDIsValid { get; set; }
public string FileName { get; set; }
private IList<string> _errors;
public IList<string> Errors
{
get { return _errors ?? (_errors = new List<string>());}
set { _errors = value; }
}
public bool Show { get; set; }
public FundLevelInvestmentViewModel()
{
Errors = new List<string>();
Show = true;
}
// knockoutjs is returning "null" instead of "" for a null object when calling ko.mapping.fromJS
private string GetString(string value)
{
if (value == "null")
return string.Empty;
return value;
}
}
Here is my knockout viewModel:
var viewModel = {
FileData: ko.observableArray([]),
validateFile: function (file, event) {
$.ajax({
type: 'post',
url: newUrl,
data: ko.mapping.toJS(file)
}).done(function (data) {
var newFile = ko.mapping.fromJS(data);
var index = file.Items.indexOf(file);
viewModel.FileData.replace(file, newFile);
});
}
};
If you are using version 2.1.0.0 or later of knockout you can render the name attribute as follows from an observable array.
<input data-bind='attr: { name: "Items["+$index()+"].DealCode"}' />

How to model bind a class that implements an interface?

The model binding worked fine until i implemented interfaces on top of the following classes:
public class QuestionAnswer : IQuestionAnswer
{
public Int32 Row_ID { get; set; }
public Int32 Column_ID { get; set; }
public String Value { get; set; }
}
public class HiddenAnswer : IHiddenAnswer
{
public Int32 Hidden_Field_ID { get; set; }
public String Hidden_Field_Value { get; set; }
}
public class SurveyAnswer : ISurveyAnswer
{
public string SessionID { get; set; }
public List<IQuestionAnswer> QuestionAnswerList { get; set; }
public List<IHiddenAnswer> HiddenAnswerList { get; set; }
public SurveyAnswer()
{
QuestionAnswerList = new List<IQuestionAnswer>();
HiddenAnswerList = new List<IHiddenAnswer>();
}
}
Now that the interfaces are there, i get a 500 (Internal Server Error)
The javascript that i use to model bind is the following:
$('#submitbutton').click(function () {
var answers = new Array();
var hiddenfields = new Array();
var formname = "#" + $("#formname").val();
$(':input', formname).each(function () {
if ($(this).is(":text") || $(this).is(":radio") || $(this).is(":checkbox"))
{
var answerObject = {
Column_ID: $(this).attr('data-column_id'),
Row_ID: $(this).attr('data-row_id'),
Value: $(this).attr('data-theValue')
};
answers.push(answerObject);
}
else if($(this).is(":hidden")) {
var hiddenObject =
{
Hidden_Field_ID: $(this).attr('data-hidden_field_id'),
Hidden_Field_Value: $(this).attr('data-hidden_field_value')
}
hiddenfields.push(hiddenObject);
}
});
$('textarea', formname).each(function () {
var answerObject = {
Column_ID: $(this).attr('data-column_id'),
Row_ID: $(this).attr('data-row_id'),
Value: $(this).val(),
};
answers.push(answerObject);
});
var allAnswers = {
SessionID: 0,
QuestionAnswerList: answers,
HiddenAnswerList: hiddenfields
}
postForm(allAnswers);
});
The Controller Action looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SubmitSurvey(SurveyAnswer answers)
{
// Dette tillader CORS
Response.AppendHeader("Access-Control-Allow-Origin", "*");
bc.SaveSurvey(answers);
return null;
}
what am i doing wrong?
what am i doing wrong?
You cannot expect the model binder to know that when it encounters the IQuestionAnswer interface on your SurveyAnswer view model it should use the QuestionAnswer type. It's nice that you have declared this implementation of the interface but the model binder has no clue about it.
So you will have to write a custom model binder for the IQuestionAnswer interface (same for the IHiddenAnswer interface) and indicate which implementation do you want to be used:
public class QuestionAnswerModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var type = typeof(QuestionAnswer);
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
which will be registered in your Application_Start:
ModelBinders.Binders.Add(typeof(IQuestionAnswer), new QuestionAnswerModelBinder());

Resources