Null foreign key in database - but why? - c#

I have a database full of vehicles. Each vehicle can have many pictures. Obviously, the pictures table has a Vehicle foreign key.
Whenever I add a picture, then try to retrieve it, I cannot. This is due to the fact that the Vehicle foreign key is returned as Null. Trouble is, I don't understand why. At this point in time, understand that on a database level, all the fields in the picture table are populated as expected. There are 3 fields Id (PK, int), Picture (nVarChar(max)) and VehicleId(FK, int)). Crucially, the foreign key (VehicleId) IS populated with a valid vehicle Id.
I am using the MVC.
First of all, code for the Picture Model.
public class Picture
{
public int Id {get; set;}
public string Image {get; set;}
[ForeignKey("VehicleId")]
public virtual Vehicle Vehicle{get;set;}
}
Secondly, Picture Controller code that saves a picture. Notice how I am setting the Vehicle Foreign Key.
IMPORTANT Clue - If I put test code in Immediately before 'return Created' to retrieve a picture, I can. Vehicle Foreign Key appears to be set. If I just run the application and try to retrieve a picture straight away (i.e withoud adding a new one), the Vehicle Foreign key is null. Why though? I have included the code I call that retrieves an individual picture.
public async Task<IActionResult> PostNewPicture(int vehicleId_, PictureViewModel picture_)
{
try
{
if(ModelState.IsValid)
{
var vehicle = _vehicleRepository.GetVehicleById(vehicleId_);
if (vehicle == null) return BadRequest("Vehicle Not Found");
var newPicture = _mapper.Map<Picture>(picture_);
newPicture.Vehicle = vehicle;
_vehicleRepository.AddPicture(newPicture);
if (await _vehicleRepository.SaveChangesAsync())
{
var url = _linkGenerator.GetPathByAction("GetIndividualPicture",
"Pictures",
new {vehicleId_ = newPicture.VehicleForeignKey.Id,
pictureId_ = newPicture.Id});
var pictureViewModel = _mapper.Map<PictureViewModel>(newPicture);
return Created(url, _mapper.Map<PictureViewModel>(newPicture));
}
}
return BadRequest("Failed to save the picture");
}
catch(Exception ex)
{
return BadRequest($"Exception Thrown : {ex}");
}
}
Code to retrieve an individual picture:
public Picture GetIndividualPicture(int vehicleId_, int pictureId_)
{
_vehicleContext.Pictures.Include(vp => vp.Vehicle);
IEnumerable<Picture> pictures = from v in _vehicleContext.Pictures.ToList()
.Where(x => x.Vehicle.Id == vehicleId_
&& x.Id == pictureId_) select v;
return pictures.FirstOrDefault();
}
Why VehicleForeignKey is Null, when it is clearly set at the point of adding?

Kudos to Gert Arnold for this one. He correctly states
the second code snippet. You don't have _vehicleContext.VehiclePictures.Include(vp => vp.VehicleForeignKey)
I feel like a bit of a fraud answering this myself - again, It was Gert. Here are my check in notes on this....
12th November 2019
Pictures Controller - I could not understand why the foreign Key withing the Pictures
table was always being returned as null. The answer was because I was not Including it.
See Include in the query below. Dont include this and Vehicle is always null.
ALSO - I was using .ToList() immediately after the .Include(vp => vp.Vehicle)
dont do this - it loads the entire result set into memory BEFORE any filtering is done
public Picture GetIndividualPicture(int vehicleId_, int pictureId_)
{
IEnumerable<Picture> pictures = from v in _vehicleContext.Pictures
.Include(vp => vp.Vehicle)
.Where(x => x.Vehicle.Id == vehicleId_
&& x.Id == pictureId_) select v;
return pictures.FirstOrDefault();
}

Related

How do i extend a model to fill in a grid without having the foreign keys validated upon saving?

We have a design paradigm we are considering standardizing to but it has some problems and none of us have a good solution for it.
1) We create a core model that contains all the general fields and index values of any foreign keys. This is tied to a table and in our design will be edited in a popup from our grids.
2) a second class inherits the first one and has all foreign key references. This class would populate grids. This would allow us to show specific info from foreign tables.
Now for the Problem: The popup model must match the model for the grid (we are using telerik grids) and when we save, EF tries to validate anything even remotely connected to the table through the foreign keys and anything they reference through foreign keys etc. We once had a record throw an error because we tried to edit an order and the truck assigned to the order's driver was missing mileage info.
I tried (successfully) to replace foreign key references with notmapped fields and load them in the index file but it was slow compared to EF. I looped through each record in the grid (for each) and looked up each matching model and added it.
Another programmer got around the problem by creating two completely different models and having the 2nd model with the foreign keys point to an alias of the table. Personally this feels illegal to me.
Our third developer did something similar but pulled data from a view. He actually had a good reason though as the view had a calculated "locked" column based on financial settlement.
So there you have it. Three different methods to solve the same issue. I've also tried "exclude" from the post but i was evidently doing it wrong.
We considered shutting off the validation in EF but nobody is familiar enough with it to know if it kills the validation on that page as well. MVC is a recent step for us. Our site was originally in asp. The driving force in fact was our orders grid which could take a minute to load.
**So after i gave you all that info, here's the question: ** What is the best way to set our models up? I am the junior programmer and feel like we are doing it wrong in all three scenarios. More importantly i feel like it's completely unnecessary. I would like to bind a model to a grid with full data that inherits a core model but that's all we validate. How is that done?
Example controller:
namespace DispatchCrude.Controllers
{
[Authorize(Roles = "viewShipperTimeCardWorkTypeRates")]
public class ShipperTimeCardWorkTypeRatesController : _DBController
{
[HttpGet]
public ActionResult Index(int? orderid, int? typeid)
{
// Send through any filters
if (orderid != null)
ViewBag.OrderID = orderid;
if (typeid != null)
ViewBag.TypeID = typeid;
return View();
}
// Read
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
virtual public ContentResult Read([DataSourceRequest] DataSourceRequest request = null, int id = 0, int? orderid = null)
{
return Read(request, null, id, orderid);
}
protected ContentResult Read([DataSourceRequest] DataSourceRequest request, ModelStateDictionary modelState, int id = 0, int? orderid = null)
{
var data = db.ShipperTimeCardWorkTypeRates
.Where(m => m.ID == id || id == 0).ToList();
if (orderid != null)
{
var validRates = db.Database.SqlQuery<int>(#"SELECT R.ID
FROM dbo.viewOrder O
CROSS APPLY dbo.fnShipperTimeCardRates(isnull(O.OrderDate, O.DueDate), null, null,
O.TicketTypeID, O.DestTicketTypeID, O.CustomerID, CarrierTypeID, O.CarrierID, DriverGroupID, O.DriverID, O.TruckTypeID, O.ProductGroupID,
O.DestinationID, O.OriginID, O.DestStateID, O.OriginStateID, O.DestRegionID, O.OriginRegionID, O.ProducerID, 0) R
WHERE O.ID = " + orderid);
data = data.Where(m => validRates.Contains(m.ID)).ToList();
}
return ToJsonResult(data, request, modelState);
}
// Create
[HttpPost]
[Authorize(Roles = "createShipperTimeCardWorkTypeRates")]
public ActionResult Create([DataSourceRequest] DataSourceRequest request, ShipperTimeCardWorkTypeRateBase timeCardWorkTypeRates)
{
// Create functionality is now taken care of in the update function as of Kevin's 2017 update to how MVC controllers are written
return Update(request, timeCardWorkTypeRates);
}
// Update
[HttpPost]
[Authorize(Roles = "editShipperTimeCardWorkTypeRates")]
public ActionResult Update([DataSourceRequest] DataSourceRequest request, ShipperTimeCardWorkTypeRateBase timeCardWorkTypeRates)
{
try
{
DateTime checkDate1;
DateTime checkDate2;
bool success1 = DateTime.TryParse(timeCardWorkTypeRates.EffectiveDate.ToString(), out checkDate1);
bool success2 = DateTime.TryParse(timeCardWorkTypeRates.EndDate.ToString(), out checkDate2);
if (success1 && success2 && checkDate2.Subtract(checkDate1).TotalHours < 0)
{
ModelState.AddModelError("Update", "End Date must be equal to or later than Start Date.");
}
if (ModelState.IsValid)
{
// Create new record or update existing record
db.AddOrUpdateSave(User.Identity.Name, timeCardWorkTypeRates);
}
}
catch (Exception ex)
{
ModelState.AddModelError("Update", ex.Message); // TODO: use a common routine to "cleanup" db generated errors
return App_Code.JsonStringResult.Create(new[] { timeCardWorkTypeRates }.ToDataSourceResult(request, ModelState));
}
return Read(request, ModelState, timeCardWorkTypeRates.ID);
}
// Delete (Deactivate)
[HttpPost]
[Authorize(Roles = "deleteShipperTimeCardWorkTypeRates")]
public ActionResult Delete([DataSourceRequest] DataSourceRequest request, ShipperTimeCardWorkTypeRateBase shipperTimeCardWorkTypeRate)
{
// Delete the record
db.ShipperTimeCardWorkTypeBases.Attach(shipperTimeCardWorkTypeRate);
db.ShipperTimeCardWorkTypeBases.Remove(shipperTimeCardWorkTypeRate);
db.SaveChanges(User.Identity.Name);
return null;
}
public ContentResult getBestMatch(int orderid, int? typeid = null)
{
string sql = "SELECT DISTINCT ID FROM fnOrderShipperTimeCardRates(" + orderid + ")";
if (typeid != null)
sql += " WHERE WorkTypeID = " + typeid;
var bestmatchids = db.Database.SqlQuery<int>(sql);
return ToJsonResult(bestmatchids.ToList());
}
}
}
`
the popup editor uses #model DispatchCrude.Models.ShipperTimeCardWorkTypeRate
the grid uses the same. This split works but EF won't allow us to set two models to the same table, and the inheriting didn't work either.
What do we do?

cannot "Add or Update" Entity because Primery key cannot be changed

I am implementing an import routine, where a user pastes a specific formatted string into an input field, which in turn gets tranformated into an entity and then put into a database.
The algorithm checks if the entity already exists and either tries to update it or insert it into the database. Inserting works fine - updating fails.
//considered existing if Name and owning user match.
if (db.Captains.Any(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId))
{
var captainToUpdate = db.Captains.Where(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId).SingleOrDefault();
db.Entry(captainToUpdate).CurrentValues.SetValues(captain);
db.Entry(captainToUpdate).State = EntityState.Modified;
await db.SaveChangesAsync();
}
The problem at hand is, that written like this, it tries to update the primary key as well, (captain Id is 0, whereas captainToUpdate Id is already set) which results in an exception The property 'Id' is part of the object's key information and cannot be modified.
What do I need to change, so the enttiy gets updated properly. If it can be avoided I don't want to update every property by hand, because the table Captain contains 30ish columns.
What you can do is first set the Id of captain to be the same as the Id of captainToUpdate:
captain.Id = captainToUpdate.Id;
db.Entry(captainToUpdate).CurrentValues.SetValues(captain);
await db.SaveChangesAsync();
I would not use the entity Captain to transfer the data to the UI, but a DTO object that has all properties you want to copy and no more. You can copy values from any object. All matching properties will be copied, all other properties in captainToUpdate will not be affected.
Try something like this ?
var captainToUpdate = db.Captains.FirstOrDefault(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId);
if(captainToUpdate != null){//Update captain Here
captainToUpdate.Update(captain);
}else{//Create captain here
db.Captains.Add(captain);
}
db.Savechanges();
I had the same issue and solved it by extension method and reflection, of course it will be better to create standalone class with some cachning for relfection, but performance wasn't critical in my task.
public static class EnitityFrameworkHelper
{
public static void SetValuesByReflection(this DbPropertyValues propertyValues, object o, IEnumerable<string> properties = null)
{
var reflProperties = o.GetType().GetProperties();
var prop = properties ?? propertyValues.PropertyNames;
foreach (var p in prop)
{
var refp = reflProperties.First(x => x.Name == p);
var v= refp.GetValue(o);
propertyValues[p] = v;
}
}
}
and here is example how to use it
var entry = ctx.Entry(accSet);
entry.CurrentValues.SetValuesByReflection(eParameters, entry.CurrentValues.PropertyNames.Except(new [] { "ID"}));
Also be careful with foreign keys in object which you want to update, probably you want to exclude them too.

Trouble with Code First DatabaseGenerated Composite Primary Key

This is a tad complicated, and please, I know all the arguments against natural PK's, so we don't need to have that discussion.
using VS2012/MVC4/C#/CodeFirst
So, the PK is based on the date and a corresponding digit together. So, a few rows created today would be like this:
20131019 1
20131019 2
And one created tomorrow:
20131020 1
This has to be automatically generated using C# or as a trigger or whatever. The user wouldn't input this. I did come up with a solution, but I'm having problems with it, and I'm a little stuck, hence the question.
So, I have a model:
public class MainOne
{
//[Key]
//public int ID { get; set; }
[Key][Column(Order=1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string DocketDate { get; set; }
[Key][Column(Order=2)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string DocketNumber { get; set; }
[StringLength(3, ErrorMessage = "Corp Code must be three letters")]
public string CorpCode { get; set; }
[StringLength(4, ErrorMessage = "Corp Code must be four letters")]
public string DocketStatus { get; set; }
}
After I finish the model, I create a new controller and views using VS2012 scaffolding.
Then, what I'm doing is debugging to create the database, then adding the following instead of trigger after Code First creates the DB [I don't know if this is correct procedure]:
CREATE TRIGGER AutoIncrement_Trigger ON [dbo].[MainOnes]
instead OF INSERT AS
BEGIN
DECLARE #number INT
SELECT #number=COUNT(*) FROM [dbo].[MainOnes] WHERE [DocketDate] = CONVERT(DATE, GETDATE())
INSERT INTO [dbo].[MainOnes] (DocketDate,DocketNumber,CorpCode,DocketStatus) SELECT (CONVERT(DATE, GETDATE
())),(#number+1),inserted.CorpCode,inserted.DocketStatus FROM inserted
END
And when I try to create a record, this is the error I'm getting:
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: The object state cannot be changed. This exception may result from one or more of the primary key properties being set to null. Non-Added objects cannot have null primary key values. See inner exception for details.
Now, what's interesting to me, is that after I stop debugging and I start again, everything is perfect. The trigger fired perfectly, so the composite PK is unique and perfect, and the data in other columns is intact.
My guess is that EF is confused by the fact that there is seemingly no value for the PK until AFTER an insert command is given. Also, appearing to back this theory, is that when I try to edit on of the rows, in debug, I get the following error:
The number of primary key values passed must match number of primary key values defined on the entity.
Same error occurs if I try to pull the 'Details' or 'Delete' function.
Any solution or ideas on how to pull this off? I'm pretty open to anything, even creating a hidden int PK. But it would seem redundant.
EDIT 21OCT13
[HttpPost]
public ActionResult Create(MainOne mainone)
{
if (ModelState.IsValid)
{
var countId = db.MainOnes.Count(d => d.DocketDate == mainone.DocketNumber); //assuming that the date field already has a value
mainone.DocketNumber = countId + 1; //Cannot implicitly convert type int to string
db.MainOnes.Add(mainone);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(mainone);
}
EDIT 21OCT2013 FINAL CODE SOLUTION
For anyone like me, who is constantly searching for clear and complete solutions.
if (ModelState.IsValid)
{
String udate = DateTime.UtcNow.ToString("yyyy-MM-dd");
mainone.DocketDate = udate;
var ddate = db.MainOnes.Count(d => d.DocketDate == mainone.DocketDate); //assuming that the date field already has a value
mainone.DocketNumber = ddate + 1;
db.MainOnes.Add(mainone);
db.SaveChanges();
return RedirectToAction("Index");
}
hy don't you manage it via code instead of using a trigger?
var countId = context.MainOne.Count(d => d.DocketDate == newItem.DocketNumber); //assuming that the date field already has a value
newItem.DocketNumber = CountId + 1;
Context.MainOne.Add(newItem);
context.SaveChanges();
And this should solve your problem.

Delete junction table record in Entity Framework Code First (M:M)

How do I delete a record in a junction table within Entity Framework 5?
When reverse engineering my DataContext, Entity Framework seems to have recognized my junction table and automatically added Collections to my Models to represent the M:M relationship. This is great when adding items, as I can simply build my entire Entity and everything gets inserted properly. Perfect.
However, I'm stumped on removing a relationship. For example, an Activity can have multiple Contacts associated to it, and this is linked using a junction table (dbo.ActivityContacts) that consists of the columns:
ActivityID
ContactID
Both my Activity and Contact models have been updated by EF with Collections to represent the other. For example, my Activity model looks like this:
public class Activity
{
public int ActivityID { get; set; }
public string Subject { get; set; }
public virtual ICollection<Contacts> Contacts { get; set; }
}
In a non-EF environment, I would simply delete the record from the junction table and move on with my day. However, it seems I cannot access the junction table directly using EF, so I'm a tad confused on how to remove the record (relationship).
How can I properly remove a record from a junction table in Entity Framework?
Agree with #Chris.
Another solution is to do:
context.Entry(activity).State = EntityState.Deleted;
Entity Framework should remove the record for you, if you remove the associated object from either side of the relationship.
Assuming you've obtained this Activity instance from your context and want to remove a specific Contact with a known ID:
unwantedContact = context.Contacts.Find(contactID);
myActivity.Contacts.Remove(unwantedContact);
context.SaveChanges();
Should delete the record in your junction table, unless I'm being daft.
ali golshani did a good job providing a solution. Let me try to expand on it a little more. In my scenario I have two list boxes where you can move items left or right (selected or not selected)
The 'dto' object below is sent from the client. It's checking the selected state for each item in the list. If anyone knows of any way to improve this any more please leave feedback.
file_appender selectedAppender = context.file_appender.Find(dto.Id);
int[] ids = dto.Loggers.Where(x => !x.Selected).Select(x => x.Id).ToArray();
var loggers_to_delete = selectedAppender.logger.Where(x => ids.Contains(x.id));
loggers_to_delete.ToList().ForEach(x =>
{
selectedAppender.logger.Remove(x);
});
ids = dto.Loggers.Where(x => x.Selected).Select(x => x.Id).ToArray();
var loggers_to_add = context.logger.Where(x => ids.Contains(x.id));
loggers_to_add.ToList().ForEach(x =>
{
selectedAppender.logger.Add(x);
});
Lets look at another example....This one is for a list box with embedded check boxes (a little simpler). Honestly this could probably be applied to the solution above to make easier to read code.
protected void saveRelatedConnectors(test_engine testEngine, List<int> connectorTypes)
var stepConnectorsToDelete = testEngine.step_connector.Where(x => (connectorTypes.Count == 0) ||
(connectorTypes.Count != 0 && !connectorTypes.Contains(x.id)));
stepConnectorsToDelete.ToList().ForEach(x =>
{
testEngine.step_connector.Remove(x);
});
var stepConnectorsToAdd = entities.step_connector.Where(x => connectorTypes.Contains(x.id));
stepConnectorsToAdd.ToList().ForEach(x =>
{
testEngine.step_connector.Add(x);
});
entities.SaveChanges();
contact_to_delete = context.Contacts.Find(contactID);
selected_activity = context.Activity.Find(ActivityID);
context.Entry(selected_activity).Collection("Activity").Load();
selected_activity.Contacts.Remove(contact_to_delete);
db.SaveChanges();

What is the correct way of using Entity Framework?

I have a DB like this that I generated from EF:
Now I'd like to add a "fielduserinput" entity so I write the following code:
public bool AddValueToField(string field, string value, string userId)
{
//adds a value to the db
var context = new DBonlyFieldsContainer();
var fieldSet = (from fields in context.fieldSet
where fields.fieldName.Equals(field)
select fields).SingleOrDefault();
var userSet = (from users in context.users
where users.id.Equals(userId)
select users).SingleOrDefault();
var inputField = new fielduserinput { userInput = value, field = fieldSet, user = userSet };
return false;
}
Obviously it's not finished but I think it conveys what I'm doing.
Is this really the right way of doing this? My goal is to add a row to fielduserinput that contains the value and references to user and field. It seems a bit tedious to do it this way. I'm imagining something like:
public bool AddValueToField(string userId, string value, string fieldId)
{
var context = new db();
var newField = { field.fieldId = idField, userInput = value, user.id = userId }
//Add and save changes
}
For older versions of EF, I think you're doing more or less what needs to be done. It's one of the many reasons I didn't feel EF was ready until recently. I'm going to lay out the scenario we have to give you another option.
We use the code first approach in EF 4 CTP. If this change is important enough, read on, wait for other answers (because Flying Speghetti Monster knows I could be wrong) and then decide if you want to upgrade. Keep in mind it's a CTP not an RC, so considerable changes could be coming. But if you're starting to write a new application, I highly recommend reading some about it before getting too far.
With the code first approach, it is possible to create models that contain properties for a reference to another model and a property for the id of the other model (User & UserId). When configured correctly setting a value for either the reference or the id will set the id correctly in the database.
Take the following class ...
public class FieldUserInput{
public int UserId {get;set;}
public int FieldId {get;set;}
public virtual User User {get;set;}
public virtual Field Field {get;set;}
}
... and configuration
public class FieldUserInputConfiguration{
public FieldUserInputConfiguration(){
MapSingleType(fli => new {
userid = fli.UserId,
fieldid = fli.FieldId
};
HasRequired(fli => fli.User).HasConstraint((fli, u)=>fli.UserId == u.Id);
HasRequired(fli => fli.Field).HasConstraint((fli, f)=>fli.FieldId == f.Id);
}
}
You can write the code...
public void CreateField(User user, int fieldId){
var context = new MyContext();
var fieldUserInput = new FieldUserInput{ User = user, FieldId = fieldId };
context.FieldUserInputs.Add(fieldUserInput);
context.SaveChanges();
}
... or vice versa with the properties and everything will work out fine in the database. Here's a great post on full configuration of EF.
Another point to remember is that this level of configuration is not necessary. Code first is possible to use without any configuration at all if you stick to the standards specified in the first set of posts referenced. It doesn't create the prettiest names in the database, but it works.
Not a great answer, but figured I'd share.

Categories