Using #model dynamic in a Partial nested in a subfolder throws RuntimeBinderException - c#

I'm having some problems finding a solution for this, though it seems straight-forward enough, and I imagine someone else must have run into this issue before.
Using MVC/Razor 4, I am trying to render a partial using a dynamic model.
To organize things, I have placed my partials in a sub-folder within the same view folder.
When the partial in question is moved to the sub-folder, it throws a RuntimeBinderException with an exception message saying that 'object' does not contain a definition for 'Id' (the parameter I am trying to access).
This works perfectly fine when the partial is located in the same folder.
This structure works fine
Views/Orders/Details.cshtml
Views/Orders/_PartialWithDynamicModel.cshtml
This structure causes the exception
Views/Orders/Details.cshtml
Views/Orders/MyPartials/_PartialWithDynamicModel.cshtml
CODE
Details.cshtml
#Html.Partial("MyPartials/_PartialWithDynamicModel", new { Id = 54 } )
_PartialWithDynamicModel.cshtml
#model dynamic
# { //The following line throws the RuntimeBinderException
int id = Model.Id; }
Any thoughts? If I move the partial into the same folder as the view, everything works fine.

Your problem is that you can't pass an anonymous type to an object in a separate assembly. They are created as "internal" types, and thus cannot be passed externally. Views are generated dynamically at runtime into their own assemblies.
Instead, use an ExpandoObject, like this:
#{ var myExpando = new ExpandoObject();
myExpando.Id = 54; }
#Html.Partial("MyPartials/_PutOnHoldForm", myExpando)
A better choice, however, would be to just pass a ViewDataDictionary, or perhaps use Tuples.
There is also the DynamicViewPage extension in the MVC futures project that allows you to do this as well without the expand object.
http://weblogs.asp.net/imranbaloch/using-the-features-of-asp-net-mvc-3-futures
(note, it says MVC3, but there is an MVC5 version of futures in Nuget)

Related

How to specify a view component under a sub folder within shared/components

I get an error anytime I try to specify a view component from a razor page which lies anywhere but directly under the folder structure of shared/components. For example, shared/components/UserCRUD/{mycomponent} will not work. See the pictures below:
where I include the view component. Note that I have also tried this with typeof() and nameof(), to which the intellisense correctly identifies the correct location under the sub folder.
My relevant folder structure:
The namespace I am using. Note that I have also tried naming this with the "ViewComponent" suffix to no end.
And finally the exception. The most strange thing is that the exception isn't looking at the path I handed it. It's completely omitting UserCRUD.
The folder setup you have doesn't match with the default view location formats.
Even adjusting those doesn't seem to work - or at least I don't succeed in it.
No combination of fixed values and predefined placeholders ({0} view name, {1} page name) seem to give the expected result; there's something with that Components segment that always get added automatically.
Below does work.
In AdminDashboard/Index.cshtml include that UserTable component as below.
You can also keep your current call since that one seems to work.
#await Component.InvokeAsync("UserTable")
Adjust your UserTable component in UserTable.cs by returning the full path to the corresponding view, being /Areas/Identity/Pages/Shared/Components/UserCRUD/UserTable/UserTable.cshtml.
public class UserTable : ViewComponent
{
public IViewComponentResult Invoke(string module)
=> View("/Areas/Identity/Pages/Shared/Components/UserCRUD/UserTable/UserTable.cshtml");
}
You'll have to do similarly for the other views in that UserCRUD folder.

Use a variable to reference a partial view's location

I am new to asp.net core and am trying to render a partial view in an ASP.Net Core application. The address of the partial view is determined at run time. I have constructed a view model which is parsed into the view from the controller and contains the desired file address.
The following code throws the following compiler error:
#Html.Partial(Model.File);
Error CS1973 'IHtmlHelper' has no applicable method named 'Partial'
but appears to have an extension method by that name. Extension methods
cannot be dynamically dispatched. Consider casting the dynamic arguments or
calling the extension method without the extension method syntax.
Any help would be greatly appreciated.
The solution in the end was to specify the type in the Razor file. The following line worked:
#Html.Partial((string) Model.File)
Just to add to Lachlan Fergusson's excellent answer (thank you!) just to say that you also get this message if the name of your view contains a variable, without it's type.
So, the following line threw the error for me:
#Html.Partial("UserDetailsPartial." + language, Model)
...but it went away when I added this...
#Html.Partial("UserDetailsPartial." + (string)language, Model)
Behind the scenes, I had different partials based on language,
UserDetailsPartial.es.html
UserDetailsPartial.de.html
UserDetailsPartial.fr.html
The strange thing is that previously (with an earlier version of .Net Core?) the original line worked fine.
So, add this to Microsoft's "list of error messages which don't really explain what the problem is.."

ASP MVC has a strange interpreting of model - extension methods cannot be dynamically dispatched

Sorry if this is obvious, but it is very confusing for me. After specifying model type:
#model MyNamespace.MyModel
Which does not display any error and the path to the MyModel is correct, same is the model name, some of the methods seem not to recognize the Model type as follows:
#Html.Partial("_Title", Model)
Which outputs the following error:
extension methods cannot be dynamically dispatched
Which should not be shown, since the Model type is specified. Also if I am trying to cast it again:
#Html.Partial("_Title", (MyNamespace.MyModel)Model)
Resharper is saying that Cast is redundant, but the error goes away.
What could cause this behavior to an MVC view?
Note: I have other views which have the model defined in the very same way and which are using exactly same partial views, but they are working properly.
I have tried deleting the file and recreating and the errors keep coming back.
I'm not sure why you'd need to do that at all. When you call Partial() and don't specify the model, the current model is passed to the Partial :)
The reason this happens is because internally when you pass a model to Partial() MVC copies the current ViewData (including ViewData["Model"]) and passes it to the next partial, if you don't specify the value the current value is used.

Creating partial view with dynamic content

I understand when I create a view, I shouldn't be putting any code in there besides html and the data from the model/controller, which is what I've done so far.
But lets say there is a snipped of dynamically generated html that can be used in multiple views, I'm guessing this would be a partial view that goes in the Shared folder in the project. But since it's a partial view, that has no absolute controller to handle it's propagation of dynamic data (from db), how would I call, and where would I code the propagation of data from the db into the view (or model?), if lets say the partial view was to dynamically render content for table.id=n, etc.
I'm fairly to new and working off a tutorial in .net, trying to figure out how to do this. Anyone know how it's done? Hope the question makes sense.
You can always define a model for the partial.
And you can render the partial from the container view passing a dinamically populated instance of its model:
<!-- index.cshtml -->
<h1>Feed Upload</h1>
<div id="uploader">
#Html.Partial("~/Views/Shared/Controls/_FileUploader.cshtml", new FileUploaderModel() { UploaderClassName = this.Model.UploaderClassName })
</div>
In this simple example I call the partial _FileUploader.cshtml from the index.cshtml using the #Html.Partial() method, passing a new model instance that specifies the UploaderClassName value.
Edit
The this.Model.UploaderClassName refers to the container's model and it is initialized inside the container's controller business. Of course the container's controller can run any data access logic to grab dynamic data from the db and pass them to the partial's model.
Have a look at MSDN, and at this article.
Assuming you are using the razor view engine, you can put an .cshtml file in the App_Code folder with helper functions.
The syntax is like this:
#helper FormatDate(DateTime date)
{
#date.ToShortDateString()
}
You call it like this (assuming the file is Utility.cshtml)
#Utility.FormatDate(Patient.DOB)
Because you can pass parameters to a helper, you can pass any type you need, including complex objects.
I recently published a nuget package to do this very thing. It's called Dynamic MVC.
http://dynamicmvc.com
You can look at the source code on codeplex.
https://dynamicmvc.codeplex.com
The way I did this was to use the ModelMetadata engine built into MVC to allow me get the value for any property in a weakly typed fashion. The ModelMetadata engine originally came from ASP.net Dynamic Data and was ported over to MVC in MVC2. It works great for this kind of situation.

The model item passed into the dictionary is of type 'System.Guid', but this dictionary requires a model item of type 'System.String'

I've looked through so many similar questions here, but none were able to solve our problem or explain why this might be happening.
We created an EDMX from an existing database, and used MVC 4 scaffolding to automatically generate the controller and views for us. The Index page is fine, but when we choose an entity to Edit, this error is thrown. There are no custom editors in our solution, nor is this a partial page update.
The controller code for the edit page is this:
public ActionResult Edit(int id = 0)
{
MessageProfile messageprofile = db.MessageProfiles.Find(id);
if (messageprofile == null)
{
return HttpNotFound();
}
return View(messageprofile);
}
The model declaration on Edit.cshtml is
#model Web.UI.Areas.Admin.Models.MessageProfile
and the error is thrown at
#Html.EditorFor(model => model.APIKey)
The EDMX has APIKey as a GUID, the generated model class has APIKey as a model, and the EditorFor indicates it's an editor for a GUID when we hover over it in the code. We are completely stumped where a String is entering the process. Appreciate any help.
Having workd with ASP.NET Dynamic Data quite often, my expectation was that there would be an editor already present for every primitive type in MVC scaffolding. There is not, and attempts are made to do an implicit conversion. When that fails, this error usually gets thrown.
In this case, the easy solution was to use Html.TextBoxFor instead of Html.EditorFor. It's a weird case, but the value is being pasted in from an email. In other cases, we made the form better with a lookup and dropdownlist.
Had we wanted to have a GUID-specific editor, we would have had to create our own. All we would have ended up with was a textbox anyway, so that's why the first solution was the simplest.
This happened to me as well. The problem for me was that I created custom Editfor templates and I did not include one for System.Guid. Since it could find one to match system.Guid it would try to use System. String thus generating that error.
If this is the case in your EditorTemplates folder add a cshtml called Guid, then add the following:
#model System.Guid
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue)

Categories