I am currently developing an application with the new ASP.NET MVC2 framework. Originally I started writing this application in the ASP.NET MVC1 and I'm basically just updating it to MVC2.
My problem here is, that I don't really get the concept of the FormCollection object vs. the old Typed object.
This is my current code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
try
{
Member member = new Member();
member.FirstName = collection["FirstName"];
member.LastName = collection["LastName"];
member.Address = collection["Address"];
// ...
return RedirectToAction("Details", new { id = member.id });
}
catch
{
return View("Error");
}
}
This is the Code from the MVC1 application:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Member member)
{
try
{
memberRepository.Add(member);
memberRepository.Save();
return RedirectToAction("Details", new { id = member.id });
}
catch
{
}
return View(new MemberFormViewModel(member, memberRepository));
}
What are the benefits of switching to FormCollection in MVC2 and more importantly - how is it used properly?
You had the FormCollection object in v1 as well. But it is more preferred to use a typed object. So if you are already doing that, then continue doing so.
By using FormCollection, you wind up manually matching your post data or query string key/values into values to use in your code using string typing (resulting in stringly-typed code), when instead the built-in Model Binding can do this for you if you use form models, aka "typed objects."
I think by using the FormCollection, you would probably also lose the ability to use the handy Data Annotation (slash Validation) attributes on your model objects as well, which are designed for use with typed object model binding.
Additionally, unit testing can become much more cumbersome once you start touching your controller.Request.Form. You might find yourself having to mock and setup an HttpContextBase, and an HttpRequestBase just to have that mock request's .Form property return the NameValueCollection that you are wanting your test to see. Contrast this to letting model binding do the work for you such as:
// Arrange
var myModel = new MyModel( Property1 = "value1", Property2 = "value2");
// Act
var myResult = myController.MyActionMethod(myModel);
// Assert
// whatever you want the outcome to be
In summary, I would recommend against using FormCollection to the maximum extent possible.
Related
I'd like to return a data object that contains the details of the error with a BadRequestErrorMessageResult or BadRequestErrorMessageResult object like so:
public IHttpActionResult Action(Model model)
{
var validationResult = model.Validate();
if (validationResult.Successful)
{
// this one's okay; it supports sending data with a 200
return Ok(validationResult);
}
else
{
// However, how do I return a custom data object here
// like so?
// No such overload, I wish there was
// return BadRequest(validationResult);
}
}
The only three overloads of the ApiController.BadRequest() method are:
1. BadRequest();
2. BadRequest(string message);
3. BadRequest(ModelStateDictionary modelState);
Even with #3, a model state dictionary is ultimate a deep collection with one layer upon another, at the bottom of which, though, is a bunch of KeyValuePair<string, ModelError> where each ModelError also only has either a string or an Exception object.
Therefore, even with #3, we are only able to pack a string to send and not a custom object like I want to.
I am really not asking how I may go about working a hack or a kludge around the situation. My question is: is there an overload or another way baked into the .NET API to send an object to the client with a Bad Request HTTP status code?
I am using ASP.NET Web API version 5.2.4 targeting .NET Framework version 4.6.1.
You can use the Content<T>(...) method to do this. It returns a NegotiatedContentResult, which is serialized depending on the request headers (e.g. json, xml), and allows you to specify a HttpStatusCode.
You can use it like this:
return Content(HttpStatusCode.BadRequest, myObject);
If you wanted to, you could create your own BadRequest<T>(T obj) method in the controller as a wrapper, so then you could call it as you wanted:
public IHttpActionResult BadRequest<T>(T obj)
{
return Content(HttpStatusCode.BadRequest, obj);
}
public IHttpActionResult Action()
{
// do whatever validation here.
var validationResult = Validate();
// then return a bad request
return BadRequest(validationResult);
}
You can build/format the string in JSON format, pass it as string in the BadRequest() parameter and convert it to JSON again or any object on the caller's backend.
Haven't tried that but that should work.
How does the ASP.NET MVC's ViewBag work? MSDN says it is just an Object, which intrigues me, how does "Magic" properties such as ViewBag.Foo and magic strings ViewBag["Hello"] actually work?
Also, how can I make one and use it in my ASP.NET WebForms app?
Examples would be really appreciated!
ViewBag is of type dynamic but, is internally an System.Dynamic.ExpandoObject()
It is declared like this:
dynamic ViewBag = new System.Dynamic.ExpandoObject();
which is why you can do :
ViewBag.Foo = "Bar";
A Sample Expander Object Code:
public class ExpanderObject : DynamicObject, IDynamicMetaObjectProvider
{
public Dictionary<string, object> objectDictionary;
public ExpanderObject()
{
objectDictionary = new Dictionary<string, object>();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
object val;
if (objectDictionary.TryGetValue(binder.Name, out val))
{
result = val;
return true;
}
result = null;
return false;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
try
{
objectDictionary[binder.Name] = value;
return true;
}
catch (Exception ex)
{
return false;
}
}
}
It's a dynamic object, meaning you can add properties to it in the controller, and read them later in the view, because you are essentially creating the object as you do, a feature of the dynamic type. See this MSDN article on dynamics. See this article on it's usage in relation to MVC.
If you wanted to use this for web forms, add a dynamic property to a base page class like so:
public class BasePage : Page
{
public dynamic ViewBagProperty
{
get;
set;
}
}
Have all of your pages inherit from this. You should be able to, in your ASP.NET markup, do:
<%= ViewBagProperty.X %>
That should work. If not, there are ways to work around it.
The ViewBag is an System.Dynamic.ExpandoObject as suggested. The properties in the ViewBag are essentially KeyValue pairs, where you access the value by the key. In this sense these are equivalent:
ViewBag.Foo = "Bar";
ViewBag["Foo"] = "Bar";
ViewBag is used to pass data from Controller Action to view to render the data that being passed. Now you can pass data using between Controller Action and View either by using ViewBag or ViewData.
ViewBag: It is type of Dynamic object, that means you can add new fields to viewbag dynamically and access these fields in the View. You need to initialize the object of viewbag at the time of creating new fields.
e.g:
1. Creating ViewBag:
ViewBag.FirstName="John";
Accessing View:
#ViewBag.FirstName.
ViewBag is of type dynamic. More, you cannot do ViewBag["Foo"]. You will get exception - Cannot apply indexing with [] to an expression of type 'System.Dynamic.DynamicObject'.
Internal implementation of ViewBag actually stores Foo into ViewData["Foo"] (type of ViewDataDictionary), so those 2 are interchangeable. ViewData["Foo"] and ViewBag.Foo.
And scope. ViewBag and ViewData are ment to pass data between Controller's Actions and View it renders.
ViewBag is a dynamic type that allow you to dynamically set or get values and allow you to add any number of additional fields without a strongly-typed class
They allow you to pass data from controller to view.
In controller......
public ActionResult Index()
{
ViewBag.victor = "My name is Victor";
return View();
}
In view
#foreach(string a in ViewBag.victor)
{
.........
}
What I have learnt is that both should have the save dynamic name property ie ViewBag.victor
public dynamic ViewBag
{
get
{
if (_viewBag == null)
{
_viewBag = new DynamicViewData(() => ViewData);
}
return _viewBag;
}
}
I have a MVC4 WebAPI controller that returns an IQueryable, and therefore I can use $filter and friends in the URL to manipulate the result from the REST endpoint. Here's my controller:
public class EnrollmentController : ApiController
{
[Queryable]
public IQueryable<tblEnrollment> Get()
{
var context = new ProjectEntities();
context.ContextOptions.LazyLoadingEnabled = false;
return context.tblEnrollment.AsQueryable();
}
}
But, like this poster, I'm wanting to make the JSON output format a little different to be friendlier with Ember Data's expected format. So I'd like to return this instead:
return new { enrollment = context.tblEnrollment.AsQueryable() };
However, that breaks OData capability because I'm not returning the IQueryable to the WebAPI layer. So, I'm wondering if there's a way to do something like this:
return new { enrollment = context.tblEnrollment.AsQueryable().ApplyOData() };
Which I'm sure would be way to good to be true...but is there some way to explicitly process the OData parameters against an IQueryable instead of letting the WebAPI layer do it implicitly on the result set returned from a Get method? Or is there another way to accomplish what I want here?
Incidentally, I'm stuck on EF4 for the time being, because I can't upgrade to VS2012 (and hence to .NET4.5 and hence EF5). I could theoretically upgrade to EF 4.3.1, if it would help.
Instead of marking your action as [Queryable], you can add a parameter of type ODataQueryOptions and apply it manually. Here's what it might look like:
public class EnrollmentController : ApiController
{
public object Get(ODataQueryOptions<tblEnrollment> query)
{
var context = new ProjectEntities();
context.ContextOptions.LazyLoadingEnabled = false;
var queryResults = query.ApplyTo(context.tblEnrollment.AsQueryable());
return new { enrollment = queryResults };
}
}
I am currently looking for a design pattern or rather a best practice in implementing Repository<Entity>.Update() method for a ASP.NET MVC 4 application which uses Entity Framework 5 with Code First approach.
Problem:
The problem I encountered is that when an entity is queried from the database and shown on a view it may not have all the attributes populated. As a result when the repository.Update(entity) method is invoked, the entity passed to the Update() method may have un-bound properties having null values. However they may have some values in the database. As an example Customer.Misc in below code.
So the problem comes here. According to this approach all the properties which were not bound on the view are set to Null in the database after the first Update() method call.
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Misc { get; set; }
}
[HttpGet]
public ActionResult Update(int id)
{
Repository<Customer> repo = new Repository<Customer>();
return View(repo.GetById(id)); // View only binds Customer.Name
}
[HttpPost]
public ActionResult Update(Customer customer)
{
Repository<Customer> repo = new Repository<Customer>();
repo.Update(customer); // Customer.Misc is null
...
}
public void Update(TEntity entity)
{
var entry = DbContext.Entry<TEntity>(entity);
if (entry.State == EntityState.Detached)
{
ObjectContext.ApplyCurrentValues(EntitySetName, entity);
}
DbContext.SaveChanges();
}
Solutions I could think:
Bind all entity attributes on the view:
I think this is not feasible and at the same time it may lead to performance issues since all attributes get populated.
Implement a custom method to copy property values to avoid null values being copied.
EntityHelper.CopyNotNullValues(source, target) and ignore null values in the source entity. If we do this we might not be able to set any of the values to null if required.
Implement View Models and transform data back and forth with the Domain Model.
This is the best approach I could think of so far. All the attributes bound to the View Model will get populated always, on the Update POST, copy all View Model values to the Domain Model.
Really appreciate your thoughts on this.
In Entity Framework, using ChangeObjectState or ApplyCurrentValues will cause data loss. The only way to work around this issue in this case is attaching the input entity and mark the properties to be updated. See below example:
public void Update(TEntity entity, string[] updatedProperties)
{
DbContext.Entities.Attach(entity);
var entry = DbContext.Entry<TEntity>(entity);
for (int i = 0; i < updatedProperties.Length; i++)
{
entry.SetModifiedProperty(updatedProperties[i]);
}
DbContext.SaveChanges();
}
[HttpPost]
public ActionResult Update(Customer customer)
{
Repository<Customer> repo = new Repository<Customer>();
repo.Update(customer, new string[]{ "Name" }); // Only update name
...
}
It's the best solution I can think of. You wanna have least code and good performance. It's as difficult as finding an easy and well paid job.
This is a follow up on this:
other SO post
Is this a good way to test whether the validation based on DataAnnotations works in the controller:
[Test]
public void UserController_CannotCreateUserWithNoLastName()
{
// Arrange
var user = new CreateUserViewModel();
UsersController controller = new UsersController();
var validationContext = new ValidationContext(user, null, null);
var validationResults = new System.Collections.Generic.List<ValidationResult>();
Validator.TryValidateObject(user, validationContext, validationResults);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError("", validationResult.ErrorMessage);
}
// Act
var result = controller.CreateUser(user);
// Assert
Assert.IsFalse(controller.ModelState.IsValid);
}
Any improvement suggestions would be very much welcome. I am also wondering whether one usually writes one test for each validation/business rule. Thanks!
Is the code you listed based off of what's found here?
Personally, I write tests like so on a per annotation basis:
[Test]
public void CreateEventViewModel_Description_Property_Contains_StringLength_Attribute()
{
// Arrange
PropertyInfo propertyInfo = typeof(CreateEventViewModel)
.GetProperty("Description");
// Act
StringLengthAttribute attribute = propertyInfo
.GetCustomAttributes(typeof(StringLengthAttribute), true)
.Cast<StringLengthAttribute>()
.FirstOrDefault();
// Assert
Assert.NotNull(attribute);
Assert.AreEqual(255, attribute.MaximumLength);
}
I based this off of some information that Brad Wilson posted some time back. These tests are stored separate of the controller tests. I'm not sure if there's a more efficient way of doing this today (some folks have created more generic helper methods to do this type of testing; I just prefer having explicit tests for each attribute on my own) but it will verify that your data annotations do exist on your view models as expected.
A combination of this type of test, specifically verifying attributes, along with tests to test model state as listed in your previous question
UsersController.ModelState.AddModelError("username", "Bad username");
is what I typically go with.