Currently I am working on changing the values within a table row, which includes the following variables:
public int ID { get; set; }
public string Item_Name { get; set; }
[DisplayFormat(DataFormatString = "{0:0.##}")]
public decimal Price { get; set; }
public int TimeSlot { get; set; }
public bool Food_AddOns { get; set; }
public bool Drink_AddOns { get; set; }
public virtual Item_Description Item_Description { get; set; }
public virtual Item_Status Item_Status { get; set; }
public virtual Dinner Dinner { get; set; }
public string Ingredients { get; set; }
My View passes the values that are given from the user to this model:
public class Edit_AddItemModel
{
[Display(Name = "ID")]
public int ID { get; set; }
[Display(Name = "Item Name:")]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 1)]
public string New_ItemName { get; set; }
[DisplayFormat(DataFormatString = "{0:0.##}", ApplyFormatInEditMode = true)]
[Display(Name = "Price:")]
public decimal New_Price { get; set; }
[Display(Name = "Time Slot:")]
public int New_TimeSlot { get; set; }
[Display(Name = "Lunch Special?:")]
public bool New_Food_AddOns { get; set; }
[Display(Name = "Free Drink?:")]
public bool Drink_AddOns { get; set; }
[Display(Name = "Item Description:")]
public string New_Item_Description { get; set; }
public bool New_spicy { get; set; }
public bool New_gluten { get; set; }
public bool New_vegetarian { get; set; }
[Display(Name = "Dinner:")]
public string New_Dinner { get; set; }
[Display(Name = "Ingredients:")]
[StringLength(140, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 0)]
public string New_Ingredients { get; set; }
}
My values are passed correctly to the controller as illustrated in the image below, and is passed to a function called Edit_CheckAllValues. This is what the Edit_CheckAllValues looks like, which basically creates an ItemsModel Object with the what has been passed from the View:
private ItemsModel Edit_CheckAllValues(ItemsModel NewItem, Edit_AddItemModel model)
{
int CurrentItem_ItemStatus;
NewItem.ID = model.ID;
NewItem.Item_Name = model.New_ItemName;
NewItem.Price = model.New_Price;
NewItem.TimeSlot = model.New_TimeSlot;
NewItem.Food_AddOns = model.New_Food_AddOns;
NewItem.Drink_AddOns = model.Drink_AddOns;
NewItem.Item_Description = convertToForeignKey_ItemDescription(Convert.ToInt32(model.New_Item_Description));
//Get Current Item Status.
CurrentItem_ItemStatus = get_ItemStatus(model.New_spicy, model.New_gluten, model.New_vegetarian);
NewItem.Item_Status = convertToForeignKey_ItemStatus(CurrentItem_ItemStatus);
NewItem.Dinner = convertToForeignKey_Dinner(Convert.ToInt32(model.New_Dinner));
NewItem.Ingredients = model.New_Ingredients;
return NewItem;
}
An Example of what is returned, what the new ItemModel looks like, which contains the ID of the item I want to edit: The Return Value:
An Example of one of the foreign keys being changed from ID: 20 to ID: 2 Item Description Foreign Key Change:
The New Object is then passed back to the original ActionResult EditItem, which it is changing the state of the item selected, which works correctly when changing variables that are not foreign keys such as price, Item Name, TimeSlot, etc:
public ActionResult EditItem(EditItemModel model)
{
if (ModelState.IsValid)
{
ItemsModel newItem = new ItemsModel();
newItem = Edit_CheckAllValues(newItem, model.edit_AddItemModel);
ApplicationDbContext db = new ApplicationDbContext();
db.Items.Attach(newItem); //Tired both with Attach and Without Attach
db.Entry(newItem).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("ChangeItems", "Employee");
}
I'm not sure where in my code I need to make edits, but my guess is I need to grab the row first within my Edit_CheckAllValues instead of passing the values into a new Object and sending it to the database using EntityState.Modified.
Any help would be appreciated, for I have been stuck on this question for 3 days now.
Based off the reference to this page (ASP.Net MVC 4 Update value linked to a foreign key column)
I was able to figure out the problem. The portions I needed to update were my ItemsModel:
public class ItemsModel
{
public int ID { get; set; }
public string Item_Name { get; set; }
[DisplayFormat(DataFormatString = "{0:0.##}")]
public decimal Price { get; set; }
public int TimeSlot { get; set; }
public bool Food_AddOns { get; set; }
public bool Drink_AddOns { get; set; }
[ForeignKey("Item_Description")]
public virtual int Item_Description_ID { get; set; }
public virtual Item_Description Item_Description { get; set; }
[ForeignKey("Item_Status")]
public virtual int Item_Status_ID { get; set; }
public virtual Item_Status Item_Status { get; set; }
[ForeignKey("Dinner")]
public virtual int Dinner_ID { get; set; }
public virtual Dinner Dinner { get; set; }
public string Ingredients { get; set; }
}
Adding the [ForeignKey] from the API: using System.ComponentModel.DataAnnotations.Schema;
and update my Edit_CheckAllValues function:
private ItemsModel Edit_CheckAllValues(ItemsModel NewItem, Edit_AddItemModel model)
{
int CurrentItem_ItemStatus;
NewItem.ID = model.ID;
NewItem.Item_Name = model.New_ItemName;
NewItem.Price = model.New_Price;
NewItem.TimeSlot = model.New_TimeSlot;
NewItem.Food_AddOns = model.New_Food_AddOns;
NewItem.Drink_AddOns = model.Drink_AddOns;
//NewItem.Item_Description = convertToForeignKey_ItemDescription(Convert.ToInt32(model.New_Item_Description));
NewItem.Item_Description_ID = Convert.ToInt32(model.New_Item_Description);
//Get Current Item Status.
CurrentItem_ItemStatus = get_ItemStatus(model.New_spicy, model.New_gluten, model.New_vegetarian);
NewItem.Item_Status_ID = CurrentItem_ItemStatus;
// NewItem.Item_Status = convertToForeignKey_ItemStatus(CurrentItem_ItemStatus);
NewItem.Dinner_ID = 163; //Ignore this is my empty default Dinner.
// NewItem.Dinner = convertToForeignKey_Dinner(Convert.ToInt32(model.New_Dinner));
// CurrentItem.Item_Description = convertToForeignKey_ItemDescription(Convert.ToInt32(model.New_Item_Description));
NewItem.Ingredients = model.New_Ingredients;
return NewItem;
}
The entity-modified will finally register the changes to the foreign keys.
Related
I will try to keep this one short. Any help is welcome and appreciated!
I have 2 classes that have a many-to-many relationship and their composite key class. When I'm creating a new "appointment" I want to pick "dental procedures" that are in the system.
All works fine until I reach the AppointmentRepository where I try to save the newly created appointment. The error is as stated.
I tried to add the
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
or
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
annotations above the DentalProcedureId property in the DentalProcedure class, but nothing works. Saved the changes and dropped tables, deleted all of the migrations etc.
DentalProcedure class:
public class DentalProcedure
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int DentalProcedureId { get; set; }
[Required(ErrorMessage = "The name of the procedure must be specified")]
public string ProcedureName { get; set; }
[Required(ErrorMessage = "The price of the procedure must be specified")]
public decimal ProcedurePrice { get; set; }
public bool isEnabled { get; set; }
public List<CustomerProcedure> CustomerProcedures { get; set; }
public List<AppointmentProcedure> AppointmentProcedures { get; set; }
}
Appointment class:
public class Appointment
{
[Key]
public int AppointmentId { get; set; }
[Required]
public DateTime AppointmentStart { get; set; }
[Required]
public DateTime AppointmentEnd { get; set; }
[Required]
public string Title { get; set; }
public string ProcedureDescription { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public int WorkDaysId { get; set; }
public WorkDays WorkDays { get; set; }
public List<AppointmentProcedure> AppointmentProcedures { get; set; }
}
AppointmentProcedure class:
public class AppointmentProcedure
{
public int AppointmentId { get; set; }
public Appointment Appointment { get; set; }
public int DentalProcedureId { get; set; }
public DentalProcedure DentalProcedure { get; set; }
public bool ProcedureAppointmentCanceled { get; set; }
}
Home controller:
Appointment appointment = new Appointment
{
AppointmentStart = model.AppointmentStart,
AppointmentEnd = model.AppointmentEnd,
Title = model.Title,
ProcedureDescription = model.ProcedureDescription,
CustomerId = Id,
WorkDaysId = workkWeek.WorkDaysId,
};
foreach (var proc in model.DentalProcedures)
{
if (proc.isEnabled)
{
appointment.AppointmentProcedures = new List<AppointmentProcedure>
{
new AppointmentProcedure
{
Appointment = appointment,
DentalProcedure = proc,
ProcedureAppointmentCanceled = false
}
};
}
}
_appointment.CreateAppointment(appointment);
And the error :
Thanks once more in advance.
PS: I'm still learning so if I forgot to mention something, I apologize in advance!
Entity Framework needs to track the object in the database so you need to either attach or (what I usually prefer to do) load it from the database. Here's the changed code:
foreach (var proc in model.DentalProcedures)
{
if (proc.isEnabled)
{
//assuming the DBSet is called Procedures
var dbProc = await db.Procedures.FirstOrDefaultAsync(p => p.DentalProcedureId == id));
appointment.AppointmentProcedures = new List<AppointmentProcedure>
{
new AppointmentProcedure
{
Appointment = appointment,
DentalProcedure = dbProc, //now set the loaded entity
ProcedureAppointmentCanceled = false
}
};
}
}
Is there any way to somehow combine the data from two models and THEN map them both to the same viewModel in the context of an edit action?
I have never had to update several tables at once in an edit action in ASP.NET MVC with Entity Framework 6.1.3. This is the layout:
I have a DB table called "Address" which has fields for StreetNumber, StreetName, City, State, ZipCode. It has a one-to-one relationship with another table called Bars. As in, a bar can only have one address and one address can only have one bar.
Because I am storing this data in two separate tables, I am having a very difficult time trying to successfully implement an Edit action which takes data from one form (BarForm) and should update both the Bar and Address database tables. See my code:
BarController
public ActionResult Edit(int id)
{
var bar = _context.Bars.SingleOrDefault(m => m.Id == id);
var address = _context.Addresses.SingleOrDefault(a => a.BarId == id);
//Make sure that the id actually exists:
if (bar == null)
{
return HttpNotFound();
}
var viewModel = Mapper.Map<Bar, BarFormViewModel>(bar, new BarFormViewModel());
if (address == null)
{
address = new Address();
}
Mapper.Map<Address, BarFormViewModel>(address, viewModel);
viewModel.IsNew = false;
return View("BarForm", viewModel);
}
[ValidateAntiForgeryToken]
public ActionResult Save(BarFormViewModel bar)
{
if (!ModelState.IsValid)
{
var viewModel = Mapper.Map<BarFormViewModel, BarFormViewModel>(bar, new BarFormViewModel());
viewModel.IsNew = false;
return View("BarForm", viewModel);
}
if (bar.Id == 0)
{
var newbar = Mapper.Map<BarFormViewModel, Bar>(bar);
newbar.LastUpdated = DateTime.UtcNow;
_context.Bars.Add(newbar);
var addressToAdd = Mapper.Map<BarFormViewModel, Address>(bar);
_context.Addresses.Add(addressToAdd);
}
else
{
var barInDb = _context.Bars.Single(b => b.Id == bar.Id);
var addressInDb = _context.Addresses.Single(a => a.BarId == bar.Id);
Mapper.Map<BarFormViewModel, Bar>(bar, barInDb);
Mapper.Map<BarFormViewModel, Address>(bar, addressInDb);
}
_context.SaveChanges();
return RedirectToAction("Index", "Bar");
}
Domain Models:
public class Bar
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public string GooglePlaceId { get; set; }
public string SundayDiscounts { get; set; }
public string MondayDiscounts { get; set; }
public string TuesdayDiscounts { get; set; }
public string WednesdayDiscounts { get; set; }
public string ThursdayDiscounts { get; set; }
public string FridayDiscounts { get; set; }
public string SaturdayDiscounts { get; set; }
[Display(Name = "Last Updated")]
public DateTime LastUpdated { get; set; }
}
public class Address
{
public int Id { get; set; }
public int? Number { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public string State { get; set; }
[Required]
public int ZipCode { get; set; }
public Bar Bar { get; set; }
public int BarId { get; set; }
}
View Model which includes both Address and Bar properties:
{
public class BarFormViewModel
{
public int? Id { get; set; }
public string Name { get; set; }
[Required]
[Display(Name = "Google Place ID")]
public string GooglePlaceId { get; set; }
[Display(Name = "Sunday Happy Hour Info:")]
public string SundayDiscounts { get; set; }
[Display(Name = "Monday Happy Hour Info:")]
public string MondayDiscounts { get; set; }
[Display(Name = "Tuesday Happy Hour Info:")]
public string TuesdayDiscounts { get; set; }
[Display(Name = "Wednesday Happy Hour Info:")]
public string WednesdayDiscounts { get; set; }
[Display(Name = "Thursday Happy Hour Info:")]
public string ThursdayDiscounts { get; set; }
[Display(Name = "Friday Happy Hour Info:")]
public string FridayDiscounts { get; set; }
[Display(Name = "Saturday Happy Hour Info:")]
public string SaturdayDiscounts { get; set; }
[Display(Name = "Last Updated")]
public DateTime? LastUpdated { get; set; }
//Address Model Info
public Address Address { get; set; }
public int? AddressId { get; set; }
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "Must be a number")]
public int? Number { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public string State { get; set; }
[Required]
public int? ZipCode { get; set; }
public bool IsNew { get; set; }
}
The problem here is that I am getting an empty AddressId with this setup, which is causing an exception when the Save action gets run. This is because the BarForm view is getting passed a ViewModel which has been mapped from a Bar object and the Bar domain model actually has no Address information in it, since it is not the Address model/table.
Is there any way to somehow combine the data from both the Address and Bar models and THEN map them both to the same viewModel?
I keep getting a Sequence Contains no Elements error for this line in the Save action:
var addressInDb = _context.Addresses.Single(a => a.Id == bar.AddressId);
I also tried:
var addressInDb = _context.Addresses.Single(a => a.BarId == bar.Id);
Neither work. I understand what the error is saying and have also checked the actual HTML for my hidden Addressid field and it is blank... See code in my BarForm View:
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.AddressId)
#Html.AntiForgeryToken()
Remove the new BarFormViewModel() as the second parameter in your mapping calls as it is not necessary.
In your post action, inside your if statement that checks if the ModelState is valid and if bar.Id == 0, bar is already a view model, so no need to mapping.
And when you create your AutoMapper mapping, you must create a custom property mapping because the Address.Id property will not map automatically to the AddressId property as the name is not the same.
AutoMapper.Mapper.CreateMap<Address, BarFormViewModel>()
.ForMember(dest => dest.AddressId, o => o.MapFrom(source => source.Id));
And then do the same for the inverse mapping.
I have this model (Animal Model):
public int Id { get; set; }
public int AnimalSpecieId { get; set; }
public int AnimalBreedId { get; set; }
public Nullable<int> ProtectorId { get; set; }
public Nullable<int> OwnerId { get; set; }
public string Name { get; set; }
public virtual Owner Owner { get; set; }
public virtual Protector Protector { get; set; }
Protector Model:
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string CellPhone { get; set; }
public string Email { get; set; }
public virtual ICollection<Animal> Animals { get; set; }
Owner Model:
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string CellPhone { get; set; }
public string Email { get; set; }
public virtual ICollection<Animal> Animals { get; set; }
When I insert this model at the first time, if
ProtectorID = 1
and
OwnerID = null
it's ok, but, and I try to update this model, changing to:
OwnerID = 1
and
ProtectorID = null
I get the error in title, someone can help me with that ?
I don't agree with the above answer. I am not sure whether it solved your problem permanently because the issue is not related with the null value assignment. The actual reason is related with DBContext. When we go for any SaveChanges the context needs to be dispatched properly in order to proceed with the next SaveChanges to insert another record into DB on the same item with a different foreign key. You just need to add the below line after your "context.SaveChanges()"
context.Entry(your object).State = System.Data.Entity.EntityState.Detached;
This will solve the conflicts. Multiple insertion with same context results in conflicts.
Apologize if my comments criticised your answer in any manner.
I found the problem, after read this msdn post, I was thinking and found out what was happening, in my repository when I will update my entity, I was forgeting to set null all the related entities.
Old code:
var oldAnimal = context.Animals.Find(animal.Id);
if (oldAnimal != null)
{
oldAnimal.AnimalBreed = context.AnimalBreeds.Find(animal.AnimalBreed.Id);
oldAnimal.AnimalSpecie = context.AnimalSpecies.Find(animal.AnimalSpecie.Id);
oldAnimal.OwnerId = animal.OwnerId;
oldAnimal.ProtectorId = animal.ProtectorId;
oldAnimal.Castrated = animal.Castrated;
oldAnimal.DateBirth = animal.DateBirth;
oldAnimal.Gender = animal.Gender;
oldAnimal.Name = animal.Name;
oldAnimal.UpdateDate = DateTime.Now;
oldAnimal.Vaccinated = animal.Vaccinated;
oldAnimal.Weight = animal.Weight;
}
context.SaveChanges();
return animal;
new code:
var oldAnimal = context.Animals.Find(animal.Id);
if (oldAnimal != null)
{
oldAnimal.AnimalBreed = context.AnimalBreeds.Find(animal.AnimalBreed.Id);
oldAnimal.AnimalSpecie = context.AnimalSpecies.Find(animal.AnimalSpecie.Id);
oldAnimal.Owner = null;
oldAnimal.Protector = null;
oldAnimal.OwnerId = animal.OwnerId;
oldAnimal.ProtectorId = animal.ProtectorId;
oldAnimal.Castrated = animal.Castrated;
oldAnimal.DateBirth = animal.DateBirth;
oldAnimal.Gender = animal.Gender;
oldAnimal.Name = animal.Name;
oldAnimal.UpdateDate = DateTime.Now;
oldAnimal.Vaccinated = animal.Vaccinated;
oldAnimal.Weight = animal.Weight;
}
context.SaveChanges();
return animal;
I have been searching for days now trying to figure this one out. It saves my records correctly but throws the following error:
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: Unable to set field/property Actors on entity type BOR.DataModel.StagComplaint. See InnerException for details.
I am using Code First and EF 5 in a C# Web Forms solution with a supporting WCF Service. Here are my POCO classes:
public partial class StagComplaint : ComplaintBase {
public IList<StagParcel> Parcels { get; set; }
public IList<StagActor> Actors { get; set; }
public IList<StagRectification> Rectifications { get; set; }
public ComplaintType ComplaintType { get; set; }
public int ComplaintTypeID { get; set; }
public StagComplaint() {
this.Parcels = new List<StagParcel>();
this.Actors = new List<StagActor>();
this.Rectifications = new List<StagRectification>();
}
}
public class ComplaintBase : BORBase {
public string Number { get; set; }
public int ParentID { get; set; }
public int TaxYear { get; set; }
public string Category { get; set; }
public double BuildingValue { get; set; }
public double LandValue { get; set; }
public double OwnerOpinion { get; set; }
public string Notes { get; set; }
}
public class BORBase {
[Required]
public DateTime CreationDate { get; set; }
public int ID { get; set; }
[MaxLength(25)]
[Required]
public string UserIdentification { get; set; }
}
public partial class StagParcel : ParcelBase {
public virtual StagActor Owner { get; set; }
[ForeignKey("Owner")]
public int OwnerID { get; set; }
public StagAddress Address { get; set; }
[IgnoreDataMember]
public virtual StagComplaint Complaint { get; set; }
public int ComplaintID { get; set; }
public StagParcel() {
this.Address = new StagAddress();
}
}
public class ParcelBase : BORBase {
public string Number { get; set; }
public double BuildingValue { get; set; }
public double LandValue { get; set; }
public double OwnerOpinion { get; set; }
public string LandUseCode { get; set; }
public string NeighborhoodCode { get; set; }
public string TaxDistrict { get; set; }
public string SchoolDistrict { get; set; }
public int SchoolBoardID { get; set; }
}
public partial class StagActor : ActorBase {
public StagAddress Address { get; set; }
public virtual IList<StagEmail> Emails { get; set; }
public virtual IList<StagPhone> Phones { get; set; }
[IgnoreDataMember]
public virtual StagComplaint Complaint { get; set; }
public int ComplaintID { get; set; }
public virtual Role Role { get; set; }
public int RoleID { get; set; }
public StagActor() {
this.Emails = new List<StagEmail>();
this.Phones = new List<StagPhone>();
this.Address = new StagAddress();
}
}
public class ActorBase : BORBase {
public string Name { get; set; }
}
public class StagRectification : BORBase {
public bool Active { get; set; }
public string Notes { get; set; }
public virtual RectificationType RectificationType { get; set; }
public int RectificationTypeID { get; set; }
[IgnoreDataMember]
public virtual StagComplaint Complaint { get; set; }
public int ComplaintID { get; set; }
}
This is the client side code I am using to create the Complaint:
public int AddParcelsToStagingComplaint(List<string> parcelIDs, string userID) {
StagComplaint comp = new StagComplaint();
int Result = 0;
using (BORServiceClient db = new BORServiceClient()) {
comp = new StagComplaint() {
BuildingValue = 111222,
Category = "*",
LandValue = 222333,
Number = "*",
TaxYear = DateTime.Now.Year,
ComplaintTypeID = 1,
UserIdentification = userID,
CreationDate = DateTime.Now,
};
StagAddress ca = new StagAddress() { Line1 = "670 Harvard Blvd", City = "Cleveland", State = "OH", ZipCode = "44113", };
List<StagPhone> ps = new List<StagPhone>();
ps.Add(new StagPhone() { Number = "5556664646", Type = PhoneTypes.Home, UserIdentification = userID, CreationDate = DateTime.Now, });
comp.Actors.Add(
new StagActor() {
Name = "Joe Schmoe",
Address = ca,
Phones = ps,
RoleID = 1,
UserIdentification = userID,
CreationDate = DateTime.Now,
}
);
StagAddress aa = new StagAddress() {
City = wp.Address.City,
Line1 = wp.Address.Line1,
Line2 = wp.Address.Line2,
State = wp.Address.State,
ZipCode = wp.Address.ZipCode,
};
ps = new List<StagPhone>();
ps.Add(new StagPhone() { Number = "4448887878", Type = PhoneTypes.Work, UserIdentification = userID, CreationDate = DateTime.Now, });
StagParcel p = new StagParcel() {
Address = new StagAddress() { Line1 = "4 Oxford Drive", City = "Hudson", State = "OH", ZipCode = "44236" },
BuildingValue = wp.BuildingValue,
LandUseCode = wp.LandUseCode,
LandValue = wp.LandValue,
NeighborhoodCode = wp.NeighborhoodCode,
Number = wp.Number,
Owner = new StagActor() { Name = "Owner Person", Address = aa, RoleID = 2, Phones = ps, UserIdentification = userID, CreationDate = DateTime.Now, },
OwnerOpinion = wp.OwnerOpinion,
SchoolBoardID = wp.SchoolBoardID,
SchoolDistrict = wp.SchoolDistrict,
TaxDistrict = wp.TaxDistrict,
UserIdentification = userID,
CreationDate = DateTime.Now,
};
comp.Parcels.Add(p);
ServiceResponse<int> saved = db.AddComplaint((ComplaintBase)comp, Contexts.Staging, userID);
if (saved.WasSuccessful)
Result = saved.Result;
} // using the database
return Result;
} // AddParcelsToStagingComplaint - Method
Here is the WCF method that gets called:
using (StagComplaintRepo cr = new StagComplaintRepo()) {
cr.Add((StagComplaint)complaint, userID);
if (cr.Save()) {
Result.Result = complaint.ID;
Result.WasSuccessful = true;
} else {
Result.AddException(string.Format("Unable to create a new Complaint in the {0} context.", context));
} // if the save was successful
} // using the Complaint Repository
And here is the BaseRepository that has the Save and Add methods:
public abstract class BaseRepository<T> : IDisposable, IRepository<T> where T : class {
public virtual bool Save(bool detectChanges = false) {
if (detectChanges == true)
this.Entities.ChangeTracker.DetectChanges();
return (this.Entities.SaveChanges() > 0);
}
public virtual void Add(T entity, string userID) {
this.Entities.Set<T>().Add(entity);
}
...
}
It fails on the above this.Entities.SaveChanges() call with the error mentioned at the top of this post. There is no extra inner exception. If I only fill in the Complaint properties that are required and are part of that object, it works. But once I add a Parcel with an Actor it fails.
I assume it is something simple, perhaps a switch needs to be turned on or off. But similar errors all seem to reference AcceptChanges and that is not the issue here. At least based on the error message. Any help would be appreciated.
EDIT
Here is the full stack trace:
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at BOR.WebService.Repositories.BaseRepository`1.Save(Boolean detectChanges) in d:\DevProjects\BOR\WebService\Main\Source\WebServiceSolution\WcfServiceProject\Repositories\BaseRepository.cs:line 22
at BOR.WebService.BORService.AddComplaint(ComplaintBase complaint, Contexts context, String userID) in d:\DevProjects\BOR\WebService\Main\Source\WebServiceSolution\WcfServiceProject\BORService.svc.cs:line 65
Line 22 is:
return (this.Entities.SaveChanges() > 0);
Line 65 is:
if (cr.Save()) {
I'm using EF4.1 code first to create a simple database app with SQL CE 4 backend. I have a Product class and a CallItem class defined as so:
class CallItem
{
public int id { get; set; }
public float discount { get; set; }
public virtual Product Product { get; set; }
}
class Product
{
public int id { get; set; }
public decimal BaseCost { get; set; }
public int UnitSize { get; set; }
public bool isWasteOil { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Ingredients { get; set; }
}
edit - When I am creating a collection of CallItems using a LINQ query, I cannot access the attributes of the Product attached to each CallItem, eg
var callItems = from ci in context.CallItems select ci;
foreach(CallItem callItem in callItems)
{
RunSheet nrs = new RunSheet();
nrs.prodCode = callitem.Product.Code;
}
Interrogating the database shows that Productid in CallItems is being populated. However, the following line generates a NullReferenceException during run time:
nrs.prodCode = callitem.Product.Code;
Because callitem.Product is evaluating to null. Is this something to do with lazy loading and if so how can I resolve the issue?
RunSheet is another class, nrs is an instance whose attribute 'prodCode' I want to populate with the CallItem's Product's code.
Thanks!
From that code what you've showed it should work. Have you tried explicit loading?
var callItems = from ci in context.CallItems.Include(c => c.Product) select ci;
foreach(CallItem callItem in callItems)
{
RunSheet nrs = new RunSheet();
nrs.prodCode = callitem.Product.Code;
}
public class CallItem
{
public int Id { get; set; }
public float Discount { get; set; }
public virtual Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public decimal BaseCost { get; set; }
public int UnitSize { get; set; }
public bool IsWasteOil { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Ingredients { get; set; }
}
using (var context = new StackOverFlowContext())
{
var p = new Product
{
Id = 1,
BaseCost = 200,
Code = "Hola",
Description = "Soe description",
Ingredients = "Some ingredients",
IsWasteOil = true,
Name = "My Product",
UnitSize = 10
};
var item = new CallItem
{
Id = 101,
Discount = 10,
Product = p
};
context.CallItems.Add(item);
context.SaveChanges();
var result = from temp in context.CallItems
select temp;
Console.WriteLine("CallItem Id"+result.First().Id);
Console.WriteLine("ProductId"+result.First().Product.Id);
}
I wrote the above code with the following output
CallItemId 1
ProductId 1
The sql Profiler showed this
SELECT TOP (1)
[c].[Id] AS [Id],
[c].[Discount] AS [Discount],
[c].[Product_Id] AS [Product_Id]
FROM [dbo].[CallItems] AS [c]
It was too long for a comment ,so i put it here .