Is it possible to pass an entire object from my view to my controller. In my for each the address is populated as expected however I am struggling to pass this complete object back to my controller method "EditPlusNewAddress".
#if (Model.Count() > 0)
{
<table border="1" cellpadding="1">
#foreach (var address in Model)
{
<tr>
<td>#address.postcode</td>
<td>#address.lat</td>
<td>#address.lng</td>
<td>#address.thorofare</td>
<td>#address.dthorofare</td>
<td>#address.county</td>
<td>#address.paon</td>
<td>#address.saon</td>
<td>#address.posttown</td>
<td>#address.uprn</td>
<td>#Html.ActionLink("Select Address", "EditPlusNewAddress", new { evpId = 11}, new JsonAddressModel{ county = address.county,
dthorofare = address.dthorofare,
ExtensionData = address.ExtensionData,
lat = address.lat,
lng = address.lng,
paon = address.paon,
postcode = address.postcode,
posttown = address.posttown,
saon = address.saon,
thorofare = address.thorofare,
uprn = address.uprn}, null)</td>
</tr>
}
</table>
}
I'm getting the following errors:
Error 2 Argument 4: cannot convert from 'AnonymousType#1' to 'string'
Error 3 Argument 5: cannot convert from
'Solution.Core.AddressService.JsonAddressModel' to
'System.Web.Routing.RouteValueDictionary'
Error 1 'System.Web.Mvc.HtmlHelper>'
does not contain a definition for 'ActionLink' and the best extension
method overload
'System.Web.Mvc.Html.LinkExtensions.ActionLink(System.Web.Mvc.HtmlHelper,
string, string, string, System.Web.Routing.RouteValueDictionary,
System.Collections.Generic.IDictionary)' has some
invalid arguments
We don't pass objects back from a view, we pass name:value combinations and build a model from that on the server:
In this case all you need is an [HttpGet] action on your controller accepting a JsonAddressModel parameter
[HttpGet]
public ActionResult EditPlusNewAddress(JsonAddressModel model)
{
....
}
Then in your view you pass up the values you want to use. Here the error message should have given you the clue, as you're simply using the wrong type of object... a JsonAddressModel instead of a RouteValueDictionary.
This is because you went a position to far in your ActionLink definition. So assuming evpId is the Identifier for you JsonAddressModel it should have just been:
#Html.ActionLink("Select Address", "EditPlusNewAddress",
new { evpId = 11 ,
county = address.county,
dthorofare = address.dthorofare,
ExtensionData = address.ExtensionData,
lat = address.lat,
lng = address.lng,
paon = address.paon,
postcode = address.postcode,
posttown = address.posttown,
saon = address.saon,
thorofare = address.thorofare,
uprn = address.uprn
})
That would be how you build up an url that looked like:
/Controller/EditPlusNewAddress?evpId=11&county=burbank&dthorofare=abc&......
The model binder is then smart enough to take the query string and build a JsonAddressModel out of it. So that would be how you could pass around a complex url for sharing search filters, etc.
The problem however is GET is supposed to be an idempotent operation. Whereas you're calling your action "EditPlusNewAddress" which sounds like you want to change something server side and for that, good practice dictates, that you should be creating a form and POSTing it back.
GET links that change things reeks of malware, evil doers and/or incompetence.
Related
Trying to perform a calculation in a view model. I have two donation columns, want to add them together in the viewmodel.
This is in the ViewModel Class:
[DataType(DataType.Currency)]
public decimal ttlDonation
{
get { return Donation.GetValueOrDefault() + GuestDonation.GetValueOrDefault(); }
}
The Viewmodel builds fine, and it displays properly in the razor view using (modelItem => item.ttlDonation). Problem is when I try to SUM this expression with a LINQ query in the controller like so:
var DonateTTl = GuestViewModel.Sum(f => f.ttlDonation);
Then my code throws an error:
The specified type member 'ttlDonation' is not supported in LINQ to Entities...
I am wondering the best way to do this. I also tried this in my controller (below), where I've changed my viewmodel slightly to a basic get; set; and it still isn't working. What is the best way to do this? Should I perform the calculation on my original Model rather than my viewmodel?
var GuestViewModel = guests.Select(g => new GuestIndexViewData
{
ID = g.ID,
FRID = g.FRID,
FirstName = g.FirstName,
LastName = g.LastName,
Company = g.Company,
email = g.email,
Donation = g.Donation,
GuestFirstName = g.GuestFirstName,
GuestLastName = g.GuestLastName,
GuestCompany = g.GuestCompany,
Guestemail = g.Guestemail,
GuestDonation = g.Donation,
Attended = g.Attended,
RSVP = g.RSVP,
Guest3 = g.Guest3,
ttlDonation = (g.GuestDonation.GetValueOrDefault() + g.Donation.GetValueOrDefault())
}).OrderBy(f => f.FRID);
var DonateTTl = GuestViewModel.Sum(f => f.ttlDonation);
I know I can find a way to do this but I am looking for the best way to do this as for standards/performance.
I think picking it out will work:
var DonateTTl = GuestViewModel.Select(vm => vm.ttlDonation).Sum();
I have converted my MVC3 application to MVC5, I had to change all views to razor. Having a challenge with a select list:
In ASPX view that works I am using the following:
<select id="Profession" name="Profession" style="width: 235px; background-color: #FFFFCC;">
<% List<string> allProfessions = ViewBag.AllProfessions;
string selectedProfession;
if (Model != null && !String.IsNullOrEmpty(Model.Profession))
selectedProfession = Model.Profession;
else
selectedProfession = allProfessions[0];
foreach (var aProfession in allProfessions)
{
string selectedTextMark = aProfession == selectedProfession ? " selected=\"selected\"" : String.Empty;
Response.Write(string.Format("<option value=\"{0}\" {1}>{2}</option>", aProfession, selectedTextMark, aProfession));
}%>
</select>
In Razor I am using:
<select id="Profession" name="Profession" style="width: 235px; background-color: #FFFFCC;">
#{List<string> allProfessions = ViewBag.AllProfessions;
string selectedProfession;}
#{if (Model != null && !String.IsNullOrEmpty(Model.Profession))
{selectedProfession = Model.Profession;}
else {selectedProfession = allProfessions[0];}
}
#foreach (var aProfession in allProfessions)
{
string selectedTextMark = aProfession == selectedProfession ?
"selected=\"selected\"" : String.Empty;
Response.Write(string.Format("<option value=\"{0}\" {1}>{2}</option>",
aProfession, selectedTextMark, aProfession));
}
</select>
The list shows up at the top of the page, I can't figure out where is the problem. Would appreciate your assistance.
Don't create your dropdown manually like that. Just use:
#Html.DropDownListFor(m => m.Profession, ViewBag.AllProfessions, new { style = "..." })
UPDATE
I tried your solution but got this error: Extension method cannot by dynamically dispatched
And, that's why I despise ViewBag. I apologize, as my answer was a little generic. Html.DropDownList requires the list of options parameter to be an IEnumerable<SelectListItem>. Since ViewBag is a dynamic, the types of its members cannot be ascertained, so you must cast explicitly:
(IEnumerable<SelectListItem>)ViewBag.AllProfessions
However, your AllProfessions is a simple array, so that cast won't work when the value gets inserted at run-time, but that can be easily fixed by casting it to a List<string> and then converting the items with a Select:
((List<string>)ViewBag.AllProfessions).Select(m => new SelectListItem { Value = m, Text = m })
There again, you see why dynamics are not that great, as that syntax is rather awful. The way you should be handling this type of stuff is to use your model or, preferably, view model to do what it should do: hold domain logic. Add a property to hold your list of profession choices:
public IEnumerable<SelectListItem> ProfessionChoices { get; set; }
And then, in your controller action, populate this list before rendering the view:
var model = new YourViewModel();
...
model.ProfessionChoices = repository.GetAllProfessions().Select(m => new SelectListItem { Value = m.Name, Text = m.Name });
return View(model);
repository.GetAllProfessions() is shorthand for whatever you're using as the source of your list of professions, and the Name property is shorthand for how you get at the text value of the profession: you'll need to change that appropriately to match your scenario.
Then in your view, you just need to do:
#Html.DropDownListFor(m => m.Profession, Model.ProfessionChoices)
Given that you don't have this infrastructure already set up, it may seem like a lot to do just for a drop down list, and that's a reasonable thing to think. However, working in this way will keep your view lean, make maintenance tons easier, and best of all, keep everything strongly-typed so that if there's an issue, you find out at compile-time instead of run-time.
I believe it's happening because of the Response.Write. Try this:
#Html.Raw(string.Format("<option value=\"{0}\" {1}>{2}</option>", aProfession,
selectedTextMark, aProfession))
This query produces an error No value given for one or more required parameters:
using (var conn = new OleDbConnection("Provider=..."))
{
conn.Open();
var result = conn.Query(
"select code, name from mytable where id = ? order by name",
new { id = 1 });
}
If I change the query string to: ... where id = #id ..., I will get an error: Must declare the scalar variable "#id".
How do I construct the query string and how do I pass the parameter?
The following should work:
var result = conn.Query(
"select code, name from mytable where id = ?id? order by name",
new { id = 1 });
Important: see newer answer
In the current build, the answer to that would be "no", for two reasons:
the code attempts to filter unused parameters - and is currently removing all of them because it can't find anything like #id, :id or ?id in the sql
the code for adding values from types uses an arbitrary (well, ok: alphabetical) order for the parameters (because reflection does not make any guarantees about the order of members), making positional anonymous arguments unstable
The good news is that both of these are fixable
we can make the filtering behaviour conditional
we can detect the category of types that has a constructor that matches all the property names, and use the constructor argument positions to determine the synthetic order of the properties - anonymous types fall into this category
Making those changes to my local clone, the following now passes:
// see https://stackoverflow.com/q/18847510/23354
public void TestOleDbParameters()
{
using (var conn = new System.Data.OleDb.OleDbConnection(
Program.OleDbConnectionString))
{
var row = conn.Query("select Id = ?, Age = ?", new DynamicParameters(
new { foo = 12, bar = 23 } // these names DO NOT MATTER!!!
) { RemoveUnused = false } ).Single();
int age = row.Age;
int id = row.Id;
age.IsEqualTo(23);
id.IsEqualTo(12);
}
}
Note that I'm currently using DynamicParameters here to avoid adding even more overloads to Query / Query<T> - because this would need to be added to a considerable number of methods. Adding it to DynamicParameters solves it in one place.
I'm open to feedback before I push this - does that look usable to you?
Edit: with the addition of a funky smellsLikeOleDb (no, not a joke), we can now do this even more directly:
// see https://stackoverflow.com/q/18847510/23354
public void TestOleDbParameters()
{
using (var conn = new System.Data.OleDb.OleDbConnection(
Program.OleDbConnectionString))
{
var row = conn.Query("select Id = ?, Age = ?",
new { foo = 12, bar = 23 } // these names DO NOT MATTER!!!
).Single();
int age = row.Age;
int id = row.Id;
age.IsEqualTo(23);
id.IsEqualTo(12);
}
}
I've trialing use of Dapper within my software product which is using odbc connections (at the moment). However one day I intend to move away from odbc and use a different pattern for supporting different RDBMS products. However, my problem with solution implementation is 2 fold:
I want to write SQL code with parameters that conform to different back-ends, and so I want to be writing named parameters in my SQL now so that I don't have go back and re-do it later.
I don't want to rely on getting the order of my properties in line with my ?. This is bad. So my suggestion is to please add support for Named Parameters for odbc.
In the mean time I have hacked together a solution that allows me to do this with Dapper. Essentially I have a routine that replaces the named parameters with ? and also rebuilds the parameter object making sure the parameters are in the correct order.
However looking at the Dapper code, I can see that I've repeated some of what dapper is doing anyway, effectively it each parameter value is now visited once more than what would be necessary. This becomes more of an issue for bulk updates/inserts.
But at least it seems to work for me o.k...
I borrowed a bit of code from here to form part of my solution...
The ? for parameters was part of the solution for me, but it only works with integers, like ID. It still fails for strings because the parameter length isn't specifed.
OdbcException: ERROR [HY104] [Microsoft][ODBC Microsoft Access Driver]Invalid precision value
System.Data.Odbc. OdbcParameter.Bind(OdbcStatementHandle hstmt,
OdbcCommand command, short ordinal, CNativeBuffer parameterBuffer, bool allowReentrance)
System.Data.Odbc.OdbcParameterCollection.Bind(OdbcCommand command, CMDWrapper cmdWrapper, CNativeBuffer parameterBuffer)
System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader, object[] methodArguments, SQL_API odbcApiMethod)
System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader)
System.Data.Common.DbCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
Dapper.SqlMapper.QueryAsync(IDbConnection cnn, Type effectiveType, CommandDefinition command) in SqlMapper.Async.cs
WebAPI.DataAccess.CustomerRepository.GetByState(string state) in Repository.cs
var result = await conn.QueryAsync(sQuery, new { State = state });
WebAPI.Controllers.CustomerController.GetByState(string state) in CustomerController .cs
return await _customerRepo.GetByState(state);
For Dapper to pass string parameters to ODBC I had to specify the length.
var result = await conn.QueryAsync<Customer>(sQuery, new { State = new DbString { Value = state, IsFixedLength = true, Length = 4} });
All,
I've read through a lot of posts about Checkboxes and ASP.MVC but I'm not that much wiser.
My scenario:
I have a strongly typed View where I pass a collection of summary objects to the view for rendering in a for-each. This summary object contains label data based on a unique id. I also add a checkbox to the row so do so via:
<td>
<%= Html.CheckBox("markedItem", Model.MarkedItem, new { TrackedItemId = Model.Id })%>
</td>
When I perform a POST to get the submitted results my action method takes the strongly typed ViewModel back but the original summary object that I used to create the list is not populated.
Ok, this is annoying, but I can understand why so I'll live with it.
What I then do is to add a new property to my ViewModel called "MarkedItem" which is a string collection.
On postback this marked item is filled with the before and after states if the checkbox has changed but nothing to tell me which key they were for. Just to clarify, if I send this
TrackedItemId = A, Value = false
TrackedItemId = B, Value = true
TrackedItemId = C, Value = false
and set the page to this:
TrackedItemId = A, Value = true
TrackedItemId = B, Value = true
TrackedItemId = C, Value = false
I will get back this:
MarkedItem[0] = true
MarkedItem[1] = false
MarkedItem[2] = true
MarkedItem[3] = false
in other words [0] is the new value and [1] is the old value, [2] and [3] represent values that haven't changed.
My questions are:
Is this right - that I get before and after in this way? Is there any way to only send the latest values?
How can I get hold of the custom attribute (TrackedItemId) that I've added so that I can add meaning to the string array that is returned?
So far I like MVC but it not handling simple stuff like this is really confusing. I'm also a javascript noob so I really hope that isn't the answer as I'd like to return the data in my custom viewmodel.
Please make any explanations/advice simple :)
<p>
<label>
Select project members:</label>
<ul>
<% foreach (var user in this.Model.Users)
{ %>
<li>
<%= this.Html.CheckBox("Member" + user.UserId, this.Model.Project.IsUserInMembers(user.UserId)) %><label
for="Member<%= user.UserId %>" class="inline"><%= user.Name%></label></li>
<% } %></ul>
and in the controller:
// update project members
foreach (var key in collection.Keys)
{
if (key.ToString().StartsWith("Member"))
{
int userId = int.Parse(key.ToString().Replace("Member", ""));
if (collection[key.ToString()].Contains("true"))
this.ProjectRepository.AddMemberToProject(id, userId);
else
this.ProjectRepository.DeleteMemberFromProject(id, userId);
}
}
With thanks to Pino :)
ok, one hack I've come up with - I really hate that I have to do this but I don't see another way round it and I'm sure it will break at some point.
I've already implemented by own ModelBinder to get round some other issues (classes as properties for example) so have extended it to incorporate this code. We use Guid's for all our keys.
If there are any alternatives to the below then please let me know.
Html
<%= Html.CheckBox("markedItem" + Model.Id, false)%>
C#
(GuidLength is a const int = 36, Left and Right are our own string extensions)
//Correct checkbox values - pull all the values back from the context that might be from a checkbox. If we can parse a Guid then we assume
//its a checkbox value and attempt to match up the model. This assumes the model will be expecting a dictionary to receive the key and
//boolean value and deals with several sets of checkboxes in the same page
//TODO: Model Validation - I don't think validation will be fired by this. Need to reapply model validation after properties have been set?
Dictionary<string, Dictionary<Guid, bool>> checkBoxItems = new Dictionary<string, Dictionary<Guid, bool>>();
foreach (var item in bindingContext.ValueProvider.Where(k => k.Key.Length > GuidLength))
{
Regex guidRegEx = new Regex(#"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
if (guidRegEx.IsMatch(item.Key.Right(GuidLength)))
{
Guid entityKey = new Guid(item.Key.Right(GuidLength));
string modelKey = item.Key.Left(item.Key.Length - GuidLength);
Dictionary<Guid, bool> checkedValues = null;
if (!checkBoxItems.TryGetValue(modelKey, out checkedValues))
{
checkedValues = new Dictionary<Guid, bool>();
checkBoxItems.Add(modelKey, checkedValues);
}
//The assumption is that we will always get 1 or 2 values. 1 means the contents have not changed, 2 means the contents have changed
//and, so far, the first position has always contained the latest value
checkedValues.Add(entityKey, Convert.ToBoolean(((string[])item.Value.RawValue).First()));
}
}
foreach (var item in checkBoxItems)
{
PropertyInfo info = model.GetType().GetProperty(item.Key,
BindingFlags.IgnoreCase |
BindingFlags.Public |
BindingFlags.Instance);
info.SetValue(model, item.Value, null);
}
I'm trying to do something similar to this post where I don't pull back all columns from a particular entity, however my framework makes use of inheritence and I lose scope of the entity type after it's been cast to an anonymous type.
The structure of my Entity Framework has a base entity called Action. From here I've created two inherited entities called Event and Activity. I want to pull back the last X Actions and pass them to my strongly typed view which accepts an Action and from there determines if its an Activity or Event and renders the correct partial view.
if(Model.GetType() == typeof(Event))
{
//render Event view
}
else if(Model.GetType() == typeof(Activity))
{
//render Activity view
}
I can pull the last 10 as an anonymous type and then cast:
var result = from a in new DataContext().Actions
where a.UserId == someGuidValue
select new { a.CreatedOn, a.Summary };
List<Action> list = result.AsEnumerable()
.Select(o => new Action {
CreatedOn = o.CreatedOn,
Summary = o.Summary
}).ToList();
However, once I pass the new List of Actions to my strongly typed view it loses scope of whether it's an Activity or an Event since it's been cast as an Action. My question is, without exposing the discriminator column, is there any way to cast each item to the proper type or am I going about this the wrong way?
A bit kludgy, but will work:
var result = from a in new DataContext().Actions
where a.UserId == someGuidValue
let IsEvent = a as Event != null
select new { a.CreatedOn, IsEvent, a.Summary };
List<Action> list = result.AsEnumerable()
.Select(o => o.IsEvent ?
(Action) new Event {
CreatedOn = o.CreatedOn,
Summary = o.Summary
}
: (Action) new Activity {
CreatedOn = o.CreatedOn,
Summary = o.Summary
}
}).ToList();
Example with type-specific columns, presuming that e.EventSpecific is of a nullable type.
var result = from a in new DataContext().Actions
where a.UserId == someGuidValue
let ev = a as Event
let IsEvent = ev != null
select new { a.CreatedOn, IsEvent, a.Summary, ev.EventSpecific };
List<Action> list = result.AsEnumerable()
.Select(o => o.IsEvent ?
(Action) new Event {
CreatedOn = o.CreatedOn,
Summary = o.Summary,
EventSpecific = o.EventSpecific
}
: (Action) new Activity {
CreatedOn = o.CreatedOn,
Summary = o.Summary,
EventSpecific = o.EventSpecific // will be null, but using o.EventSpecific saves casting
}
}).ToList();
If o.EventSpecific is of a non-nullable type, then you must convert it to a nullable type in the L2E query.
You are probably on the wrong way. At first I would assume that Action should be an abstract class and you should not be able to create instances of it at all. If you then only fetch a subset of the properties and the subset does no longer allow to discriminate between events and activities, it is probably the wrong way to try making events and activities out of them.
So it actually seems not to be a technical problem - it should be quite easy to include some discrimination information in the anonymous type - but a design problem. I suggest to rethink if it is required to discriminate the query result and if so if it is really a good idea to discriminate the result in absence of an discriminator.