Asked  7 Months ago    Answers:  5   Viewed   64 times

I'm getting the following error:

'object' does not contain a definition for 'RatingName'

When you look at the anonymous dynamic type, it clearly does have RatingName.

Screenshot of Error

I realize I can do this with a Tuple, but I would like to understand why the error message occurs.

 Answers

10

Anonymous types having internal properties is a poor .NET framework design decision, in my opinion.

Here is a quick and nice extension to fix this problem i.e. by converting the anonymous object into an ExpandoObject right away.

public static ExpandoObject ToExpando(this object anonymousObject)
{
    IDictionary<string, object> anonymousDictionary =  new RouteValueDictionary(anonymousObject);
    IDictionary<string, object> expando = new ExpandoObject();
    foreach (var item in anonymousDictionary)
        expando.Add(item);
    return (ExpandoObject)expando;
}

It's very easy to use:

return View("ViewName", someLinq.Select(new { x=1, y=2}.ToExpando());

Of course in your view:

@foreach (var item in Model) {
     <div>x = @item.x, y = @item.y</div>
}
Tuesday, June 1, 2021
 
RenegadeAndy
answered 7 Months ago
58

var does not mean "use an anonymous type", it means "Compiler, go figure out the type for me!". In the first three cases, the type is actually a "named" type - System.Int32, System.String, and System.Int32[] (in the last case the type of array's elements is also deduced by the compiler from the type of array elements that you put in the initializer).

The last case is the only one where an anonymous type is used. It is by design that C#'s anonymous types are immutable. The primary case for adding them in the language in the first place has been introduction of LINQ, which does not need mutability in cases when anonymous types are produced. In general, immutable classes tend to give designers less problems, especially when concurrency is involved, so designers of the language decided to go with immutable anonymous types.

Friday, August 13, 2021
 
kmunky
answered 4 Months ago
54

I'm not sure if you mean Html helpers, or razor helpers when you say "helpers" In any case, I only create Html helpers when it's a small, idividual item like a control.

If you mean Razor helpers, then they are different from Partials in that you can call them like functions, passing whatever parameters you want. Partials are largely stuck with the "model" system (and of course Temp/ViewData/Bag.

It's all about how you want to work with the code.

As for your Partial. You have to include the suffix.

@Html.Partial("~/Views/ControllerName/_PartialView.cshtml", Model)
Wednesday, October 6, 2021
 
Vadim Rybak
answered 2 Months ago
19

So what happened here?

Your partial view is weakly typed. You do not have a @model definition for it. So by default it is object which obviously doesn't have an Action property.

The correct way to solve this is to define a view model:

public class MyViewModel
{
    public string Action { get; set; }
}

that your partial view will be strongly typed to:

@model MyViewModel
@{
    string throwsException = Model.Action; 
}

and which will be passed by the main view:

@Html.Partial("_PartialView", new MyViewModel { Action = "Foo" })

Another possibility (which personally I don't like as it relies on runtime binding) is to use a dynamic model in the partial view:

@model dynamic
@{
    string throwsException = Model.Action; 
}

and then you will be able to pass an anonymous object when calling it:

@Html.Partial("_PartialView", new { Action = "Foo" })
Sunday, November 7, 2021
 
BalusC
answered 1 Month ago
76

You could use a custom model binder. Let's take an example.

Model:

public class MyViewModel
{
    public IList<BaseClass> Children { get; set; }
}

public abstract class BaseClass
{
    public int Id { get; set; }

    [HiddenInput(DisplayValue = false)]
    public string ModelType
    {
        get { return GetType().FullName; }
    }
}

public class Derived1 : BaseClass
{
    public string Derived1Property { get; set; }
}

public class Derived2 : BaseClass
{
    public string Derived2Property { get; set; }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            Children = new BaseClass[]
            {
                new Derived1 { Id = 1, Derived1Property = "prop1" },
                new Derived2 { Id = 2, Derived2Property = "prop2" },
            }
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        // everything will be fine and dandy here
        ...
    }
}

View (~/Views/Home/Index.cshtml):

@model MyViewModel

@using (Html.BeginForm())
{
    for (int i = 0; i < Model.Children.Count; i++)
    {
        @Html.EditorFor(x => x.Children[i].ModelType)
        <div>
            @Html.EditorFor(x => x.Children[i].Id)
            @Html.EditorFor(x => x.Children[i])    
        </div>
    }

    <button type="submit">OK</button>
}

Editor template for the Dervied1 type (~/Views/Home/EditorTemplates/Derived1.cshtml):

@model Derived1
@Html.EditorFor(x => x.Derived1Property)

and the editor template for the Dervied2 type (~/Views/Home/EditorTemplates/Derived2.cshtml):

@model Derived2
@Html.EditorFor(x => x.Derived2Property)

Now all that's left is a custom model binder that will use the hidden field value to instantiate the proper type in the collection:

public class BaseClassModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var typeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".ModelType");
        var type = Type.GetType(
            (string)typeValue.ConvertTo(typeof(string)),
            true
        );
        var model = Activator.CreateInstance(type);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
        return model;
    }
}

which will be registered in Application_Start:

ModelBinders.Binders.Add(typeof(BaseClass), new BaseClassModelBinder());
Tuesday, November 9, 2021
 
Karsten
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share