0

I'd want to achieve something like that:

public IActionResult Test(string id)
{
    (...)

    var user = context.Users.First(x => x.Id.ToString() == id);
    var message = "abc";

    return View("Edit", user, message);
}

Is that possible without creating ViewModel?

4
  • google ViewBag Commented Aug 8, 2018 at 19:10
  • 1
    I mean, you should just be using ViewModels Commented Aug 8, 2018 at 19:12
  • @maccettura I mean - yea, viewmodels are ok, but I think that's overkill just for one additional string. Commented Aug 8, 2018 at 19:17
  • @Xiaox it is not overkill though. Every View that requires data/model should have a ViewModel. You shouldn't use your entities / data models in the View. Build up a layer of abstraction and segregation. You will thank yourself later Commented Aug 8, 2018 at 19:18

5 Answers 5

2

I completely agree with the advice around using view models where possible. They are much easier to work with, even if they can seem overkill at times.

That said, to answer your question, you could use a Tuple for this:

public IActionResult Test(string id)
{
    var user = context.Users.First(x => x.Id.ToString() == id);
    var message = "abc";

    // The generic types will be inferred here so they're not necessary
    // but I've added them for clarity in the example.
    var viewModel = Tuple.Create<User, string>(user, message);

    return View("Edit", viewModel);
}

Then in the view, you'll need this:

@model Tuple<User, string>

Just make sure to add the correct namespace to the User type in the view.

Edit

For completeness, here's an example of using a ValueTuple from C# 7, following Adam's comment below:

public IActionResult Test(string id)
{
    var user = context.Users.First(x => x.Id.ToString() == id);
    var message = "abc";

    return View("Edit", (user, message));
}

If you're using .NET Core 2.1 and above, the view's model directive becomes:

@model (User user, string message)

You'd then access those like so:

@Model.user

Otherwise, you'll need to use (credit to @AdamSimon from the comments):

@model ValueTuple<User, string>
var (user, message) = Model;

See the What's New in C# 7.0 blog post on MSDN for more information.

Sign up to request clarification or add additional context in comments.

7 Comments

Speaking of ASP.NET Core, I'd prefer ValueTuples.
@AdamSimon That's a fair point. I'll edit an example in now. Thanks for the suggestion.
@JohnH I'm afraid @model (User user, string message) won't work. Razor crashed for me. However, @model ValueTuple<User, string> is ok, then e.g. you can decompose the tuple like this: var (user, message) = Model;
@JohnH Ok, I've done some digging. It works fine in ASP.NET Core 2.1 but crashes in 2.0. In the latter case, C#7.1 is the highest language version supported by Razor and that seems not enough. At least, I found no way to make it work. The bottom line is: tuple syntax will only work in 2.1 and above.
@AdamSimon FWIW, that's likely because ValueTuple used to require a NuGet and a using in previous versions of .NET Core. If you have the NuGet and add the using to _ViewImports.cshtml, it might work in ASP.NET Core 2.0, as well.
|
1

You can use the ViewBag but be aware that its contents are not strongly typed.

Controller

public IActionResult Test(string id)
{
    (...)

    ViewBag.User = context.Users.First(x => x.Id.ToString() == id);
    ViewBag.Message = "abc";

    return View("Edit");
}

View

@{
    User user = ViewBag.User as User; 
    string message = ViewBag.Message as string;
}

Comments

0

Try using anonymous objects:

public IActionResult Test(string id)
{
    (...)

    var user = context.Users.First(x => x.Id.ToString() == id);
    var message = "abc";

    return View("Edit", new {User = user, Message = message});
}

2 Comments

This isn't a good idea. You should use strongly typed objects whenever possible.
I am aware of that, but that wasn't the question.
0

When adding one additional string field, you can use ViewBag.

public IActionResult Test(string id)
{
    (...)

    var user = context.Users.First(x => x.Id.ToString() == id);
    ViewBag.message = "abc";

    return View("Edit", user);
}

I agree with others here, if it gets much more complex a viewmodel will save time down the road.

1 Comment

wow. I tried that but probably f... up something @ View :( Anyway thanks.
0

Anonymous objects won't really work as you'd have to use reflection in the view to access its properties.

Your options:

  • Tuples

    • Action: return View("Edit", Tuple.Create(user, message));
    • View:

      @model Tuple<User, string>
      // accessing model data:
      @{ 
         var user = Model.Item1;
         var message = Model.Item2; 
      }
      
  • Value tuples

    • Action: return View("Edit", (user, message));
    • View:

      @model ValueTuple<User, string>
      // accessing model data:
      @{ 
          var (user, message) = Model; 
      }
      
  • ViewBag/ViewData

    • Action:

      ViewData["user"] = user; // or: ViewBag.user = user;
      ViewData["message"] = message; // or: ViewBag.message = message;
      return View("Edit");
      
    • View:

      // accessing model data:
      @{
          var user = (User)ViewData["user"]; // or: User user = ViewBag.user;
          var message = (string)ViewData["message "]; // or: string message = ViewBag.message ;
      }
      

However, in the end you'll usually realize you're better off creating a separate viewmodel class. ;)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.