MVC CodeFirst one to many relationship creates new data - asp.net-mvc

I'm using CodeFirst with MVC 3 and have these two classes:
public class Person
{
public int PersonId { get; set; }
[Email]
[Required]
public string Email { get; set; }
public string Passwort { get; set; }
public virtual City City { get; set; }
}
public class City
{
public int CityId { get; set; }
[Required]
public string Name { get; set; }
public virtual IEnumerable<Person> Persons { get; set; }
}
When adding a new person I want to reference a city to this person. Therefore i'm using a SelectList with all cities in my view. The CityId and the object is transferred correctly to the Post-method, but when saving the changes to the database I will have a new object in the city-table (with same name, but new Id).
I suggest there's something wrong with the relations in my models. Maybe somebody can help me.

If you give your Person model an explicit CityId property, then you won't need to retrieve the City object from your repository, you can just assign the CityId value directly to the Person object and save it. For really straightforward views, you don't need to use a viewmodel either, you could receive a Person instance into the POST action method and CityId would already be assigned, assuming the html field in the View has the same name.
This should fix your problem, because you will then know you are explicitly using a CityId that already exists.
Your database will already contain a Person.City_CityId field anyway so you're not creating anything new, just giving yourself more control over the situation. Sometimes you may need to use a [ForeignKey] attribute in the model to connect the Person.CityId property with the virtual property, but using the standard naming convention this shouldn't be necessary.

Related

mvc 5: Optional model property

I have a model called Project
public class Project
{
[Key]
public int ID { set; get; }
public string Title { set; get; }
public string Image { set; get; }
public double? gained { set; get; }
}
I use this model with two stored procedures one returns all the properties and the other without the property gained. And I got this error
The data reader is incompatible with the specified 'Test.Models.Project'. A member of the type, 'Gained', does not have a corresponding column in the data reader with the same name.
I don't want to write separate models for each stored procedure.
How to solve that please ?
The datareader is kind of dumb in the sense that it will only match what was sent back to it. If a column is missing, it fails, as you can see.
The easiest way to solve this would be to update your second SELECT statement in your stored procedure to pass back a column named gained.
SELECT ID, Title, Image, NULL as gained FROM table
Here, we are passing back no data (NULL) as the gained column. This should make the data reader happy, keep you from needing multiple models and not send back any extra data.
The other possibility would be to use inheritance in your models. Have a base model that does not include gained, and have a second model that inherits from the base model that does include gained.
public class ProjectBase
{
[Key]
public int ID { set; get; }
public string Title { set; get; }
public string Image { set; get; }
}
public class ProjectGained : ProjectBase{
public double? gained { set; get; }
}

How to Retrieve Many to Many Relationship based records using Code First Approach with LINQ To SQL?

I am using Code First LINQ To SQL Approach and trying to retrieve records from two tables by using junction table. I have structured data model in very simple way and include Foriegn Keys in the child classes as properties like "CourseId"( Course Id was primary key in Program Class).
So My many to Many Relationship is somehow structured in classes like :
Program (ProgramId,ProgramName)
Course (CourseId,CourseName,CreditPts)
ProgramCourse ( ProgramId, CourseId ) <-- Junction Table
Here's my data model.
public class ProgramCourse
{
[Required]
public int ProgramId { get; set; }
[Required]
public int CourseId { get; set; }
}
public class Program
{
public int ProgramId { get; set; }
[Required]
[Display(Name = "Program Name")]
public string ProgramName { get; set; }
public int InstituteId { get; set; }
// public virtual Institute Institute { get; set; }
}
class Course
{
public int CourseID { get; set; }
[Required]
[Display(Name = "Course Name")]
public string CourseName { get; set; }
[Required]
[Display(Name = "Credit Points")]
public string CreditPoints { set; get; }
}
My Problem
I am not able to retrieve the data from both tables ( i.e Program and Course ). I want to write method "GetAllCoursesForProgram" which displays the courses for specific program.
Because I can't navigate towards "ProgramCourse" class from Course class or from Program class. Because foreign keys included as simple int's and there are no navigational properties included in the whole data model.
If I include navigational properties , I would have problems to manipulate these object and making AJAX based calls to server from jTable.
Any body can suggest me the solution ? How to form a query to retrieve data from multiple tables ? I would be extremely obliged. Many Thanks in advance.
Regards
Usman
If you are using Code First for a many to many relationship, DO NOT CREATE THE JUNCTION TABLE YOURSELF! Code First will do it for you, and you will get Navigation properties, solving your problem.
Instead of having problems manipulating those objects, it will solve your problem.....
Start by changing your model, generate the database, look how Code First created the 3rd "junction" table, then see how to work with that in code. Let me know if it does not help.

Foreign key constraint, EF with collection of childobjects

I'm trying to update a model, but get the error "The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted."
From what I understand from The relationship could not be changed because one or more of the foreign-key properties is non-nullable the problem might be with how Entity Framework handles my virtual ICollection
However I'm not really sure how to implement the solution when using scaffolded repository pattern. Do I have to edit the Save()-method ParentObjectRepository-class?
Actually I really think that there must be some way to make EF understand this. I can't see how the EF-team was thinking "Probably noone is using a collection of objects with a foreign key constraint, lets not support that".
Update
Added code
[HttpPost]
public ActionResult Edit(int id, FormCollection formCollection)
{
var eventRepository = new MagnetEventRepository();
var original = eventRepository.Find(id);
UpdateModel(original);
eventRepository.Save();
return RedirectToAction("Details", "Home", new { slug = original.Slug });
}
public void Save()
{
context.SaveChanges();
}
More code:
public class MagnetEvent
{
public virtual int Id { get; set; }
[Required]
public virtual string Name { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd HH:mm}")]
[DataType(DataType.DateTime)]
public virtual DateTime? StartDate { get; set; }
public virtual string Description { get; set; }
[StringLength(100)]
public virtual string Slug { get; set; }
public virtual int MaximumCapacity { get; set; }
[DataType(DataType.Currency)]
public virtual int TicketPrice { get; set; }
public virtual int LocationId { get; set; }
public virtual Location Location { get; set; }
public virtual Collection<Ticket> Tickets { get; set; }
public virtual Collection<AttendeeInformationField> CaptureAttendeeInformationFields { get; set; }
public virtual int CustomerId { get; set; }
[Required]
public virtual CUSTOMER Customer { get; set; }
}
The Save()-method is from MagnetEventRepository, which is scaffolded from the above class.
Another update
I successfully removed the error by changing MagnetEventId in AttendeeInformationField to nullable int. When examining the database I can see exactly what's wrong.
Let's say I have a single AttendeeInformationField with the value "E-mail". When I edit my MagnetEvent, the AttendeeInformationField updates the MagnetEventId to null and then adds a new post with the correct MagnetEventId and Value.
I'd very much prefer if the posts in AttendeeInformationField were updated instead.
can you add the code for your event object. The one you call original.
It might be so that the UpdateModel change some info on the associated objects and that's not good if so. Not sure about this though I can't see all the code.
I prefer to not uder UptadeModel and instead use a inputmodel or your MVC model as the inparameter and manually map the chages to the loaded original object.
Antoher problem is that I can't see if
eventRepository.Save();
really do an SaveShages? does it? I can se some context code in another method Save?
As the exception say it seams like your associated collections or other associated objects cant find a valid ID value.
Are you Eager-loading the associated objects? like Customer?
One thing of note is that you shouldn't have the [Required] on Customer as its inferred from the fact that your FK isn't nullable. Required should only be used on a navigation property if you do not have the FK in the model.
To try to diagnose the issue, can you load the object and look at it in a debugger, you should expect that both locationId and CustomerId have non-zero values.
I found a solution to my problem. It seems to be a bug (?) in ASP.NET MVC when it comes to UpdateModel and a model containing an ICollection.
The solution is to override the default behaviour, as described in this blog post: http://www.codetuning.net/blog/post/Binding-Model-Graphs-with-ASPNETMVC.aspx
Update
I found a solution! The above only worked when updating existing items in the collection. To solve this, I have to manually check and add new AttendeeInformationFields. Like this:
[HttpPost]
public ActionResult Edit(int id, MagnetEvent magnetEvent)
{
var eventRepository = new MagnetEventRepository();
var original = eventRepository.Find(id);
UpdateModel(original);
foreach (var attendeeInformationField in magnetEvent.CaptureAttendeeInformationFields)
{
var attendeeInformationFieldId = attendeeInformationField.Id;
if (original.CaptureAttendeeInformationFields.AsQueryable().Where(ai => ai.Id == attendeeInformationFieldId).Count() == 0)
{
original.CaptureAttendeeInformationFields.Add(attendeeInformationField);
}
}
eventRepository.Save();
}
Together with the modified DefaultModelBinder, this actually works with both editing and adding. For now I haven't tried deleting.
Still, I hope there is a simpler way to do this. Seems like a lot of coding to do a very basic task.

Partial update of entity objects with EF 4

I'm implementing DAL and BL layers of application.
It is hosted as WCF service, and EF 4 is used as ORM.
We have role based security layer and business rule that only part of object can be updated by some particular role.
Here is simplified example of problem:
We have such DTOs:
MainType
{
public Guid ID { get; set; }
public String DoctorField1 { get; set; }
public String DoctorField2 { get; set; }
public String NurseField1 { get; set; }
public String NurseField2 { get; set; }
public DateTime Created {get; set;}
public DateTime Updated {get; set;}
public Guid LastUpdateBy {get; set;}
public List<DetailsType> Details { get; set; }
}
DetailsType
{
public Guid MainTypeID { get; set; }
public Guid SomeIdentityID { get; set; }
public String DoctorDetail { get; set; }
public String NurseDetail { get; set; }
public DateTime Created {get; set;}
public DateTime Updated {get; set;}
public Guid LastUpdateBy {get; set;}
}
This entities are mapped to corresponding DB tables with the same fields.
ID field of MainType is Primary Key;
MainTypeID of DetailsType is Foreign Key to MainType table.
SomeIdentityID of DetailsType is FK to some other entity that is not important for this sample.
MainTypeID SomeIdentityID is complex primary key for DetailsType table.
I have graph of such objects (1 main and list details), and determined role of user who performs update operation.
My task is:
IF current user has role Doctor - update Doctor fields in Main object and all Details objects, insert new details objects.
IF current user has role Nurse - update Nurse fields in Main object and all Details objects.
save current date to Updated field
save current user id to LastUpdateBy field
do not modify Created field and any other field that are not updated by this role.
So for example if I have user with role Doctor I should do following:
Update DoctorField1, DoctorField2, Updated, LastUpdateBy in MainObject
Update DoctorDetail, Updated, LastUpdateBy in every details object
DO NOT modify any other fields.
Currently we have implementation that reads full graph for MainObject, makes necessary modifications and saves in back to DB.
This solution works too slow and I need to find a way to improve it.
Currently I know clearly how to do that by RAW SQL, but this will be my solution in case nothing else will help.
How can I make Entity Framework to update only needed fields and ignore another.
I have some successful results with ApplyOriginalValues and ApplyCurrentValues methods for String fields.
Idea was to assign some fictive value to property in both objects, for example string "##$%&##$%&##$%&##$%&##$%&", and EF then treats them as not modified properties during saving changes.
However this trick does not work with Boolean, Int32 and Decimal values.
I should use some simple approach to all objects.
I will appreciate any ideas and thoughts about this problem.
If you have such specific requirement you should start by modifying your WCF service to not accept fields which user cannot modify. That leads to two simple DTOs:
public class MainTypeUpdateDto
{
public Guid ID { get; set; }
public String Field1 { get; set; }
public String Field2 { get; set; }
public List<DetailsTypeUpdateDto> Details { get; set; }
}
public class DetailsTypeUpdateDto
{
public Guid MainTypeID { get; set; }
public Guid SomeIdentityID { get; set; }
public String Detail { get; set; }
}
All other fields either cannot be updated or should be handled by your server side logic.
Now when you receive dtos you can map them back to real entity objects. Based on user role you will know which fields and details you must set. You have two options to force EF to save only fields you want:
First create object graph with MainType and related details. Set only Ids in these entities and attach MainType entity to context. After that set all updatable fields to current values. Do not change state of any entity.
Create object graph with MainType and all related details and set all Ids and all updatable fields. After that attachMainType` entity to the context and manually set state for each modified property (on each entity).
You can need some additional logic if user can also remove or add details.

managing lookup in MVC2 and persisting object with Nhibernate

My simplified domain model looks something like this:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class Contact : Entity
{
public virtual string Name { get; set; }
public virtual Company Company { get; set; }
}
public class Company : Entity
{
public virtual string Name { get; set; }
}
and I've defined a viewmodel:
public ContactViewModel()
{
public Guid Code { get; set; }
public int Version { get; set; }
public string Name { get; set; }
public string Company { get; set; }
public List<SelectListItem> Companies { get; set; }
}
to manage my contacts in a view.
Since I want the user to be able to choose from a list of companies I've added a list of SelectedListItem which will be rendered in my view like this:
<%=Html.ListBoxFor(m => m.Company, (List<System.Web.Mvc.SelectListItem>)Model.Companies)%>
Now, when the user submits my form I remap my viewmodel with my model before I save it.
I populate my Contact and use the id of the ContactViewModel.Company to create an object of type Company to associate with the property of the Contact class.
Since I don't want to fetch the whole company from the database I just fill the id.
When I persist my contact, though, I get an exception: "not-null property references a null or transient Domain.Contact.Company".
What is the best solution to manage lookups and persistence with MVC + Nhibernate?
Do you have any suggestions from your experience?
Unfortunately with NHibernate and lookups you can't just assign the ID property to a new instance of the Company object and then assign that Company object to the Contact.
Generally what I would do is in my repository, assuming that you can't change the Company information when saving a contact is something like this:
public Contact Save(Contact contact)
{
if(contact.Company.Id > 0)
contact.Company = Session.Load<Company>(contact.Company.Id);
Session.SaveOrUpdate(contact);
}
I generally find this allows you to encapsulate the logic of loading the Company and also allows you to keep it all wrapped up nicely in a single session.
Using Session.Load in this manner avoids hitting the database as described here
If you don't do this, what you're essentially saying to NHibernate is that you have a company object which you have assigned an ID and now want to save it with all the properties set to Null or empty string values or whatever and that is not what you want.
Alternatively you could create a Save specific Domain Object that looks like this:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class SavableContact : Entity
{
public virtual string Name { get; set; }
public virtual IdK CompanyId { get; set; }
}
Which maps directly to the Contact table in your database so that when you Save this entity you can literally just map back the CompanyId from your view model and NHibernate will only save that value back and not care at all about the company objects.
It's a case of working out what works best for you. I personally prefer the first option as the extra bit of logic helps simplify the domain model, however if you're creating and exposing a public API then the second method might make more sense.

Resources