I have a question, that I tried to Google but honestly, I don't really know how to search or even ask this particular question.
Let's imagine I have the following:
Controller
[HttpGet]
public virtual ActionResult Summary()
{
var summaryViewModel = new CheckoutSummaryViewModel()
{
Products = ProductViewModel.BuildListFromShoppingCart(GetShoppingCart())
};
return View("_Summary", summaryViewModel);
}
ProductViewModel
public class ProductViewModel
{
public string Name
{
get; set;
}
public static List<ProdutoCheckoutViewModel> BuildListFromShoppingCart(ShoppingCart shoppingCart, IMappingService mappingService)
{
var itemsInCart = new List<ProductViewModel>();
foreach (var item in shoppingCart.ItemsInCart)
{
var itemViewModel = mappingService.Map<Product, ProductViewModel>(item.Product);
itemViewModel.Quantidade = item.Quantity;
itemsInCart.Add(itemViewModel);
}
return itemsInCart;
}
}
This is not production code. Is just so I can explain what I mean.
Is this the correct way of doing this?
Is there a better way than using static for building the list? I really don't want to do it inside the controller.
Passing IMappingService to the method does not look right. But maybe I'm just being picky. Is it the best way?
Another case, where I need to pass Session State to a static helper class.
public static Guid GetCheckoutId(HttpSessionStateBase session)
{
return (Guid)session["checkoutId"];
}
Or, also, sometimes I need to pass as parameter, to helper methods, my "unifOfWork", since I use the repository pattern.
I've come accross this "problem" a lot and I did not find the best way, yet, to do it.
PS: If any of you has a better title for this question, please tell me so I can update it.
Controller
If you use DI, it would look something like this:
public class CheckoutController
{
private readonly ICheckoutService _checkoutService;
public CheckoutController(ICheckoutService checkoutService) =>
_checkoutService = checkoutService;
[HttpGet]
public virtual ActionResult Summary()
{
var shoppingCartData = _checkoutService.GetShoppingCart(Session["..."]);
// The viewmodel here could be returned by your service or the service
// would return all required data and the viewmodel simply transforms that Dto into what is needed by the UI
var summaryViewModel = new CheckoutSummaryViewModel()
{
Products = shoppingCartData
};
return View("_Summary", summaryViewModel);
}
}
Related
I am trying to define a Controller Action in ASP.NET Core 2.2.
The tricky part is that I prefer this to be a GET endpoint, and the data that it must recieve is a collection of custom objects. Here is my sample code:
[Route("api/example")]
[ApiController]
public class ExampleController : ControllerBase
{
[HttpGet("getData")]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
public async Task<IActionResult> GetMarketData([FromQuery] MyObject[] queryData)
{
return this.Ok(0);
}
}
public class MyObject
{
public int A { get; set; }
public int B { get; set; }
}
I am expecting this to bind to something like
http://localhost/api/example/getData/?queryData=[A=1,B=1],queryData=[A=2,B=2]
However I can't seem to get it to work.
(Sending a request to the URL, does not parse the objects, and I receive an empty array in my controller)
I'm not sure if this is the best way to approach this, and maybe I need to change the place where I bind the data from?
The only thing I care about is being able to recieve an array (or some kind of a collection) of MyObject that I can process and return a response. I would also prefer for this to be a GET request, as, after all, I am trying to query this API and get data from it.
I know I can get it to work with using a [FromBody] attribute, but as far as I know GET requests should not use the body.
Any help is gladly appreciated.
Your GET request must be constructed as follows:
GET: /api/example/getData?queryData[0].A=1&queryData[0].B=2&queryData[1].A=3
Very similar to model binding when using <form>s :)
Your QueryString should look like:
/TestMe?queryData[0].A=1&queryData[0].B=1&queryData[1].A=2&queryData[1].B=2
If your code looks like:
public class MyObject
{
public int A { get; set; }
public int B { get; set; }
}
[Route("/TestMe")]
public IActionResult TestMe([FromQuery] MyObject[] queryData)
{
return Json(queryData);
}
Note that [FromQuery] isn't even required.
it's not going to work since there's not a default binder for collection types, you'd have to use a custom binder. I made my own implementation of a generic array model binder, here it goes:
// generic array model binder
public class ArrayModelBinder<TType> : IModelBinder {
public Task BindModelAsync(ModelBindingContext bindingContext) {
if (bindingContext.ModelMetadata.IsEnumerableType) {
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ToString();
if (!string.IsNullOrEmpty(value)) {
var elementType = typeof(TType);
var typeConverter = TypeDescriptor.GetConverter(elementType);
var splittedValues = value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
var values = splittedValues.Select(t => typeConverter.ConvertFromString(t.Trim())).ToArray();
var typedValues = Array.CreateInstance(elementType, values.Length);
values.CopyTo(typedValues, 0);
bindingContext.Model = typedValues;
return SuccessBinding(bindingContext, typedValues);
}
return SuccessBinding(bindingContext, null);
}
return FailedBinding(bindingContext);
}
private static Task SuccessBinding(ModelBindingContext bindingContext, Array typedValues) {
bindingContext.Result = ModelBindingResult.Success(typedValues);
return Task.CompletedTask;
}
private static Task FailedBinding(ModelBindingContext bindingContext) {
bindingContext.Result = ModelBindingResult.Failed();
return Task.CompletedTask;
}
To use it on your Action you'll just have to use this piece of code:
public async Task<IActionResult> GetMarketData([ModelBinder(BinderType = typeof(ArrayModelBinder<object>))] MyObject[] queryData)
{
return this.Ok(0);
}
I have the source of this implementation and other things in a repo of my own Library feel free to check it out CcLibrary.AspNetCore
Hey all new to the MVC world so I'm sure I am not doing something correct here. I am trying to call a function from the model page from my view index page to populate some tooltips.
In my model:
public class tips
{
public List<string> allTips()
{
List<string> theTips = new List<string>();
theTips.Add("example 1");
theTips.Add("example 2");
return theTips;
}
private List<string> _tips;
public List<string> getTips { get { return _tips; } set { _tips = allTips(); } }
}
And in my view:
public ActionResult Index()
{
var blah = new tips().getTips;
ViewBag.pageTips = blah;
return getMainData();
}
And then I have this on the razor page:
#Html.Raw(ViewBag.pageTips[1])
Which should display example 2 on the page but instead it just displays null as the value for the toolTips.
Currently it has a value of null when it gets to the return for the pageTips in my view.
So what would I be doing incorrectly? I put some stops here and there and notice that it never calls the allTips() function so that's a good starting place as to what I need to do in order to do that.
I just figured that calling the .getTips would fire off the theTips function?
You seem to be confusing constructors with properties in your model, which is very strange. I suspect you want something like this instead:
public class TipsObj
{
// a property
public List<string> Tips { get; set; }
// a constructor
public TipsObj()
{
Tips = new List<string>();
Tips.Add("example 1");
Tips.Add("example 2");
}
}
The idea here is that the constructor is called by default when you create a new instance of the object, so your Tips property will automatically be populated right away just by creating an instance of this.
Then in your controller action you can simply do this:
var tips = new TipsObj();
ViewBag.pageTips = tips.Tips;
Which will create an instance of the TipsObj and set a ViewBag property to the Tips property of that instance. Which was initialized in the constructor of that object.
Note that none of this really has anything to do with MVC, this is just creating an object in C# and using a property on that object.
Note also that I changed some of the names here. In C# class names should begin with a capital letter. Also, you don't want to call everything "tips" (and you don't want to call anything "blah"). Using meaningful and intuitive names makes your code easier to understand, which will help you understand what you're writing.
You are misunderstanding the concept of setter and using it as an "initializer", a setter is meant to set the value, to change it in other word. if you want to initialize it do it in the constructor.
Here you are using two different Lists, I don't really know why.
A working code would be:
public class tips
{
public tips()
{
_tips = new List<string>();
_tips.Add("example 1");
_tips.Add("example 2");
}
private List<string> _tips;
public List<string> getTips { get { return _tips; } set { _tips = value; } }
}
Then in the controller:
ViewBag.pageTips = new tips().getTips;
Then call it this way in the view:
#Html.Raw(ViewBag.pageTips[1])
Change your model like the following:
public class Tips
{
private List<string> _tips {get; set;}
public Tips()
{
_tips = new List<string>();
_tips.Add("example 1");
_tips.Add("example 2");
}
public List<string> getTips()
{
return _tips;
}
}
Then use it :
ViewBag.pageTips = new Tips().getTips();
There are a few things wrong here, but I'll start with answering your question directly... the reason ViewBag.pageTips[1] is null is because you never initialize the _tips array in your model. The only code that would do that is in the getTips property's setter, which is never invoked. If you attach a debugger it'll be apparent :)
You could refactor this in a few ways, including changing the allTips() method to a constructor, and initializing your collection there.
I don't want to put an example of that directly though. You mentioned you were new to MVC, so I want to show you how this should really be done. One of the big benefits of MVC is Model Binding, so forget about ViewBag.
Try this instead:
The Model
using System.Collections.Generic;
namespace WebApplication1.Models
{
public class TipsModel
{
public List<string> Tips { get; }
public TipsModel()
{
Tips = new List<string> {"example 1", "example 2"};
}
}
}
The Controller
public ActionResult Index()
{
var model = new TipsModel();
return View(model);
}
The View
#model WebApplication1.Models.TipsModel
#{
ViewBag.Title = "Index";
}
#Model.Tips[1]
I know this question has been asked several times and answered, but none of the solutions are working for me.
This is my ViewModel:
public class FlightSearchResults
{
public FlightSearch SearchModel { get; set; }
public List<vwLowFareSearchResults> SearchResults { get; set; }
public string TestString { get; set; }
public DateTime TestDate { get; set; }
}
I am using a RedirectToAction like this:
FlightSearchResults flightSearchResults = new FlightSearchResults();
flightSearchResults.SearchModel = model;
flightSearchResults.SearchResults = flights;
flightSearchResults.TestDate = DateTime.Now.AddDays(-2);
flightSearchResults.TestString = "Just Testing . . .";
return RedirectToAction("index", "flights", flightSearchResults);
I am only getting this List<vwLowFareSearchResults> SearchResults property in my flights index, none of the others are having values assigned. I have tried several variations from some threads on StackOverFlow like:
return RedirectToAction("index", "flights", new { SearchResults = flights, SearchModel = model });
return RedirectToAction("Result", "Dialog", new RouteValueDictionary(flightSearchResults));
I can return the view like this:
return View("~/Views/Flights/Index.cshtml", flightSearchResults);
but this is not a good solution as the url is not updated. I am modifying some older projects and it's mess of using Session and Viewbag.
I need to simplify and the pattern of view and controller communication of data in the previous code is a mess. Is it possible to do this without using the ViewData or Viewbag in a simple RedirectToAction.
Any kind of help in achieving this will be great as I am new to MVC.
Here is an approach I used recently. Try:-
... Previous code omitted.
//In your controller action, save the data to TempData...
TempData["FlightSearchResults"] = FlightSearchResults;
return RedirectToAction("flights");
}
public ActionResult flights()
{
FlightSearchResults flightResults = TempData["FlightSearchResults"];
return View(flightResults);
}
I actually used NewtonSoft to serialise the objects to a string, and then back again in the target action. So you might want to try something like ...
using Newtonsoft.Json;
...
...
... Previous code omitted.
//In your controller action, save the data to TempData...
TempData["FlightSearchResults"] = JsonConvert.SerializeObject(FlightSearchResults);
return RedirectToAction("flights");
}
public ActionResult flights()
{
string storedResults = TempData["FlightSearchResults"].ToString();
FlightSearchResults flightResults = JsonConvert.DeserializeObject<FlightSearchResults>(storedResults);
return View(flightResults);
}
Update
I've managed to create something that is satisfactory. You can see the relevant parts of the code here on PasteBin. If there is there something that I could improve please let me know. I've got a nagging feeling this isn't very efficient.
Clarification
While it certainly seems to work with static dependencies as suggested by d_z, I was hoping, to avoid instantiating objects not utlilized, that I could use something similar to this:
public class HomeController : Controller
{
[Dependency]
protected IBaseData ActionData { get; set; }
public ActionResult Index()
{
return View(ActionData);
}
public ActionResult About()
{
return View(ActionData);
}
}
The data in the IndexData and AboutData instances in reality aren't static. The instance properties are set with data from a database. The DbProvider is injected into these classes.
In the end what I want is to minimize the memory footprint and database accesses.
Original
Let's say we have the following basic controller (with corresponding views):
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}
We also have two classes with the same interface:
public class IndexData : IBaseData
{
public string Name { get; set; }
public string Index { get; set; }
public IndexData()
{
Name = "Index";
Index = "This is an Index instance";
}
}
public class AboutData : IBaseData
{
public string Name { get; set; }
public string About { get; set; }
public AboutData()
{
Name = "About";
About = "This is an About instance";
}
}
What I'd like is for Unity to inject an instance of one of these classes, depending on which action is executed, into the corresponding view. I've tried to get my head around how to achieve this but I'm stumped. Is this even possible?
What you are requesting makes little sense. Dependency Injection is about injecting (design time) behavior (a.k.a. services). What you are trying to do however is to inject runtime data. So this is not a task that yout IoC container should handle.
Next the view should be completely ignorant of any dependency injection. The controller should return all data that the view needs from its action method. Make sure that your About and Index action methods return the proper instance.
To register several mappings for a type in Unity you have to create named registration like this:
myContainer.RegisterType<IBaseData, IndexData>("Index");
myContainer.RegisterType<IBaseData, AboutData>("About");
So after this in your actions you can resolve an instance accordingly:
Index:
IBaseData data = myContainer.Resolve<IBaseData>("Index");
About:
IBaseData data = myContainer.Resolve<IBaseData>("About");
Or for static dependencies it works like this:
[Dependency("Index")]
IBaseData data { get; set; }
Take a look here and here for details
Lets imaging the we have model:
public class InheritModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string OtherData { get; set; }
}
We have a controller with View, that represents this model:
private InheritModel GetAll()
{
return new InheritModel
{
Name = "name1",
Description = "decs 1",
OtherData = "other"
};
}
public ActionResult Index()
{
return View(GetAll());
}
Now we can edit this in View, change some data and post in back to server:
[HttpPost]
public ActionResult Index(InheritModel model)
{
var merged = new MergeModel();
return View(merged.Merge(model, GetAll()));
}
What i need to do:
In view we have a reproduction of model
User change something and post
Merge method need to compare field-by-field posted model and previous model
Merge method create a new InheritModel with data that was changed in posted model, all other data should be null
Can somebody help me to make this Merge method?
UPDATE(!)
It's not a trivial task. Approaching like:
public InheritModel Merge(InheritModel current, InheritModel orig)
{
var result = new InheritModel();
if (current.Id != orig.Id)
{
result.Id = current.Id;
}
}
Not applicable. It's should be Generic solution. We have more than 200 properties in the model. And the first model is built from severeal tables from DB.
public InheritModel Merge(InheritModel current, InheritModel orig)
{
var result = new InheritModel();
if (current.Id != orig.Id)
{
result.Id = current.Id;
}
if (current.Name != orig.Name)
{
result.Name = current.Name;
}
... for the other properties
return result;
}
Another possibility is to use reflection and loop through all properties and set their values:
public InheritModel Merge(InheritModel current, InheritModel orig)
{
var result = new InheritModel();
var properties = TypeDescriptor.GetProperties(typeof(InheritModel));
foreach (PropertyDescriptor property in properties)
{
var currentValue = property.GetValue(current);
if (currentValue != property.GetValue(orig))
{
property.SetValue(result, currentValue);
}
}
return result;
}
Obviously this works only for 1 level nesting of properties.
Per topic, it seems that what you want is a sort of "change tracking" mechanism which is definitely not trivial or simple by any means. Probably, it makes sense to use any modern ORM solution to do that for you, does it?
Because otherwise you need to develop something that maintains the "context" (the 1st level object cache) like EF's ObjectContext or NH's Session that would be generic solution.
Also, there is no information on what happens at the lower level - how do you actualy save the data. Do you already have some mechanism that saves the object by traversing it's "non-null" properties?
I have a similar project experience, which made me thought a lot about the original design. Think the following question:
You have a view that representing a model, then users modified
something of the model in the view, all the CHANGES are posted to
server and the model is modified, and then it's saved to database
probably. What's posted to the server on earth?
An instance of InheritModel? No. You want the changes only. It's actually part of InheritModel, it's a InheritModel Updater, it's an instance of Updater<InheritModel>. And in your question you need to merge two models, because your Update method looks like:
public InheritModel Update(InheritedModel newModel)
{
//assign the properties of the newModel to the old, and save it to db
//return the latest version of the InheritedModel
}
Now ask yourself: why do I need a whole instance of InheritedModel when I just want to update one property only?
So my final solution is: posting the changes to the controller, the argument is something like a Updater<TModel>, not TModel itself. And the Updater<TModel> can be applied to a TModel, the properties metioned in the updater is assigned and saved. There shouldn't a MERGE operation.