Can I pass variable by reference in order to return value to caller. For example, can I use returnAmount in case below? I haven't seen such expression in samples I have analysed. What problems I will be faced while using it?
I understand that fact, that I can use custom class with required cont of fields in function return when I need to return more than one variable, but anyway I'm interested in fact is it possible to use out like in sample below.
public class MyController : Controller
{
public ActionResult balance( int amount, out int returnAmount )
{
returnAmount = 50;
return Proceed(a.Balance( amount));
}
}
I don't think you can do that. Instead create a ViewModel with that as a property. Update the viewmodel with whatever data you want. Then return the view and bind everything on the view through razor. If you don't want the page to completely reload, you'll have to use AJAX or a partial.
return View("viewname", viewmodel);
Related
In a view model's constructor I have a command declaration that calls a method:
OpenGroupCommand = new DelegateCommand(OnOpenGroupExecute);
And the method looks like:
private void OnOpenGroupExecute(object obj)
{
string groupName = (string)obj;
Application.Current.MainPage.Navigation.PushAsync(new GroupPage(groupName));
}
How can I test, that groupName is passed to another view model correctly? In another view model groupName parameter is sent to GroupName property on VM instance:
public class GroupPageViewModel : ViewModelBase, IGroupPageViewModel
{
private string _groupName;
public GroupPageViewModel(string groupName)
{
LoadGroupName(groupName);
}
public void LoadGroupName(string groupName)
{
GroupName = groupName;
}
public string GroupName
{
get
{
return _groupName;
}
set
{
_groupName = value;
OnPropertyChanged();
}
}
}
On debug all works fine, but how can I unit test it? Where can I read a bit about testing and mocking stuff like this, even with Moq framework?
I believe your question is actually about how to test navigation between pages.
In the implementation of method OnOpenGroupExecute, because you are using Xamarin forms stuff to implement the navigation, you have to refer Xamarin Forms assemblies in your test project which makes the unit test depend on Xamarin Forms.
As suggested in this document https://learn.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/ , try to create an interface for navigation and navigate with viewmodel (more details on https://github.com/dotnet-architecture/eShopOnContainers)
And in your unit test project, implement a fake navigation service class like below and inject into the DI container:
public class FakeNavigationService : INavigationService //this interface is from MS eShopOnContainer project
{
private List<ViewModelBase> _viewModels = new List<ViewModel>();
public Task NavigateToAsync<TViewModel>() where TViewModel : ViewModelBase {
//create viewModel object from DI container
//var viewModel = ......
_viewModels.Add(viewModel);
}
public ViewModelBase CurrentPageViewModel {
get {
if (_viewModels.Count() < 1) {
return null;
}
return _viewModels[_viewModels.Count() - 1];
}
}
}
This is just a suggestion. If you have implemented most of features in your app, it takes time to change navigate-with-page to navigate-with-viewmodel.
Well, let's see what you have:
you have some code in a private method, unless you make that public you won't be able to test it directly, because you can't call it. I am not considering here any tricks that allow you to call private methods.
what does that method do? It is not clear at all, it receives an object, we don't know what's in it. You're converting it to string, but what if it is not a string? Can you convert that object to a string? who knows.
So we have a method, that we don't know what it does, we don't know what it receives as parameters, we can't call it directly, but we want to test it. This is not a good position to be in.
Step back a bit and ask yourself, what are you really trying to test?
You said : How can I test, that groupName is passed to another view model correctly?
what does "correctly" mean? You need to define what it means for that string to be correct. This will give a test scenario you can work with.
I expect to receive an object, which looks like A and I want to convert it to a string which looks like B. Forget about viewmodels for now, that's just unimportant noise.
You can change the method into a public one and you can test that for different types of input data, you're getting the right result. This is literally, working with an object and extract some stuff from it. When that method is correct, you can guarantee that the viewmodel will receive the right input and that is good enough from a unit testing point of view.
You can of course add more tests for various inputs, you can test for correct failure conditions etc.
public ActionResult AddComplianceForm(string TemplateName)
{
}
In this ASP.net MVC5 application there is a folder Templates that contains a bunch of different classes that all have different TemplateName attributes. The first part of this method needs to find the class that has a TemplateName matching the string passed in. I then need to create an instance of whatever template matched. I am very new to working with attributes in C# so help would be very appreciated. I mostly need to know how to access that folder of classes in the program to look into it.
What you are trying to do is called "Reflection" in C#.
Below is the link to another answer that shows how to get all the classes in a namespace (I'm assuming that the physical folder implies the use of a unique namespace for the classes contained in the folder.)
Link to StackOverflow answer
**Btw you should look up reflection performance and see if it makes sense in your case. You may want to use a factory pattern instead.
This will work:
public class HomeController : Controller
{
public ActionResult AddComplianceForm(string TemplateName)
{
Assembly assembly = Assembly.Load("Testy20161006"); //assembly name
Type t = assembly.GetType("Testy20161006.Templates." + TemplateName); //namespace + class name
Object obj = (Object)Activator.CreateInstance(t);
return View();
}
Don't just use reflection
The typical way someone would deal with this is with Reflection and run-time type discovery/binding. However, this is probably a poor starting point in this exact situation. The template name is passed in as an action argument, presumably through binding to a value in the request string, and you don't want c# code that will instantiate whatever class is passed in from the web!!! That would be a serious security issue known as an insecure direct object reference.
Create a list
To mitigate the risk, the proper approach is to check the argument against a whitelist. Well, if we have a white list already, we may as well associate each item in the list with a lambda expression that returns the object you want.
class MyController
{
static private readonly Dictionary<string,Func<BaseTemplate>> _templateList = new Dictionary<string,Func<BaseTemplate>>();
static MyController()
{
_templateList.Add("ATemplate", () => return new ATemplate());
_templateList.Add("SomeOtherTemplate", () => return new SomeOtherTemplate());
_templateList.Add("JustOneMore", () => return new JustOneMore());
}
public ActionResult AddComplianceForm(string TemplateName)
{
BaseTemplate template;
try
{
template = _templateList[TemplateName]();
}
catch (KeyNotFoundException exception)
{
RedirectToAction("MyController", "InvalidTemplateError");
}
DoSomethingWithTemplate(template);
}
}
Create the list using Reflection
But what if you have a crap ton of templates? You don't want to hard code all those dictionary entries, right?
Well, you could tag each of the templates with a custom attribute, e.g. [TemplateAttribute], and then populate the list this way:
foreach (Assembly b in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type t in b.GetTypes())
{
var a = Attribute.GetCustomAttribute(t, typeof(TemplateAttribute));
if (a != null)
{
var localType = t; //Avoid closure on loop variable
_templateList.Add(t.Name, () => Activator.CreateInstance(localType) as BaseTemplate);
}
}
}
This will automatically iterate through all the types that are loaded for your application and find the ones with the TemplateAttribute. Only those types will be allowed in the action argument.
Notes:
In these examples I assume all of your templates inherit from a BaseTemplate, but if they have no ancestor in common (not recommended) I guess you could just use object.
In these examples, I store the list and implement the code in the controller, but if you are going for well-structured code you should consider moving all that stuff into some sort of factory class and just pass the string in from the controller.
The goal
Change the model depending on what it is expecting.
The problem
I have a method in my ProductsControllercalled Category. When someone request this method, two things can happen: if the parameter passed to the method is different than Daily-Offers, then one type of list is passed to View. If the parameter passed to the method is equal to Daily-Offers, then another type of list is passed to View.
To better comprehension, see:
[HttpGet]
public ActionResult Category(string categoryName = null)
{
int? categoryId = categoryName != "Daily-Offers" ?
Convert.ToInt32(Regex.Match(categoryName, #"\d+").Value) :
(int?)null;
if (categoryName == "Daily-Offers")
{
var productsList = Products.BuildOffersList();
ViewBag.Title = String.Format("Today's deal: ({0})", DateTime.Now);
ViewBag.CategoryProductsQuantity = productsList.Count;
ViewBag.CurrentCategory = "Daily-Offers";
return View(productsList);
}
else if (Regex.Match(categoryName, #"\d+").Success &&
String.Format("{0}-{1}",
categoryId,
CommodityHelpers.UppercaseFirst
(CommodityHelpers.GenerateSlug
(Categories.GetDetails((sbyte)categoryId).Category_Name)))
== categoryName)
{
ViewBag.Title = Categories.GetDetails((sbyte)categoryId).Category_Name;
ViewBag.CategoryProductsQuantity =
Categories.GetDetails((sbyte)categoryId).Category_Products_Quantity;
ViewBag.CurrentCategory =
CommodityHelpers.UppercaseFirst(CommodityHelpers.GenerateSlug
(Categories.GetDetails((sbyte)categoryId).Category_Name));
return View(Products.BuildListForHome(categoryId, null));
}
else
{
return View("404");
}
}
As you can see, the view should be prepared to receive IList<Offers> or/and IList<NormalProducts> — and I do not know how to do this.
What I have already tried
Something like this:
#model if(IEnumerable<BluMercados.Models.Data.getProductsListForHome_Result>)
?: (IEnumerable<BluMercados.Models.Data.getProductsInOfferList_Result>);
But, of course, no success. Just to illustrate.
A view should really only have one model, so trying to make a view consume two different models, while doable, shouldn't be done.
Instead, you can create different views. Say you create a new view for DailyOffers, which takes IEnumerable<Offers> as its model.
You can then user an overload of View() to specify which view to return:
return View("DailyOffers", productsList);
However, instead of doing this, would it make more sense to redirect to a different action in the case of DailyOffers? So, you have a new action:
public ActionResult DailyOffers(...)
and instead of return View(productsList) you do:
return RedirectToAction("DailyOffers");
This all assumes that the models are sufficiently different from one another. If they are similar, using the Interface solution as suggested by p.s.w.g. would make more sense.
I don't think you can do that, and even if you could, it wouldn't be good practice. If the view for each model is very similar, I'd recommend creating an common interface for Offers and NormalProducts
public interface IProduct
{
// common properties ...
}
public class Offer : IProduct
{
// ...
}
public class NormalProduct : IProduct
{
// ...
}
And then in your view use
#model IEnumerable<IProduct>
If your view for each model is significantly different, I'd strongly recommend splitting these into different views, and deciding between them in controller:
if (categoryName == "Daily-Offers")
{
...
return View("Category_Offers", productsList);
}
else if (...)
{
...
return View("Category_NormalProducts", Products.BuildListForHome(categoryId, null));
}
And then in your view use
// Category_Offers.cshtml
#model IEnumerable<Offer>
// Category_NormalProducts.cshtml
#model IEnumerable<NormalProduct>
Here's the relevant part of my Index view (Index.cshtml):
#foreach (var item in Model) {
<li>
#Html.ActionLink(item.name, "Index", "Filler", new { cap = item }, null)
</li>
}
As you can see, the ActionLink is tied to the Index action on the Filler Controller, and is passing in the entire item (the model)- "item" is of type "capsule".
Now, on my Filler Controller, in the Index action:
public ActionResult Index(capsule cap)
{
var fillers = db.fillers.ToList();
return View(fillers);
}
The capsule class that was automatically generated by Entity Framework is:
namespace CapWorx.Models
{
using System;
using System.Collections.Generic;
public partial class capsule
{
public capsule()
{
this.fillers = new HashSet<filler>();
}
public int pk { get; set; }
public string name { get; set; }
public virtual ICollection<filler> fillers { get; set; }
}
}
The problem is "cap" is NULL in the above Index action. But, if I change the type to "object" instead of "capsule", I do get some weird non-null data, but I can't cast the object to "capsule". Does anyone know why this is NULL?
Thanks,
Mike
You usually just have to pass in the id to the action. For example, can you refactor your code so that it can take in a capsuleId, get the capsule from db and do whatever processing is needed. Adding the entire object to route values in ActionLink doesn't make any sense. Have a look at the link being generated. It is probably just something like ...?cap=Namespace.Capsule as the object would have be ToStringed
The first problem is in MVC you can't bind to an interface (ICollection). You'll need to change it to a List - List<filler>. The second problem you will face is that Lists/Arrays need to be represented in array notation for proper posting, something like name="books[0].book_id". Even though MVC does a lot of magic, the model in your link still has to be represented as a query string eventually.
Depending on what you are trying to do, you may be better off representing your model as a JSON object and posting with .ajax().
See this SO post for other ideas - Need help with binding Set with Spring MVC form
I'm not totally sure why this would work(I think you're nulling out the html attributes), but try to remove the "null" part of the actionlink.
Or, the controller which created the models is wrong.
Again, don't kill me for this.
#Html.ActionLink just generates an anchor element (...), so it makes no sense to attempt to bind a complete object to the routeValues parameter. As #manojlds says, it make much more sense to just pass the relevent key value, since you'll be performing the lookup then anyway (remember, the web is "stateless").
I have an MVC2 Application that uses MVVM pattern. I am trying use Data Annotations to validate form input.
In my ThingsController I have two methods:
[HttpGet]
public ActionResult Index()
{
return View();
}
public ActionResult Details(ThingsViewModel tvm)
{
if (!ModelState.IsValid) return View(tvm);
try
{
Query q = new Query(tvm.Query);
ThingRepository repository = new ThingRepository(q);
tvm.Things = repository.All();
return View(tvm);
}
catch (Exception)
{
return View();
}
}
My Details.aspx view is strongly typed to the ThingsViewModel:
<%# Page Title=""
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<Config.Web.Models.ThingsViewModel>" %>
The ViewModel is a class consisting of a IList of returned Thing objects and the Query string (which is submitted on the form) and has the Required data annotation:
public class ThingsViewModel
{
public IList<Thing> Things{ get; set; }
[Required(ErrorMessage="You must enter a query")]
public string Query { get; set; }
}
When I run this, and click the submit button on the form without entering a value I get a YSOD with the following error:
The model item passed into the dictionary is of type
'Config.Web.Models.ThingsViewModel', but this dictionary
requires a model item of type
System.Collections.Generic.IEnumerable`1[Config.Domain.Entities.Thing]'.
How can I get Data Annotations to work with a ViewModel? I cannot see what I'm missing or where I'm going wrong - the VM was working just fine before I started mucking around with validation.
I don't think the problem is with the validation.
Change this line;
tvm.Things = repository.All(); //Is this the Linq extension method 'All()'?
to this
tvm.Things = repository.ToList();
I don't know what this is or what it does;
new ThingRepository(q);
It takes a string parameter and returns some kind of Linq IQueriable or List? If that's returning something else it could be causing the problem.
Do you have client-side validation enabled? It might even be a quick hacky-fix, but regarding the error message - it's tough to say without extra info. Could you post your View and the rendered Html?
What does your route for Details look like?
If you set a breakpoint at the start of the Details method, does it get hit when you click on the submit button?
It looks like you could just declare your ThingsViewModel like so:
public class ThingsViewModel: IEnumerable<Thing>
and then implement the interface as appropriate to access the Things list.
I think that ASP.NET MVC might be trying to map your view to the wrong controller. When you return the view you might need to specify the view file name you're trying to use.
return View("ViewName")