navigation properties not saving - breeze

I have a simple object model
public class License
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ScaffoldColumn(false), StringLength(20)]
public string CreationUserId { get; set; }
[ScaffoldColumn(false), StringLength(20)]
public string LastModifiedUserId { get; set; }
public string LicenseName { get; set; }
public LicenseType LicenseType { get; set; }
public State State { get; set; }
public DateTime DateIssued { get; set; }
public int ValidFor { get; set; }
}
public class State
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ScaffoldColumn(false), StringLength(20)]
public string CreationUserId { get; set; }
[ScaffoldColumn(false), StringLength(20)]
public string LastModifiedUserId { get; set; }
[StringLength(2)]
[Required]
public string Name { get; set; }
[Display(Name = "Long Name")]
[Required, StringLength(25)]
public string LongName { get; set; }
}
public class LicenseType
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ScaffoldColumn(false), StringLength(20)]
public string CreationUserId { get; set; }
[ScaffoldColumn(false), StringLength(20)]
public string LastModifiedUserId { get; set; }
[StringLength(100), Required]
public string Description { get; set; }
}
I am using the hot towel template breeze, durandal, knockout.
I have a simple add view model
var _licenseAdded = false;
var vm = {
states: ko.observableArray(context.states),
licenseTypes: ko.observableArray(context.licenseTypes),
viewAttached: function () {
var self = this;
$('input[name^="date"]').datepicker();
$('#validFor').spinner({
min: 365,
max: 3650,
step: 30
});
log('add Attached', null, true);
},
activate: function () {
var self = this;
self.original = context.manager.createEntity('License', { licenseName: 'Testing321', dateIssued: moment().format('L') }, null);
log('add Activated', null, true);
},
canDeactivate: function () {
if (_licenseAdded === false) {
return app.showMessage('Are you sure you want to leave this page?', 'Navigate', ['Yes', 'No']);
} else {
return true;
}
},
saveChanges: function () {
$.when(context.saveNewLicense()).done(function () {
_licenseAdded = true;
});
router.navigateTo('home');
},
original: undefined
};
return vm;
And here is my add.html, everything binds up fine and works beautifully until saving.
When I call saveChanges the saveBundle sent to the controller has no navigation properties attached that allow for the correct State and LicenseType to be stored I only get:
saveBundle {
"entities": [
{
"Id": -1,
"CreationUserId": null,
"LastModifiedUserId": null,
"LicenseName": "new for testing",
"DateIssued": "2013-03-11T04:00:00Z",
"ValidFor": 0,
"entityAspect": {
"entityTypeName": "License:#Volt.Telecom.Licensing.Models",
"entityState": "Added",
"originalValuesMap": {},
"autoGeneratedKey": {
"propertyName": "Id",
"autoGeneratedKeyType": "Identity"
}
}
}
],
"saveOptions": {
"allowConcurrentSaves": false
}
}
Don't think I can get much more vanilla than this. Why might this occur? When I am debugging on the client the state and licenseType navigationProperties are all correct and with the correct values.

I think the issue is that your EF model uses 'Independent Associations' instead of 'Foreign Key Assocations' (foreign key associations are the EF default). We do need to do a better job of documenting this assumption.
The reason for this requirement, which can be bypassed but with a substantial loss of functionality, is that the existence of foreign keys on the client is what allows breeze to automatically fix up relationships between entities that might be queried separately.
See the following MSDN article for more background: foreign-keys-in-the-entity-framework

Related

Execute Sql query in entity framework

I have a jquery data table in which i want to show a summarise report generated from sql query, but i am not able to do it, it shows error in the query. The summarise report consist of all month name and number of entries in every month.
Error:
System.Data.Entity.Core.EntityCommandExecutionException: 'The data reader is incompatible with the specified 'DHIFeedbackModel.FeedBack'. A member of the type, 'FeedbackUserName', does not have a corresponding column in the data reader with the same name.'
public ActionResult LoadData()
{
using (DHIFeedbackEntities2 Ms = new DHIFeedbackEntities2())
{
//var summary = Ms.FeedBacks.SqlQuery("select * from [DHIFeedback].[dbo].[FeedBack]").ToList<FeedBack>();
var summary = Ms.FeedBacks.SqlQuery(
#"SELECT *
FROM
(
SELECT
YEAR([FeedBackDate])[Year],DATENAME(MONTH, [FeedBackDate])[Month],
COUNT(1)[FeedbackID]
FROM
[DHIFeedback].[dbo].[FeedBack]
GROUP BY
YEAR([FeedBackDate]
),
DATENAME(MONTH, [FeedBackDate])) AS Monthlyupdate
PIVOT(SUM([FeedbackID]) FOR Month IN([January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December])) AS MNamePivot
order by 1,2"
).FirstOrDefault<FeedBack>();
return Json(new { data = summary }, JsonRequestBehavior.AllowGet);
}
}
and the javascript:
$(document).ready(function () {
$('#Summary').DataTable({
"processing": true,
"ajax": {
"url": "/Summary/LoadData",
"type": "GET",
"datatype": "json",
},
"lengthMenu": [
[5, 10, 25, 50, 100, -1],
[5, 10, 25, 50, 100, "All"]
],
"autoWidth": true,
"responsive": true,
"lengthChange": true,
"ordering": true,
"fnRowCallback": function (nRow, aData, iDisplayIndex) {
var oSettings = this.fnSettings();
$("td:first", nRow).html(oSettings._iDisplayStart + iDisplayIndex + 1);
return nRow;
},
"columns": [
{ "data":"Year", "autoWidth": true },
{ "data":"January", "autoWidth": true },
{ "data":"February", "autoWidth": true },
{ "data":"March", "autoWidth": true },
{ "data":"April", "autoWidth": true },
{ "data":"May", "autoWidth": true },
{ "data":"June", "autoWidth": true },
{ "data":"July", "autoWidth": true },
{ "data":"August", "autoWidth": true },
{ "data":"September", "autoWidth": true },
{ "data":"October", "autoWidth": true },
{ "data":"November", "autoWidth": true },
{ "data":"December", "autoWidth": true }
]
});
});
public partial class FeedBack
{
public int FeedbackID { get; set; }
public string FeedbackUserName { get; set; }
public string FeedBackUserEmailID { get; set; }
public string FeedBackComment { get; set; }
public string Designation { get; set; }
public string Organization { get; set; }
public string ContactNo { get; set; }
public string City { get; set; }
public Nullable<System.DateTime> FeedBackDate { get; set; }
public Nullable<double> IsPublished { get; set; }
public string Reply { get; set; }
public Nullable<double> IsReplied { get; set; }
public Nullable<System.DateTime> ReplyDate { get; set; }
public string ReplyBy { get; set; }
public string Sector { get; set; }
public Nullable<int> Status { get; set; }
public int Year { get; set; }
public int January { get; set; }
public int February { get; set; }
public int March { get; set; }
public int April { get; set; }
public int May { get; set; }
public int June { get; set; }
public int July { get; set; }
public int August { get; set; }
public int September { get; set; }
public int October { get; set; }
public int November { get; set; }
public int December { get; set; }
public string Monthlyupdate { get; set; }
public string Month{ get; set; }
[NotMapped]
public List<FeedBack> FeedBackCollection { get; set; }
the error means that the ORM is expecting a resultset comprising each and every property of FeedBacks including the one named FeedbackUserName
So you need to edit the select clause of your sql to return all the expected columns.
or you may use
Ms.Database.SqlQuery<SomeType>(
"your query here").ToList();
where SomeType is :
public class SomeType {
public int Year {get; set;}
//... and/or all the pivoted columns
}

Don't know how to set Web API, problem to retrieve the data

I had this problem few days ago, And thought that I'd found the solution which was
setting lazy loading : false.
but my problem to retrieve the data persisted.
Using fiddler, or a front-end app I can't retrieve the data and as result I have only has values like : $ref=6
I think it is some kind of general setting problem, so I'm going to give some information here.
controller:
[AllowAnonymous]
[HttpGet]
[Route("GetQuestionsByTestId/{id}")]
public ICollection<Question> GetQuestionsByTestId(int id)
{
return db.Questions.Where(t => t.TestId == id)
.Include(a => a.Answers)
.Include(q=>q.Test)
.Include(q=>q.Test.TestType)
.ToList();
}
identityModels:
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
this.Configuration.LazyLoadingEnabled = false; //false for Lazy Loading Off
this.Configuration.ProxyCreationEnabled = false;
}
WebApiConfig:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
question Model:
[Table("Question")]
public class Question
{
public Question()
{
Answers = new HashSet<Answer>();
}
[Key]
public int QuestionId { get; set; }
[Required]
public string Name { get; set; }
public string Comment { get; set; }
public int Difficulty { get; set; }
public byte Repeat { get; set; } // 0 - 255
public bool IsLearned { get; set; }
public string QuestionNumber { get; set; }
public virtual ICollection<Answer> Answers { get; set; }
[ForeignKey("Chapter")]
public int ChapterId { get; set; }
public Chapter Chapter { get; set; }
[ForeignKey("Test")]
public int TestId { get; set; }
public Test Test { get; set; }
}
my return retrieved with chrome NETWORK: this is where my problem is:
[{$id: "1", QuestionId: 5, Name: "11", Comment: null, Difficulty: 0, Repeat: 0,
IsLearned: false,…},…]
0: {$id: "1", QuestionId: 5, Name: "11", Comment: null, Difficulty: 0, Repeat: 0,
IsLearned: false,…}
1: {$ref: "6"}
second object is not visible, there is only this: $ref:"6"
Please help, losing hope here.
I'm guessing here that you're using Entity Framework to store and retrieve data. Realistically you don't want to be returning data/entities straight from your database, you probably want to map your data into a set of classes called Data Transfer Objects (DTO).
You can do this manually or using a tool such as AutoMapper.
Manually you would do something like this
Create a DTO class:
public class QuestionDTO
{
public int QuestionId { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public int Difficulty { get; set; }
public byte Repeat { get; set; } // 0 - 255
public bool IsLearned { get; set; }
public string QuestionNumber { get; set; }
}
Change controller method:
[AllowAnonymous]
[HttpGet]
[Route("GetQuestionsByTestId/{id}")]
public IHttpActionResult GetQuestionsByTestId(int id)
{
var questions = db.Questions.Where(t => t.TestId == id)
.Include(a => a.Answers)
.Include(q => q.Test)
.Include(q => q.Test.TestType)
.ToList();
var questionDTOs = new List<QuestionDTO>();
foreach (var question in questions)
{
questionDTOs.Add(new QuestionDTO
{
QuestionId = question.QuestionId,
Name = question.Name,
Comment = question.Comment,
Difficulty = question.Difficulty,
Repeat = question.Repeat,
IsLearned = question.IsLearned,
QuestionNumber = question.QuestionNumber
});
}
return Ok(questionDTOs);
}
(I have changed the return type so that you can use the Ok method that will return a 200 message or if needed return other status codes such as 400 using BadRequest() etc)
Using DTOs allows you to control exactly what data is returned and you don't have to worry about changing things like Lazy loading or proxy creation

asp.net 5 mvc 6 model issue

I am trying to list some data from a news section. I have two tables. News and NewsCategory
This is my model classes
public class News
{
public int NewsId { get; set; }
public string Name { get; set; }
public int NewsCategoryId { get; set; }
public virtual NewsCategory NewsCategory { get; set; }
}
public class NewsCategory
{
public int NewsCategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<News> News { get; set; }
}
public class NewsDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptions options)
{
options.UseSqlServer(Startup.Configuration.Get("Data:DefaultConnection:ConnectionString"));
}
public DbSet<News> News { get; set; }
public DbSet<NewsCategory> NewsCategory { get; set; }
}
This is also working, when I in my controller fect the data, with the exception of one thing. When I fect my news, I do not have a reference to my Category.
My controller code:
var news = _db.News.ToList();
This outputs :
[
{
"NewsId": 1,
"Name": "ghdfgd",
"NewsCategoryId": 1,
"NewsCategory": null
},
{
"NewsId": 2,
"Name": "gdfgdf",
"NewsCategoryId": 1,
"NewsCategory": null
}
]
As you can see, NewsCategory is empty. Although it is not:)
What am I missing?
It's because you are lazy loading the navigation properties.
Look into this article.
Just do this:
var news = _db.News.Include(n => n.NewsCategory).ToList();

Need to deserialize a nested json array to server side array

I have an array nested in an object in a JSON string which I need deserialized at the server:
var orderStatus = {"auth": "xxxx", "resourceType": "order.status", "idSet": "2980", "lifecycleEvent": "modified", "objects": { "orders": [ { "id": "2980", "statusId": "6" } ] }
I use Robert Koritnik's plugin like this:
$.ajax({url: "receiveJson", type: "POST", data: $.toDictionary(orderStatus) });
My .net class file is:
public class orders
{
public string Id { get; set; }
public string statusId { get; set; }
}
public class objects
{
public orders orders { get; set; }
}
public class OrderStatus
{
public string clientName { get; set; }
public string source { get; set; }
public string auth { get; set; }
public string resourceType { get; set; }
public string idSet { get; set; }
public string lifecycleEvent { get; set; }
public objects objects { get; set; }
}
my controller code is:
public JsonResult receiveJson(OrderStatus orderStatus)
So the orders object is the array. It works up to creating orders as an object but id and status id in the orders object are null.
I have no control over the JSON I will receive, it has to be in this format.
I am new to JSON and .NET MVC. Don't know how to specify server side orders object as an array.
Fixed it by slightly amending my server side classes:
public class order
{
public string Id { get; set; }
public string statusId { get; set; }
}
public class objects
{
public List<order> orders { get; set; }
}
public class OrderStatus
{
public string clientName { get; set; }
public string source { get; set; }
public string auth { get; set; }
public string resourceType { get; set; }
public string idSet { get; set; }
public string lifecycleEvent { get; set; }
public objects objects { get; set; }
}
So the "orders" class has been changed to "order". "objects.orders" property is amended to be a list.
Now the jsondata is deserialized all the way down.

Serialize Form GetJSON returns null model

I have a form I am trying to send in a GetJSON call. When I get to the Controller the model that si tied to the view is a null vlaue. I have had issues before dealign with returning data when I woudl get an empy object but never a null value. Below is the code I am using to send the form
var cqvdata = $("form").serialize();
$.getJSON('#Url.Action("GetEmailByAdvanced", "CustomerEmails")', { cqv: cqvdata }, function (contacts) {
var emails = "";
$.each(contacts, function (index, contact) {
$('#BCCText').tagit('createTag', contact.Email)
});
return false;
});
Below is what I have on the controller side
public JsonResult GetEmailByAdvanced(MassEmailViewModel cqv)
{
}
Here is what I get for results if I turn my argument into a string
"EmailFromAddressID=1&ToAddresses=&CCAddresses=bclairmont%40harr.com&BCCAddresses=adunn%40harr.com&Subject=&Body="
Below is the MassEmailViewModelClass and all sub classes
public class MassEmailViewModel
{
public MassEmailViewModel()
{
ComplexQuery = new CustomerQueryViewModel();
}
public int EmailFromAddressID { get; set; }
public CustomerQueryViewModel ComplexQuery { get; set; }
public string ToAddresses { get; set; }
public string CCAddresses { get; set; }
public string BCCAddresses { get; set; }
public string Subject { get; set; }
[AllowHtml]
public string Body { get; set; }
}
public class CustomerQueryViewModel
{
public CustomerQueryViewModel()
{
Products = new List<CustomerProductQueryProduct>();
Details = new List<CustomerQueryDetail>();
}
public Boolean IncludeOnAll { get; set; }
public Boolean ExcludeOnAll { get; set; }
public List<CustomerProductQueryProduct> Products { get; set; }
public List<CustomerQueryDetail> Details { get; set; }
}
public class CustomerProductQueryProduct
{
public CustomerProductQueryProduct()
{
ProductDetails = new List<CustomerProductQueryProductDetail>();
ProductVersions = new List<ProductVersion>();
}
public ProductType ProductType { get; set; }
public Boolean Exclude { get; set; }
public Boolean Include { get; set; }
public int VersiondID { get; set; }
public List<CustomerProductQueryProductDetail> ProductDetails { get; set; }
public List<ProductVersion> ProductVersions { get; set; }
}
public class CustomerProductQueryProductDetail
{
public ProductTypeDetail ProductDetail { get; set; }
public Boolean Exclude { get; set; }
public Boolean Include { get; set; }
public string Value { get; set; }
public string Value2 { get; set; }
}
public class CustomerQueryDetail
{
public string Description { get; set; }
public string Type { get; set; }
public Boolean Exclude { get; set; }
public Boolean Include { get; set; }
public string Value { get; set; }
public string Value2 { get; set; }
}
The only thing not being returned is my ComplexQuery in the serialize because I am using a JQuery dialog so it takes those elements out of the form. I woudl think I woudl get a MassEmaikViewModel with all the vlaues but ComplexQuery and have a null for that but I just get a null as iff the argument never even got initialized.
Any ideas on what could be causing this?
One other thing and I don't know if this will help give anyone any insight or not but I can post from the form and have the MassEmailViewModel as the argument in the post and it works fine filling out all the values except for ComplexQuery
I figured it out after a ton of trial and error. It seems like GetJSON can't handle passing the data. What I did to correctly get information was to change to an AJAX get call. I will post the code below
$.ajax({
url: '#Url.Action("GetEmailByAdvanced", "CustomerEmails")',
type: 'GET',
data: cqvdata,
success: function (data) {
//called when successful
var emails = "";
$.each(contacts, function (index, contact) {
$('#BCCText').tagit('createTag', contact.Email)
});
return false;
},
error: function (e) {
//called when there is an error
//console.log(e.message);
}
});
I used the exact data I had in the GetJSON. In fact I commented out the GetJSON and just put this in below it and I got my model filled in on the controller side.

Resources