I am fairly new to MVC 3 and I was tasked with creating a to-do list, that has got timestamps for every independent variable, so when one variable changes, the timestamp of when it was changed would appear on the text field of that variable and not change the other timestamps of the other variables i.e each variable would have an individual timestamp. I believe I can only or most likely achieve this by creating an array. Any ideas on how I can carry this out?
I dummy code would be really appreciated
Here's a sample of my model, I followed this tutorial http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/intro-to-aspnet-mvc-3
public class Checklist
{
public int ID { get; set; }
public string Title { get; set; }
public string Status { get; set; }
[Display(Name = "Start Date")]
public string Start_Date { get; set; }
[Display(Name = "Complesion Date")]
public string Complesion_Date { get; set; }
public DateTime[] Timestamp
{
get { return timestamp; }
set { timestamp = value; }
[Display(Name = "Internal Review System Reference")]
public string Internal_Review_System_Reference { get; set; }
[Display(Name = "Assignment from Original Owner")]
public bool Assignment_from_Original_Owner { get; set; }
public class listDBContext : DbContext
{
public DbSet<Checklist> List { get; set; }
}
And here's a sample of my controller code
public class SybreController : Controller
{
private listDBContext db = new listDBContext();
private Checklist check = new Checklist();
private string oldTitle { get; set; }
private string oldStatus { get; set; }
public ActionResult Edit(int id)// Called when edit button is clicked
{
Checklist checklist = db.List.Find(id);
this.oldTitle = checklist.Title;
this.oldStatus = checklist.Status;
//initAllArrays(checklist);//initialize our arrays with previous values
return View(checklist);
}
[HttpPost]
public ActionResult Edit(Checklist checklist)
{
if (ModelState.IsValid)
{
checklist.Timestamp = DateTime.Now;
if (checklist.Title != this.oldTitle)
{
checklist.stamps[0] = DateTime.Now;
}
if (checklist.Status != this.oldStatus)
{
checklist.stamps[1] = DateTime.Now;
}
else
{ checklist.stamps[1] = checklist.stamps[1]; }
db.Entry(checklist).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
Basically I need an timestamp for every individual variable in my model, so that when edited, the timestamp corresponded to when it was edited, the problem I've been facing is the timestamp variable changes across all the variables instead of only the one which was changed. I just need the program to print the former timestamp from when it was last edited, and if edited, display the current time along side the text field.
Hope you understand -__-
You can't solve your problem in this way. Asp.net MVC is stateless, it means that the instance of the controller is created per every request. It means that the checks that you have performed to set time stamps have always true value, as oldTitle and oldStatus are nulls.
Related
Is there a way in MVC to pass information from one controller to another? I have a character model that looks like this:
public class Character
{
[Key]
public string CharacterID { get; set; }
public string UserID { get; set; }
public string Name { get; set; }
public int Str { get; set; }
public int Con { get; set; }
public int Dex { get; set; }
public int Int { get; set; }
public int Wis { get; set; }
public int Cha { get; set; }
public int BaseAttack { get; set; }
}
And a separate weapon model like this:
public class Weapons
{
[Key]
public string WeaponID { get; set; }
public string UserID { get; set; }
public string CharacterID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Range { get; set; }
public int Damage { get; set; }
public int Crit { get; set; }
public int CritMultiplier { get; set; }
public string Hands { get; set; }
public string Distance { get; set; }
}
To create a weapon, you first need to create a character which assigned an ID, and I want to be able to pass that ID into the create method of my weapon controller. Is there a way to do this? Thanks
You can use TempData for this purpose. TempData stores data only between two requests. When you set the TempData the next request that is initiated can retrieve value from the TempData and it will be erased for any consequent requests.
[HttPost]
public ActionResult CreateCharacter()
{
// creates charaeters here and sets the tempdata
TempData['CharacterId'] = 50;
return RedirectToAction('CreateWeapon');
}
[HttpGet]
public ActionResult CreateWeapon()
{
var weaponModel = new WeaponModel() { CharacterId = (int)TempData['CharacterId'] };
return View(weaponModel);
}
and in your view simply have a hidden for the CharacterId, so it will be persisted if you your post fails validation or if you need to re-display the view.
#Html.HiddenFor(e => e.CharacterId);
Again this is just one approach, only if you you don't want to pass the CharacterId in the url.
You can also achive this just by passing it in the url:
[HttPost]
public ActionResult CreateCharacter()
{
// creates charaeters here and sets the tempdata
return RedirectToAction('CreateWeapon', new { characterId = 50 });
}
[HttpGet]
public ActionResult CreateWeapon(int characterId)
{
var weaponModel = new WeaponModel() { CharacterId = characterId };
return View(weaponModel);
}
I would be inclined to pass the character id to the create weapon action via routing, either as a route token that forms part of the path or via the query string. Be sure to check that the weapon can logically be associated with the character to whom the id corresponds.
You could also pass the id using TempData or Session, but considering both by default will take up memory on the web server, the simple option is to use the routing. In addition, unless you call TempData.Keep("key") after accessing TempData, the value will be removed from TempData after the first access, potentially causing issues if the user refreshes the browser window.
You could use RedirectToAction(), though as titled this will cause browser redirection.
return RedirectToAction("CreateWeapon", "Weapon", new { id = yourid });
or
#Html.ActionLink("CreateWeapon", "Create", new { id = yourid })
Edit: Your plain object property names and your action method variables need to match, to do this.
I use Asp.Net MVC, Entity Framework. I have a form it looks like below.
Here, dropdownlist is filled from a table(types). Checkboxes is filled from another table(test). Tables are like below:
public class Types
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
}
public class Tests
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
}
public class Types_Tests
{
public int Types_TestsID{ get; set; }
public int TypesID { get; set; }
public int TestsID { get; set; }
public virtual Types Types { get; set; }
public virtual Tests Tests { get; set; }
}
Types_test table is relation table between Types and Tests. When I click Kaydet button, it shuld save type and checked tests. I made this operation using ViewBag, javascript and hdnvalue.I added checked checkboz values to a hdntext. I made saving process like below:
[HttpPost]
public ActionResult Index(string drpType, string hdntesttypes)
{
var TypeList = Types.GetAll();
ViewBag.TypesList = new SelectList(TypeList, "Id", "Name");
var testypeList = testTypes.GetAll();
ViewBag.TestTypesList = new SelectList(testypeList, "Id", "TestName");
GenericRepository<TestDisabledTypes> testDisabledRepository = new GenericRepository<TestDisabledTypes>(_context);
if (!string.IsNullOrEmpty(hdntesttypes))
{
string[] disabletypesArray = hdntesttypes.Split(',');
using (TransactionScope trns = new TransactionScope())
{
for (int i = 0; i < disabletypesArray.Length; i++)
{
Test_Types types = new Test_Types ();
types.TestTypesID = Convert.ToInt32(disabletypesArray[i]);
types.TypesID = Convert.ToInt32(drpType);
testDisabledRepository.Insert(types);
}
trns.Complete();
}
}
return View();
}
It wokrs. But I search better solution for this process. Can someone give me any idea?
Thanks.
If you don't need additional attributes for your entity class, you don't need create link table.
Just define the following class, and EF will generate the link table for you automatically.
public class Type
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
public ICollection<Test> Tests { get; set; }
}
public class Test
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
public ICollection<Type> Types {get;set;}
}
Well, in EntityFramework if you want to create a many to many relation object you need to create new object of "linking" entity. Unfortunately, it is not possible to add first object, add second object and say "Guys, you are in many to many relationships. Are you happy then?" :) You need to create relation object, set appropriate fields in it (I think these are ids of two objects itself) and add it to relation collection (entity) in your model. But before doing so you need to be sure that objects with data you are linking with are already exists in database. Otherwise you'll get an error
Also it's not necessary to create manually transaction because EF does it for you automatically each time you get/save your data
I've built my Domain model layer, my repository layer, and now I'm working on my DTO layer to be used by a webApi project. I'm in the middle of implementing an Update service method, and I'm wondering about partial updates. Here's my DTO class:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
Here's my Update method in my Repository:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
My question is: What if I only need to update the Details of a person via my webAPI? Is the convention to construct an entire PersonDto and Update the entire object using SetValues, or is there any way I can specify that I only want a single field updated so that I don't have to send a ton of data over the wire (that I don't really need)?
If it is possible to do partial updates, when is it ever good to update the entire entity? Even if I have to update 5/7 properties, it requires that I send old data for 2/7 to re-write so that SetValues doesn't write nulls into my fields from my DTO.
Any help here would be awesome... totally new to this stuff and trying to learn everything right. Thank you.
I've taken similar approach to do optimization, and I've faced same issues with null values when attaching (not just null, you'll have issue with boolean as well). This is what I've come up with:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
That is extension method for DbContext. Here are the interfaces IDTO and IEntity:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
I'm using my custom EnsureUpdatedAttribute to annotate what properties should always be updated (to deal with nulls / default values not being tracked):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
And this is a sample of usage:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent() is just extension for returning 204 (NoContent).
Hope this helps.
Theres a few options you have, you can create a stored procedure to update the required parts (I wouldnt do this), or you can manually select the fileds to update on the model before saving the context changes with EF.
Heres an example how to update a specific field:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
It will depend on your scenario what you want to do, but generally speaking its fine to send your whole entity to be updated, and this is how i would approach your situation potentially changing in the future if needed.
I have a viewModel that contains the following:
public class CreateCardViewModel
{
[HiddenInput(DisplayValue = false)]
public int SetId { get; set; }
[Required]
public IList<Side> Sides { get; set; }
[Required]
public int Stage { get; set; }
[Required]
[DataType(DataType.Date)]
[HiddenInput(DisplayValue = false)]
public DateTime DateCreated { get; set; }
[Required]
public bool IsReady { get; set; }
}
The model is as follows:
public class Card
{
public virtual int CardId { get; set; }
// belongs to a Set
public virtual int SetId { get; set; }
public virtual Set Set { get; set; }
// has Sides
public virtual IList<Side> Sides { get; set; }
// is in a certain Stage
public virtual int Stage { get; set; }
// is ready to study
public virtual bool IsReady { get; set; }
public virtual DateTime DateCreated { get; set; }
}
How can I set a default value for DateCreated?
Would the method change if I want to insert a blank Side into Sides upon Card creation?
You could set defaults in the constructor:
public CreateCardViewModel()
{
DateCreated = DateTime.Now;
Sides = new List<Side> { new Side() };
}
Caveat: There is an issue with using DateTime.Now from a unit testing perspective. If you're unit testing your ViewModel creation and need to be able to verify that the created date is set to a known value, you can look at creating a separate concept for time, as detailed in Ayende's blog. You basically create a static func, SystemTime, that you can set to a known value in your tests. If you don't set it, it defaults to DateTime.Now:
public static class SystemTime
{
public static Func<DateTime> Now = () => DateTime.Now;
}
Your constructor code then becomes:
public CreateCardViewModel()
{
DateCreated = SystemTime.Now();
Sides = new List<Side> { new Side() };
}
If you need to actually set the time to a known value, you do this:
SystemTime.Now = () => new DateTime(2013, 2, 11, 17, 41, 12);
I agree on The SystemTime approach.
Although, I personally don't like setting the CreatedDate on the constructor, since there can be a short time lapse since you instantiate the object and when you persist it to the database. (And here I am assuming you definitely are)
You could make all your domain objects inherit from an interface like this one:
public interface ITimeStamped
{
DateTime DateCreated { get; set; }
}
And then on the Commit method int the Context class I would do something like this to set the date for all entities that implement the interface:
foreach (var entry in ChangeTracker.Entries<ITimeStamped>()
.Where(entry => entry.State == EntityState.Added))
{
entry.Entity.DateCreated = SystemTime.Now();
}
This way you're totally certain that the entity is stored with the correct DateTime when it was persisted on the database.
I have a class, which has 8 properties / 8 columns in DB. In the Edit page, I want to exclude the AddedDate and UserID fields. When a user edits a voucher, he can't overwrite the AddedDate or UserID values in the DB.
public class Voucher
{
public int ID { get; set; }
public string Title { get; set; }
public string SiteName { get; set; }
public string DealURL { get; set; }
public DateTime AddedDate { get; set; }
public DateTime? ExpirationDate { get; set; }
public string VoucherFileURL { get; set; }
public Guid UserID { get; set; }
}
Here is what I have for Edit controller:
// POST: /Voucher/Edit/5
[HttpPost]
public ActionResult Edit([Bind(Exclude = "AddedDate")]Voucher voucher)
{
if (ModelState.IsValid)
{
db.Entry(voucher).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(voucher);
}
On Edit page, when I click on submit, I got the following error:
System.Data.SqlServerCe.SqlCeException: An overflow occurred while converting to datetime.
Seems like the AddedDate didn't get excluded from the voucher object and triggered the error.
Would you please let me know how to fix it? Thanks!
(it is an updated version of asp.net mvc3 UpdateModel exclude properties is not working, I will go with another approach)
Never use your domain entities as action arguments and never pass your domain entities to your views. I would recommend you to use view models. In the view model you will include only the properties that you want to be bound from the view. The view model is a class that's specifically tailored to the requirements of a given view.
public class VoucherViewModel
{
public int ID { get; set; }
public string Title { get; set; }
public string SiteName { get; set; }
public string DealURL { get; set; }
public DateTime? ExpirationDate { get; set; }
public string VoucherFileURL { get; set; }
}
and then:
[HttpPost]
public ActionResult Edit(VoucherViewModel model)
{
// TODO: if the view model is valid map it to a model
// and pass the model to your DAL
// To ease the mapping between your models and view models
// you could use a tool such as AutoMapper: http://automapper.org/
...
}
UPDATE:
In the comments section #Rick.Anderson-at-Microsoft.com points out that while I have answered your question I haven't explained where the problem comes from.
The thing is that DateTime is a value type meaning it will always have a value. The [Bind(Exclude = "AddedDate")] works perfectly fine and it does what it is supposed to do => it doesn't bind the AddedDate property from the request. As a consequence the property will have its default value which for a DateTime field is 1/1/0001 12:00:00 AM and when he attempts to save this in SQL Server it blows because SQL Server doesn't support such format.