I'm and MVC1 programmer, new to the MVC2.
The data will not persist to the database in an edit scenario. Create works fine.
Controller:
//
// POST: /Attendee/Edit/5
[Authorize(Roles = "Admin")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Attendee attendee)
{
if (ModelState.IsValid)
{
UpdateModel(attendee, "Attendee");
repository.Save();
return RedirectToAction("Details", attendee);
}
else
{
return View(attendee);
}
}
Model:
[MetadataType(typeof(Attendee_Validation))]
public partial class Attendee
{
}
public class Attendee_Validation
{
[HiddenInput(DisplayValue = false)]
public int attendee_id { get; set; }
[HiddenInput(DisplayValue = false)]
public int attendee_pin { get; set; }
[Required(ErrorMessage = "* required")]
[StringLength(50, ErrorMessage = "* Must be under 50 characters")]
public string attendee_fname { get; set; }
[StringLength(50, ErrorMessage = "* Must be under 50 characters")]
public string attendee_mname { get; set; }
}
I tried to add [Bind(Exclude="attendee_id")] above the Class declaration, but then the value of the attendee_id attribute is set to '0'.
View (Strongly-Typed):
<% using (Html.BeginForm()) {%>
...
<%=Html.Hidden("attendee_id", Model.attendee_id) %>
...
<%=Html.SubmitButton("btnSubmit", "Save") %>
<% } %>
Basically, the repository.Save(); function seems to do nothing. I imagine it has something to do with a primary key constraint violation. But I'm not getting any errors from SQL Server. The application appears to runs fine, but the data is never persisted to the Database.
Got It! Here's the solution:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection form)
{
Attendee attendee = repository.GetAttendee(id);
try
{
UpdateModel(attendee, form);
repository.Save();
return RedirectToAction("Details", attendee);
}
catch
{
return View(attendee);
}
}
The UpdateModel() ValueProvider was the problem.
Related
I'm trying to get my head around EF7 by writing a simple master-detail relationship to a sqlite database. Saving works fine, reading however gives me headaches:
Here are my Entities:
public class Message
{
public int MessageId { get; set; }
public string Name { get; set; }
public List<MessagePart> MessageParts { get; set; }
}
public class MessagePart
{
public int MessagePartId { get; set; }
public string Text { get; set; }
public int MessageId { get; set; }
public Message Message { get; set; }
}
createMessage() does what it is supposed to:
static void createMessages()
{
using (var db = new TestContext())
{
var m1 = new Message
{
Name = "train_arrives_in_x_minutes",
MessageParts = new List<MessagePart>()
{
new MessagePart {
Text = "Train arrives in 5 minutes"
},
new MessagePart {
Text = "Zug faehrt in 5 Minuten ein",
}
}
};
var m2 = new Message
{
Name = "train_out_of_service",
MessageParts = new List<MessagePart>()
{
new MessagePart {
Text = "train is out of service"
},
new MessagePart {
Text = "Kein Service auf dieser Strecke",
}
}
};
db.Messages.Add(m1);
db.Messages.Add(m2);
var count = db.SaveChanges();
Console.WriteLine("{0} records saved to database", count);
}
}
Reading from an existing database reads the master record fine, but the detail recordset pointer stays null.
static void readMessages()
{
using (var db = new TestContext())
{
foreach (Message m in db.Messages)
{
Console.WriteLine(m.Name);
// exception here: m.MessageParts is always null
foreach(MessagePart mp in m.MessageParts)
{
Console.WriteLine("mp.Text={0}", mp.Text);
}
}
}
}
Is there anything I can do to force those messagesparts to load? I've worked with other (Python) ORMs before and never had this problem before. Is this a problem with Lazy Loading? I tried to fetch those childrecords using a LINQ statement, that didn't help either. Everything looks good in the database though.
If you want to enable LazyLoading you need to enable LazyLoading (should be enabled by default) and make your property virtual:
public TestContext()
: base(Name = "ConntextionName")
{
this.Configuration.ProxyCreationEnabled = true;
this.Configuration.LazyLoadingEnabled = true;
}
And your models shuodl look like:
public class Message
{
public int MessageId { get; set; }
public string Name { get; set; }
public virtual ICollection<MessagePart> MessageParts { get; set; }
}
public class MessagePart
{
public int MessagePartId { get; set; }
public string Text { get; set; }
public int MessageId { get; set; }
public virtual Message Message { get; set; }
}
If you do not want to use LazyLoading, you can load related entities using eager loading:
using System.Data.Entity;
using (var db = new TestContext())
{
int messageId = ....;
Message message = db.Messages
.Where(m => m.MessageId == messageId)
.Include(m => m.MessageParts) // Eagerly load message parts
.FirstOrDefault();
// Your message and all related message parts are now loaded and ready.
}
For more information, please have a look at this site.
Am Trying to Print out a student Identity Card using crystal report but all what i could get was this error popping up The data source object is invalid.
Guys please help me to check on this code if am making any mistake...
this is the model
public class CardModel
{
// Properties
public string Department { get; set; }
public string ExpiryDate { get; set; }
public string FirstName { get; set; }
public Sex Gender { get; set; }
public Guid Id { get; set; }
public string MiddleName { get; set; }
public string RegistrationNo { get; set; }
public byte[] SecuritySign { get; set; }
public byte[] StudentPhoto { get; set; }
public string Surname { get; set; }
}
public static class CardModelExtention
{
public static CardModel ToCardModel(this Student identity)
{
return new CardModel
{
Id = identity.Id,
FirstName = identity.FirstName,
MiddleName = identity.MiddleName,
Surname = identity.Surname,
StudentPhoto = identity.Photo.RawPhoto,
SecuritySign = identity.SecuritySignature.RawSignature,
Gender = identity.Sex,
ExpiryDate = identity.ExpiryDate,
Department = identity.Department.DepartmentName,
RegistrationNo = identity.RegistrationNo
};
}
}
and here is the service am using to pull the information from database
public class StudentService : IStudentService
{
ERMUoW _ow;
public StudentService()
{
_ow = new ERMUoW();
}
public CardModel GetStudentById(Guid id)
{
CardModel obj3 = new CardModel();
Student student = _ow.Students.GetAllIncluding(new Expression<Func<Student, object>>[] { st => st.Photo, st => st.Signature, st => st.SecuritySignature, st => st.Department }).Where(x => x.Id == id).SingleOrDefault();
var cardInfo = student.ToCardModel();
return cardInfo;
}
}
public interface IStudentService
{
CardModel GetStudentById(Guid id);
}
This is it and everything around here is working fine and am getting the data very well but when I send it to the method in my contrller that generate the identity card I get that error message
this is the code that generate the card using crytal report
public ActionResult PrintCard(Guid id)
{
var student = _studentCardService.GetStudentById(id);
ReportDocument read = new ReportDocument();
read.Load(Server.MapPath("~/Reports/rpt_StudentCard.rpt"));
read.SetDataSource(student);
Response.Buffer = false;
Response.ClearContent();
Response.ClearHeaders();
try
{
Stream stream = read.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat);
stream.Seek(0, SeekOrigin.Begin);
return File(stream, "application/pdf", "StudentIdentityCard.pdf");
}
catch (Exception ex)
{
throw ex;
}
}
I will really Appreciate your help thank you...
The data source have to be a List of elements... not a single element.
I have taken primary key (Userid) as foreign key in 'Book' table.It is one to many relationship i.e a single user is able to upload multiple books. BookID is auto incremented.Whenever user clicks 'Upload Books' button, I need to store the name of the book and its path in database with userid as foreign key from User table and BookID to be auto increment.
I am having the following exception at db.SaveChanges():
An exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll but was not handled in user code Additional information: An error occurred while updating the entries. See the inner exception for details.
Inner exception is:
Cannot insert the value NULL into column 'Id', table 'BooksModule.dbo.Book'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated.
Below is my code:
Here is my model class: Book.cs
public partial class Book
{
public string Id { get; set; }
public int BookID { get; set; }
public string BookName { get; set; }
public string BookPath { get; set; }
public virtual User User { get; set; }
}
Here is my model class of Users: User.cs
public partial class User
{
public User()
{
this.Books = new HashSet<Book>();
}
public string Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
here is a method:
[HttpGet]
public ActionResult FileUpload(string id)
{
return View();
}
[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase file,Book bk)
{
var filepath = "";
var fname = "";
if (ModelState.IsValid)
{
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName);
filepath = path;
fname = fileName;
file.SaveAs(path);
}
bk.BookName = fname;
bk.BookPath = filepath;
db.Books.Add(bk);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
else
{
return View();
}
}
Here is a view:
<div class="sidebar">
#using (Html.BeginForm("FileUpload", "UploadBooks", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary();
<fieldset>
<legend>Upload a file</legend>
<div class="editor-field">
#Html.TextBox("file", "", new { type = "file" })
</div>
<input type="submit" id="btnSubmit" class="btn btn-default2" value="Upload" />
</fieldset>
}
</div>
Entity Framework will pick Id as the primary key, and as yours is a string you need to supply it yourself (which you are not doing.)
It's preferable to have an int as your primary key so EF can make it an identity column in the database.
A few other observations too:
The FileUpload post action is taking a Book object that isn't used other than as a variable in your action, instead just declare a new Book object inside the method.
The fileName and path variables are not needed.
Do you need both Id and BookId? I would remove BookId.
I would suggest your Book.cs looks like this:
public partial class Book
{
public int Id { get; set; }
public string BookName { get; set; }
public string BookPath { get; set; }
public virtual User User { get; set; }
}
And your post action:
[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (file.ContentLength > 0)
{
fname = Path.GetFileName(file.FileName);
filepath = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName);
file.SaveAs(fname);
Book bk = new Book
{
BookName = fname,
BookPath = filepath
};
db.Books.Add(bk);
db.SaveChanges();
}
return RedirectToAction("Index", "Home");
}
else
{
return View();
}
}
Update
From the comments you made above, there is an additional change you need to make to the Book class. First you need to tell Entity Framework that your Id property if the key using the [Key] attribute. Secondly for the Id column you should do one of these:
Remove Id (as Entity Framework will create one for you)
Rename Id to UserId to allow Entity Framework to automatically link it to the User property.
Add an attribute to the User property to tell it the name of the column to use, for example [ForeignKey("Id")]
I would suggest either 1 or 2 so it is more obvious what the column is when looking at the database:
public partial class Book
{
[Key]
public int BookId { get; set; }
public string BookName { get; set; }
public string BookPath { get; set; }
public string UserId { get; set; }
public virtual User User { get; set; }
}
I have a model, 'Person', that references another model, 'Salutation'.
public class Person
{
public int Id { get; set; }
public Boolean Active { get; set; }
// fk
public virtual Salutation Salutation { get; set; }
public virtual PersonName Name { get; set; }
}
public class Salutation
{
public int Id { get; set; }
public string Name { get; set; }
}
when I try and UPDATE the 'Person' with a different 'Salutation' it doesn't update. Though if I change the actual data within the 'salutation' it does update. This is the UPDATE code in my controller, the data coming in to the function is correct but just doesn't save in the DB.
For example if the current Salutation has ID: 1 and Name: "Mr" then if I try and pass in a different existing record with ID: 2 and Name: "Mrs" it doesn't change. But if I pass in ID:2 and Name:"RandomAlienString" then it DOES change to the new model and updates the Salutation.
In the controller - UPDATE Method:
public void PutPerson(int id, [FromBody]Person person)
{
if (!ModelState.IsValid)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
if (id != person.Id)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
db.Entry(person).State = EntityState.Modified;
db.Entry(person.Name).State = EntityState.Modified;
db.Entry(person.Salutation).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!PersonExists(id))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
else
{
throw;
}
}
}
Any help would be most appreciated.
I am receiving this error:
The ViewData item that has the key 'DepartmentId' is of type 'System.Int32' but must be of type 'IEnumerable'.
with the following set up. I am not sure how to resolve it. The error is happening in the Model View code. This line: public void MapTo(Person domainModel). I am using AutoMapper to map ViewModel back to DomainModel (reversing the initial mapping of DomainModel to ViewModel).
Domain model (using LINQ to SQL, so this is a partial class):
public partial class Person { }
// Validation rules
public class Person_Validation
{
[HiddenInput(DisplayValue = false)]
[ScaffoldColumn(false)]
public object PersonId { get; set; }
[HiddenInput(DisplayValue = false)]
[ScaffoldColumn(false)]
public object DepartmentId { get; set; }
[DisplayName("Employee Name")]
[Required(ErrorMessage = "Employee Name is required")]
[StringLength(50, ErrorMessage = "Employee Name cannot be more than 50 characters")]
public object Name { get; set; }
[HiddenInput(DisplayValue = false)]
public object Active { get; set; }
[HiddenInput(DisplayValue = false)]
public object DateAdded { get; set; }
[HiddenInput(DisplayValue = false)]
public object DateDeleted { get; set; }
public object Department { get; set; }
}
This is my Model View:
public class PersonViewModel
{
public object PersonId { get; set; }
public object DepartmentId { get; set; }
public object Name { get; set; }
public object Active { get; set; }
public object DateAdded { get; set; }
public object DateDeleted { get; set; }
public object DepartmentName { get; set; }
//helper method
public void MapTo(Person domainModel)
{
Mapper.Map(this, domainModel);
}
}
Controller Class Code:
[HttpPost]
public ActionResult Edit(PersonViewModel viewModel)
{
var domainModel = new Person();
try
{
viewModel.MapTo(domainModel);
UpdateModel(domainModel);
_personRepository.Save();
return RedirectToAction("Index", "Person");
}
catch
{
return View(viewModel);
}
}
And my View HTML code:
<div class="editor-field">
<%: Html.DropDownList("DepartmentId", (IEnumerable<SelectListItem>)ViewData["DepartmentList"])%>
<%: Html.ValidationMessageFor(model => model.DepartmentId) %>
</div>
you're not really following the best practices of developing a mvc application.
About the error:
the Html.DropDownList looks for Data of type IEnumerable<SelectListItem> in the model but it finds an int instead (DepartmentId)
your ViewModel should not have the MapTo method, it breaks the single responsibility principle
in your action method you don't do any server side validation like:
if(!ModelState.IsValid)
{
//rebuild the viewmodel and return the view
}
catching everything in the action is also not necessary (and bad)
you do this in Global.asax Application_Error instead
attributes like HiddenInput, ScaffoldColumn, Validation and anything else UI related should be on you ViewModel not in your domain model
for a good sample of using viewmodels & validation & mapping between entity <-> viewmodel I recommend you to look at the Samples solution from here
I did this sample and it's main purpose is to demonstrate the usage of ValueInjecter (mapping technology)