ASP.NET MVC4 Generalized Field Initialization - asp.net

In ASP.NET MVC4, I want to initialize fields with values. When the user gets the page, before he has posted it back, I want to have it start out with values in some fields. The values will be drawn from the query string, not hard coded. Say a user is filling out a form: He's logged in, you know his name and address. As a courtesy, make those the default values. Doesn't matter where they come from, really, except that it's a set of key-value pairs, and the values are strings. I just want to put something in fields without the user posting the form first and I'd really like to do it without hard-coding a long, long list of assignments to every property in a rather complicated model.
Right now it's done in a JS loop in $(document).ready(), but it belongs on the server. I'd like to replicate that logic, though: Treat the query param names as unique identifiers.
In the Index() method of my controller, I tried calling ModelState.TrySetModelValue() (which when ModelState is populated, identifies each field by one unique string) but at this stage, ModelState is empty, so of course that didn't work. I tried changing Index() to expect an instance of the model as a parameter, but that doesn't help.
Must I rewrite every #Html.EditorFor()/TextBoxFor()/etc. call in the application? That seems crazy. Properly, this is something I'd do in a loop, in one place, not scattered around in multiple spots in each of a growing number of views.
I have a feeling that I'm failing to grasp something fundamental about the way MVC4 is intended to work.
UPDATE 2
It turns out that if you decorate your action method with [HttpGet], and you have it expect the model as a parameter, then if you use the field names (foo.bar) rather than IDs (foo_bar) in the query string, it does what I want automatically. ModelState is populated. I must not have had the action method decorated with [HttpGet] when I looked at ModelState.
If a field is set via query string automatically, that supersedes whatever's in your model. That's reasonable; the whole point is to override the model's default values. But if you want to in turn override possible query string values (e.g., say there's a checkbox for an "electronic signature"; that should always require an explicit effort on the user's part), then you've got to do that via ModelState.
That means that my first solution, below, had no actual effect (provided I had the [HttpGet] property on the action method). It only set properties of the model which had already been set in ModelState by the framework, and whose values in the model were therefore ignored.
What's a little bit stranger is that ModelState gives fields a different key if they're not in the query string. foo.bar.baz uses just that as a key if it's in the query string, but if it isn't, the key becomes foo.footypename.bar.bartypename.baz. There appears to be an exception if the property's name is the same as it's type: I have a Name model class, and another model class has a property public Name Name { get; set }. Properties of type Name, which are named name, are never followed by their type name in the ModelState keys. However, I have not yet ruled out other possible reasons for that particular property having its typename excluded. That's a guess. The typenames are excluded for "leaf" properties in all cases in my model. Is that because they're types known to the system, or "leaves", or what? I don't know.
In any case, a leaf property of the "root" class of the model always uses its own name as a key in ModelState.
So the generalized answer is you assign to the model. But there's a different specific answer for initialization from a query string.
UPDATE
Solution -- much code snipped
// Controller base
public abstract class ControllerBase<TModel> : Controller
{
[HttpGet]
public virtual ActionResult Index(TModel model)
{
HttpContext.Request.QueryString.CopyTo(model);
return View("Index", model);
}
}
public static class Extensions
{
/// <summary>
/// Given NameValueCollection of keys/values in the form
/// "foo.bar.baz" = "text", and an object which is the *parent* of
/// foo, set properties of foo accordingly.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="src"></param>
/// <param name="model"></param>
public static void CopyTo<T>(this NameValueCollection src, T target)
{
String strkey;
Object objval;
foreach (var key in src.Keys)
{
strkey = "" + key;
objval = src[strkey];
target.TrySetPropertyValue(strkey, objval);
}
}
/// <summary>
/// Given a reference to an object objThis, the string "foo.bar.baz",
/// and an object o of a type optimistically hoped to be convertible
/// to that of objThis.foo.bar.baz, set objThis.foo.bar.baz = o
///
/// If foo.bar is null, it must have a default constructor, or we fail
/// and return false.
/// </summary>
/// <param name="objThis"></param>
/// <param name="propPathName"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool TrySetPropertyValue(this object objThis,
string propPathName, object value)
{
if (string.IsNullOrWhiteSpace(propPathName))
{
throw new ArgumentNullException(propPathName);
}
var names = propPathName.Split(new char[] { '.' }).ToList();
var nextPropInfo = objThis.GetType().GetProperty(names.First());
if (null == nextPropInfo)
return false;
if (names.Count > 1)
{
var nextPropValue = nextPropInfo.GetValue(objThis, null);
if (null == nextPropValue)
{
nextPropValue = Activator
.CreateInstance(nextPropInfo.PropertyType);
nextPropInfo.SetValue(objThis, nextPropValue);
}
names.RemoveAt(0);
return nextPropValue.TrySetPropertyValue(
String.Join(".", names), value);
}
else
{
try
{
var conv = System.ComponentModel.TypeDescriptor
.GetConverter(nextPropInfo.PropertyType);
value = conv.ConvertFrom(value);
nextPropInfo.SetValue(objThis, value);
}
catch (System.FormatException)
{
return false;
}
return true;
}
}
}

You can initialize your model in controller with default values and then use it like
#Html.TextBoxFor(m => Model.Name)
Initialization in Controller:
public ActionResult Index()
{
MyModel model = new MyModel();
model.Name = "myname";
return View("myview", model);
}
You can also set the attributes in TextBoxFor
#Html.TextBoxFor(m => Model.Name, new { value = "myname"})
Update
If your url looks like mysite/Edit?id=123 try decalring your controller action like
public ActionResult Edit(string id)
{ ...
Also try decorating it with HttpPost or HttpGet attribute

Related

ReactiveUI with XF: How to create a Command Binder for a custom control?

I am working with Xamarin Forms and ReactiveUI and trying to bind a custom command from a custom XF control to my view model.
this.BindCommand(ViewModel, vm => vm.HasChangesCommand, view => view.socket1);
My control socket1 has a Dependency Property Command of type ICommand. However, I'm getting the error:
"System.Exception: Couldn't find a Command Binder for [ControlName]"
I think I have to create a Command Binder for my control but I can't find any hint on how to do this.
Is there any documentation on how to create a Command Binder for a custom control on Xamarin Forms?
EDIT:
I've seen that adding third parameter "eventName" it's working. However I would like to know if there's any way to build that Command Binder so you don't need to specify the event in that call.
If you want to be able to use BindCommand with a custom view, the easiest way is to have a property of type ICommand on the view that is named Command. Doing a OneWayBind as Daniel suggested is also easy, though it's also easy to forget to do that when you're used to using BindCommand for command bindings.
If you want to use anything else (an event, gesture recognizer, etc...), you can create an implementation of ICreatesCommandBinding that defines how the command is wired up to the target object. So you can do something like the following:
public class SocketControl : ContentView
{
public static readonly BindableProperty MyCustomCommandProperty = BindableProperty.Create(
nameof(MyCustomCommand),
typeof(ICommand),
typeof(SocketControl));
public ICommand MyCustomCommand
{
get => (ICommand)GetValue(MyCustomCommandProperty);
set => SetValue(MyCustomCommandProperty, value);
}
//...
}
public sealed class SocketControlCommandBinder : ICreatesCommandBinding
{
public IDisposable BindCommandToObject(ICommand command, object target, IObservable<object> commandParameter)
{
var socket = (SocketControl)target;
// get the original value so we can restore it when the binding is disposed...
var originalValue = socket.GetValue(SocketControl.MyCustomCommandProperty);
var disposable = Disposable.Create(() => socket.SetValue(SocketControl.MyCustomCommandProperty, originalValue));
// set the control's command to the view-model's command
socket.SetValue(SocketControl.MyCustomCommandProperty, command);
return disposable;
}
public IDisposable BindCommandToObject<TEventArgs>(ICommand command, object target, IObservable<object> commandParameter, string eventName)
{
/// not shown here ...
return Disposable.Empty;
}
/// <summary>
/// Returns a positive integer when this class supports BindCommandToObject for this
/// particular Type. If the method isn't supported at all, return a non-positive integer.
/// When multiple implementations return a positive value, the host will use the one which
/// returns the highest value. When in doubt, return '2' or '0'
/// </summary>
/// <param name="type">The type to query for.</param>
/// <param name="hasEventTarget">If true, the host intends to use a custom event target.</param>
/// <returns>A positive integer if BCTO is supported, zero or a negative value otherwise</returns>
public int GetAffinityForObject(Type type, bool hasEventTarget)
{
return type.GetTypeInfo().IsAssignableFrom(typeof(SocketControl).GetTypeInfo()) ? 2 : 0;
}
}
Once you have the command binder created, you need to register it so ReactiveUI knows how to use it. In your app.xaml.cs (or wherever you create your application):
Splat.Locator.CurrentMutable.Register(
() => new SocketControlCommandBinder(),
typeof(ReactiveUI.ICreatesCommandBinding));
One way to solve this is to do a OneWayBind to the command instead. Your control should then handle the command. Setting enabled if needed and execute command when needed.
this.OneWayBind(ViewModel, vm => vm.HasChangesCommand, view => view.socket1.CommandName);

FromUri binding when querystring is empty

If we have the following controller action in Web API
public async Task<HttpResponseMessage> GetRoutes(
[FromUri] MapExtentQuery extent,
[FromUri] PagingQuery paging)
{
...
}
with
class MapExtentQuery {
public int X { get;set; }
public int Y { get; set; }
}
class PagingQuery {
public int Skip { get; set; }
public int Top { get; set; }
}
and we make a GET request to /routes both parameters (extent and paging) will be null.
If the request contains at least one querystring parameter, for instance
/routes?x=45
then both complex parameters will get initialized, so in the case of the 2nd route
extent.X = 45
extent.Y = 0
paging != null (but Skip and Top will be 0 of course).
Why does the [FromUri] binder work this way? It makes little or no sense.
I would understand if it initialized only the parameter that contains a property that matched at least one of the querystring values.
The problem is, this behaviour requires us to check when parameters are null (which happens only in the case that no querystring parameter was provided) and then initiliaze them ourselves.
Because obviously those complex params might have constructors which would set some property values to default.
It is because when you do not have query string and the parameters have FromUri attribute, the parser for the query string does not run and all values are not binded - you receive null.
If you have query string, the binder runs and instantiates all FromUri parameters and primitive types with their default values and then tries to fill in the properties from the query string values. This leaves you with two instantiated parameters but only one having the populated value because that is all the query string has.
As for why it works this way - most likely performance - this way they will not need to find which parameters they need to instantiate - they instantiate all of them and then find the properties. It is faster and performance is critical in the action and model binding.

How to Mock this using MSTest and Moq

I'm just learning how to use Moq, with MSTest. I have the following method I want to test, in an existing applicaton:
/// <summary>
/// Return the marker equipment Id value give a coater equipment Id value.
/// </summary>
/// <param name="coaterEquipmentId">The target coater</param>
/// <returns>The marker equipment Id value</returns>
internal static string GetMarkerEquipmentId(string coaterEquipmentId)
{
return CicApplication.CoaterInformationCache
.Where(row => row.CoaterEquipmentId == coaterEquipmentId)
.Select(row => row.MarkerEquipmentId)
.First();
}
The CicApplication object is a 'global' object with a property named CoaterInformationCache, which is a List of CoaterInformation classes.
I assume I will need to mock the CicApplication.CoaterInformationCache somehow, and I probably need to pass this method an interface containing the list of CoaterInformation classes, instead of accessing the list via a global object that only contains values at runtime?
Thanks very much
Globals / statics are a bane for unit testability. To make this testable, you're correct in that you should eliminate the CicApplication global. You can create an interface, i.e. ICicApplication with the same public API, and pass an instance into your application code.
public interface ICicApplication
{
public List<CoaterInformation> CoaterInformationCache { get; }
}
public DefaultCicApplication : ICicApplication
{
public List<CoaterInformation> CoaterInformationCache
{
// Either use this class as an adapter for the static API, or move
// the logic here.
get { return CicApplication.CoaterInformationCache; }
}
}
Since this is a static method, you could pass it as a method argument, otherwise, convert the static method to an instance method an initialize a ICicApplication field on the object (perhaps pass an instance into the constructor).
Then, when you set up your unit test, you can pass in a mock instance set up with Moq:
Mock<ICicApplication> appMock = new Mock<ICicApplication>();
appMock
.SetupGet(ca => ca.CoaterInformationCache)
.Returns(new List<CoaterInformation> { ... });

EntityFramework: Update single field with detached entity

Unlike normal, I have code that actually works, but I'm wondering if it's the only (or best approach).
The basic Idea is I have an existing application that's handmade data layer is being ported to Entity Framework. As a compromise to minimize code changes, I'm working with existing methods, which tend to take a more disconnected approach. For example I have a lot of things like this:
UpdateNote(int noteId, string note)
I seem to have a method that works for this type of update without requiring a re-fetch:
var context = new MyEntities();
context.Configuration.ValidateOnSaveEnabled = false;
var note = new Model.Note{ Id = noteId, Note = ""};
context.Notes.Attach(note);
note.Note = "Some Note";
context.SaveChanges();
It's a little ugly (though concise enough), so I would like to know if there is there a better approach to use with EF? Any downsides to this method, other than loosing built-in validation?
This is a pattern that will be used all over my app.
The following extension method for DbContext is an approach which would avoid to initialize your entities with some values different to the values you want to change it to.
public static class EFExtensions
{
public static void MarkAsModified(this DbContext context, object entity,
params string[] properties)
{
foreach (var property in properties)
context.Entry(entity).Property(property).IsModified = true;
}
}
You could then use it this way:
var context = new MyEntities();
context.Configuration.ValidateOnSaveEnabled = false;
var note = new Model.Note { Id = noteId }; // only key properties required to set
note.Note = "Some Note";
note.SomeOtherProperty = 1234;
note.AndAnotherProperty = "XYZ";
context.Notes.Attach(note);
context.MarkAsModified(note, "Note", "SomeOtherProperty" , "AndAnotherProperty");
context.SaveChanges();
Note: This only works for scalar properties, not navigation properties.
Besides validation I could imagine that this approach is problematic for a proper concurrency checking.
Edit
According to #Adam Tuliper's comment below concurrency is likely not a problem because the concurrency check is skipped when an entity is attached manually to the context (without reading it from the database) and marked as modified to send an UPDATE command to the database. It just overwrites the lastest version in the DB. Thanks to Adam for pointing this out!
See the following code I use to easily attach a disconnected object back to the graph, assuming we're now going to save it.
public static class EntityFrameworkExtensions
{
/// <summary>
/// This class allows you to attach an entity.
/// For instance, a controller method Edit(Customer customer)
/// using ctx.AttachAsModified(customer);
/// ctx.SaveChanges();
/// allows you to easily reattach this item for udpating.
/// Credit goes to: http://geekswithblogs.net/michelotti/archive/2009/11/27/attaching-modified-entities-in-ef-4.aspx
/// </summary>
public static void AttachAsModified<T>(this ObjectSet<T> objectSet, T entity) where T : class
{
objectSet.Attach(entity);
objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
}
/// <summary>
/// This marks an item for deletion, but does not currently mark child objects (relationships).
/// For those cases you must query the object, include the relationships, and then delete.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objectSet"></param>
/// <param name="entity"></param>
public static void AttachAsDeleted<T>(this ObjectSet<T> objectSet, T entity) where T : class
{
objectSet.Attach(entity);
objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Deleted);
}
public static void AttachAllAsModified<T>(this ObjectSet<T> objectSet, IEnumerable<T> entities) where T : class
{
foreach (var item in entities)
{
objectSet.Attach(item);
objectSet.Context.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
}
}
}

ASP.NET MVC 3 model and viewmodel classes

I have a viewmodel and it has a nested class what is not in connection other model class. I can fill the necessary data in this nested class (ClassX) to show those in the view but I can't get out data from the nested class (through MyViewModel) in the post action just when I give it in post action as another parameter. It appears in original viewmodel (MyViewModel) but its attributes are null/0.
public class MyViewModel
{
public MyViewModel()
{
classX = new ClassX();
}
public ClassX classX;
public int attrA {get;set;}
...
}
public class ClassX {}
//
// POST: /MyModel/Create
public ActionResult Create(MyViewModel myvm, **ClassX cx**, FormCollection collection)
{}
My question: Can I read data out from the nested class through the viewmodel class?
http://goneale.com/2009/07/27/updating-multiple-child-objects-and-or-collections-in-asp-net-mvc-views/ this is a good article for you
MyViewModel myViewModel= new MyViewModel();
UpdateModel(myViewModel, "MyViewModel");
myViewModel.myViewModel= new myViewModel();
UpdateModel(myViewModel.classX, "User.classX");
If I understood your question correctly, you need BindAttribute.Prefix on your ClassX cx parameter of action method. This way, model binder will correctly bind values for it. The value for Bind.Prefix should be name of ClassX property in MyViewModel, in your example, the string - "classX"
//
// POST: /MyModel/Create
public ActionResult Create(MyViewModel myvm, [Bind(Prefix = "classX")]ClassX cx, FormCollection collection)
{}
Idea is in the following - on client side, when you submit the form, its values are sent to server like this
attrA=someValue
classX.SomeProperty=someValue
classX.SomeOtherProperty=someOtherValue
When passed to action parameters, this name=value string pairs are translated to objects. Names from left side of equality match to property names of MyViewModel, and the ClassX parameter stays empty. But then you specify Prefix = "classX", model binder matches strings after dot in left side of equality to ClassX property names, so that should fill values of ClassX too.

Resources