This is database first using entity frameworks 6 and lazy loading.
I have the following class of device that is missing four foreign key ids:
public partial class Device {
public int SolutionId { get; set; }
public string SiteId { get; set; }
public string Name { get; set; }
public int SysId { get; set; }
public Nullable<int> SysType { get; set; }
public string SerialNumber { get; set; }
public Nullable<int> ParentId { get; set; }
public virtual DeviceModel DeviceModel { get; set; }
public virtual DeviceType DeviceType { get; set; }
public virtual SolutionApplication SolutionApplication { get; set; }
public virtual SolutionType SolutionType { get; set; }
}
Missing Keys/Ids that are not generated but automatically got mapped as virtual objects:
DeviceModelId, DeviceTypeId, SolutionApplicationId, and SolutionTypeId
I want to save a new device using breeze but it is looking for the device type. I tried adding the deviceTypeId before saving:
newDevice.deviceTypeId = 5;
But it still doesn't get read and does not get saved.
[Error] Save failed: Entities in 'PortalEntities.Devices' participate in the 'FK_Devices_DeviceTypes' relationship. 0 related 'DeviceType' were found. 1 'DeviceType' is expected.
This is how my save statement in my breeze controller:
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _repository.SaveChanges(saveBundle);
}
I checked what actually is getting passed as it attempts to do a save. The save bundled contained the following entity. BUT the device type which is a required field is not there, hence the error that its missing.
"entities": [
{
"SolutionId": -1,
"SiteId": "11111d2",
"Name": "asdf",
"SysId": 0,
"SysType": null,
"SerialNumber": null,
"ParentId": null,
"entityAspect": {
"entityTypeName": "Device:#HtPortal.Data.Portal",
"defaultResourceName": "Devices",
"entityState": "Added",
"originalValuesMap": {},
"autoGeneratedKey": {
"propertyName": "SolutionId",
"autoGeneratedKeyType": "Identity"
}
}
}
Since the deviceTypeId did not work, so I tried adding the deviceType right before saving:
newDevice.deviceType = { id:5, name: 'Blue'}; //NOTE: This already exists in the devicetype table in the database
But I get the following error:
TypeError: Cannot read property 'entityState' of undefined
Using Breeze, How do I add this foreign entity so that I can save this new device?
Edit 1: This is my DeviceType Class:
public partial class DeviceType {
public DeviceType() {
this.Devices = new HashSet<Device>();
}
public byte Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Device> Devices { get; set; }
}
Just add the existing DeviceType object from your dbContext.
Something like:
using (YourDbEntities context = new YourDbEntities())
{
Device newDevice = new Device();
//whatever initialisation you need do....
newDevice.DeviceType = context.DeviceTypes.Find(5)
// the above line assumes that id is PK for DeviceType and you want the entry
// with id==5 - if not, other ways such as DeviceTypes.Where(d=>d.Id==5).First()
// to find the object in your existing db will work too!
context.Devices.Add(newDevice);
context.SaveChanges();
}
Related
I'm making a task management tool using AngularJS for the frontend and ASP.NET WEB API 2 for the backend. I have two entities in the database, a "Task" and a "Type". Each task has one type associated. The user fills a form when he can create a new task, and he has to select a type for that task.
Here's the C# code:
// KBTM_Task.cs file
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
}
// KBTM_Type.cs file
public class KBTM_Type
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
So my question is: how do I "connect" the two in the database? What I mean is, let's say I want to POST data to the database. I have to make two POSTs, right? One for the Task and one for the Type, since they're two separate entities.
But since they're stored with two different IDs, how do I know that a certain task has a certain type? In other words, if I send a GET request to KBTM_Task, how do I get the type of that task?
Modify your KBTM_Task entity to include the Type Id and foreign key relationship
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
public int TypeID { get; set; }
[ForeignKey("TypeID")]
public virtual KBTM_Type Type { get; set; }
}
This way when you get the data from the API your task object will already include the key ("TypeID") that can be updated and related object ("Type") that you can access its properties (Name, Description, ...).
When you update TypeID on the client object (model) you can simply push the updated task object to the API using $http.put() to handle the database update.
1) Add foreign key using fluent api (or data annotation)
// KBTM_Task.cs file
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
public int KBTM_TypeID {get;set}
public virtual KBTM_Type {get; set}
}
// KBTM_Type.cs file
public class KBTM_Type
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public KBTM_Task KBTM_Task { get; set;}
}
Add the following in the class inheriting from DbContext
public class KbtmContext : DbContext
{
...
//public virtual DbSet<KBTM_Task> KbtmTasks {get; set;}
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure KBTM_TypeID as FK for KBTM_Task
modelBuilder.Entity<KBTM_Task>()
.HasRequired(k => k.KBTM_Type)
.WithRequiredPrincipal(ad => ad.KBTM_Task);
}
}
2) If exposing the entity class in API response or request then you need to exclude navigation property from being serialized.
// KBTM_Task.cs file
public class KBTM_Task
{
...
[JsonIgnore]
public virtual KBTM_Type Type { get; set; }
}
To use the [JsonIgnore] atttribute use Install-Package Newtonsoft.Json in package manager console.(One of the popular solutions to manage serialization)
My database will run correctly, and I can input the data manually via SQL Server, however, when I try and pass the value in via my API (testing using Postman), the value won't pass into the database, it appears as "NULL".
I have a reports and a bookings tables.
This is the code for the reports:
public class Report
{
public Report()
{
Injuries = new List<Injury>();
this.Bookings = new HashSet<Booking>();
}
public int Id { get; set; }
public string Club1 { get; set; }
public string Club2 { get; set; }
public virtual ICollection<Injury> Injuries { get; set; }
public virtual ICollection<Booking> Bookings { get; set; }
}
Bookings:
public class Booking
{
//public Booking()
//{
// Reports = new List<Report>();
//}
public int Id { get; set; }
public string Club { get; set; }
public string PlayerName { get; set; }
public string PlayerNumber { get; set; }
public string Reason { get; set; }
public string Description { get; set; }
//public int? Report_Id { get; set; }
public Nullable<int> Report_Id { get; set; }
public virtual Report Report { get; set; }
//public virtual ICollection<Report> Reports { get; set; }
}
Controller:
//POST: api/Reports
[ResponseType(typeof(Report))]
public async Task<IHttpActionResult> PostReport(Report report)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Reports.Add(report);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = report.Id }, report);
}
I put the test information via Postman:
I'm not sure why Report_Id is showing as it's not required, however, Report_Id1 is the field that is connecting the Report and Booking together.
Since your foreign key doesn't follow convention (ReportId), you need to use the annotation [ForeignKey] or a fluent api configuration:
modelBuilder.Entity<Booking>()
.HasRequired(b => b.Report)
.WithMany(b => b.Bookings)
.HasForeignKey(p => p.Report_Id);
That is why EF is adding the second Report_ID1. https://msdn.microsoft.com/en-us/data/hh134698.aspx
public partial class User
{
public System.Guid UserId { get; set; }
public Nullable<System.Guid> RoleId { get; set; }
public Nullable<long> MembershipNo { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public string Emaiil { get; set; }
public Nullable<decimal> MobileNo { get; set; }
public string Description { get; set; }
public Nullable<System.Guid> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual Role Role { get; set; }
}
This is my table in DB named Users which is associated with Roles table of DB (as you can see last virtual row at the end above)
Now My problem is simple. I'm using angulars $http.get() method to call my Web Api in MVC 4. When i call it, it gets connected and fetches desired record but it doesn't throw proper result back to .js file or controller.
At .js side I run into error. Every time, it executes .error(jsonResult,config,header,status) .
When I jump on to JsonResult, it shows me below error.
Object
ExceptionMessage: "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'."
ExceptionType: "System.InvalidOperationException"
InnerException: Object
ExceptionMessage: "Self referencing loop detected for property 'Role' with type
'System.Data.Entity.DynamicProxies.Role_82CA96EA045B1EB47E58B8FFD4472D86502EEA79837B4AE3AD705442F6236E58'.
Path 'Role.Users[0]'."
ExceptionType: "Newtonsoft.Json.JsonSerializationException"
Message: "An error has occurred."
I don't know what's wrong here. Is it json parsing error or something? if so, I've heard and read the articles that webapi in .net handles or throws json itself.
My call happens through
$http.get(apiUrl).success(function (jsonResult, header, config, status) {
debugger;
var number = parseInt(jsonResult.membershipNo) + 1;
$scope.membershipNo = "M" + number;
})
.error(function (jsonResult, header, config, status) {
debugger;
toastr.error('Something went wrong ! Contact Administrator!!!');
});
Edited:
One more thing to mention, .CS side when I fetch single cell value (from DB/table) , it gets returned back to .success() call but when i fetch particular row or all rows, it gets returned to .error() call. I'm using entity frameworkd 6.1.1. and above class is generated by EF-6.1.1.
public partial class Role
{
public Role()
{
this.Permissions = new List<Permission>();
this.Users = new List<User>();
}
public System.Guid RoleId { get; set; }
public string RoleName { get; set; }
public string Description { get; set; }
public Nullable<System.Guid> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual ICollection<Permission> Permissions { get; set; }
public virtual ICollection<User> Users { get; set; }
}
Hi you can solve that in 2 easy steps
First Step: Create globalConfig class where you can set ignoring ReferenceLoopHandling (http://james.newtonking.com/json/help/index.html?topic=html/SerializationSettings.htm) and if you crating js app you can set as well to remove xml formaters and always get return from Webapi as JSON string is usefull for debugging. So in your app_start folder add class GlobalConfig like below:
public class GlobalConfig
{
public static void CustomizeConfig(HttpConfiguration config)
{
// Remove Xml formatters. This means when we visit an endpoint from a browser,
// Instead of returning Xml, it will return Json.
//that is optional
config.Formatters.Remove(config.Formatters.XmlFormatter);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
}
Second Step: In Global.asax set your custom configuration to do that please add code below to method Application_Start():
GlobalConfig.CustomizeConfig(GlobalConfiguration.Configuration);
it sounds like:
the problem is that EF is using lazy loading that is not materialized in time of constructing this, on role. EF from early version has switched lazy loading on by default.
Suggested solution
Create subset of you user class, with the parts that you really need.
=> Its bad practise to fetch too much data that you are not gonna need.
I'm having some doubts, maybe newbie doubts but I just got into ASP.NET MVC 4
Basically I would like to know the correct way of grabbing details of an Object inside a model.
In this case Image inside Contractor.
Model:
public class Contractor {
[Key]
public int ContractorID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Address { get; set; }
public Image Avatar { get; set; }
}
public class Image {
[Key]
public int ImageID { get; set; }
[Required]
public string File_name { get; set; }
[Required]
public byte[] File_data { get; set; }
}
public class DATACRUD : DbContext {
public DbSet<Contractor> Companies { get; set; }
public DbSet<Image> Images { get; set; }
}
Controller:
private DATACRUD db = new DATACRUD();
public ActionResult GetContractorAvatar(int id)
{
Contractor contractor = db.Contractors.Find(id);
if (contractor == null)
{
return HttpNotFound();
}
Image avatar = contractor.Avatar;
Problem 1)
avatar == null, but is not suppose to be because when I created the object Contractor, I added the image sucessfully (I checked in the DB and it is there)
The solution I'm seeing is instead of having Image property in Contractor.cs model, I would just put a string property with the image key.
Problem 2)
Even If could grab the image key like I said in the previous problem, when I pass my mouse in Debug mode over
private DATACRUD db = new DATACRUD ();
db.Images is also empty...
return File(avatar.File_data, "image");
}
Because you haven't defined your Image navigation property as virtual, you will have to eager load the Image when loading a Contractor:
db.Contractors.Include("Avatar").SingleOrDefault(c => c.ContractorID == id);
OR
// using System.Data.Entity;
db.Contractors.Include(c => c.Avatar).SingleOrDefault(c => c.ContractorID == id);
I'm just learning ASP.NET MVC 3, And recently I tried a lot of times to pass arrays/lists/ICollections etc. but couldn't. everytime the list was empty.
For example, the current project:
Model:
public class Video
{
public int VideoID { get; set; }
public string Name { get; set; }
public ICollection<string> Tags { get; set; }
}
Initializer - Seed:
protected override void Seed(DatabaseContext context)
{
var videos = new List<Video>
{
new Video {
Name = "Video01",
Tags = new List<string> { "tag1", "tag2" },
};
videos.ForEach(s => context.Videos.Add(s));
context.SaveChanges();
base.Seed(context);
}
In the view: I do get the Name property, but the Tags are completely empty.
In the debug I get Tags - Count: 0.
This is not the first time it happens to me, to be honest it happens every single time when I try to pass those kind of stuff. a bit of info about the project:
ASP.NET MVC 3, Entity-Framework:Code First, SqlServerCe.4.0.
Crean an entity Tag
public class Video
{
public int VideoID { get; set; }
public string Name { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public int VideoId { get; set; }
public string TagText { get; set; }
}
or store tags to one field separated with comma /semicolon or whatever fits for your solution
By default Entity Framework doesn't load associations of an entity, you need to specify it explicitly:
var videos = context.Videos.Include("Tags");