I am newer to MVC3 and have a controller with one action.I defined some global properties in controller class and assigned values to those properties in action method.
ex:
public class RosterController : Controller
{
int var1;
int var2;
int var3;
public ActionResult Index(int param1)
{
if(param1 ==1)
{
return view(newRosterViewModel(var1+1,var2+2,var3+3));
}
else
{
var1=1;
var2=2;
var3=3;
return view(newRosterViewModel(var1,var2,var3));
}
}
}
In this code first time assigning values to var1,var2,var3. second time I need those vaues but values are null.
I tried with TempData but that also not holding value.
but values are null.
I think you'll find their values are 0 (zero), this is the default value for integral fields.
Every request mapped to RosterController will cause a new instance of RosterController to be created, this avoids any issues with concurrent requests mixing up their controller state.
To persist information from one request to another there are many options (database, Session, cookies, Application, …), but the state of a controller instance is not one of them. The right approach to persistence across requests depends on the requirements.
Why not make them static?
private static int var1;
or use Cache or Session
HttpRuntime.Cache.Add("var1", value);
var var1= HttpRuntime.Cache.Get("var1");
You can make your variables static. Making your variables static means - thier lifetime extends across the entire run of the program.
declare static like this
private static int var1;
private static int var2;
private static int var3;
But first please tell us your exact Use case
When you render view you should pass this variables to controller again, because on every request controller is recreated:
public class RosterController : Controller
{
int var1 = 0;
int var2 = 0;
int var3 = 0;
public ActionResult Index(int param1, int var1, int var2, int var3)
{
if(param1 ==1)
{
return view(newRosterViewModel(var1+1,var2+2,var3+3));
}
else
{
var1=1;
var2=2;
var3=3;
return view(newRosterViewModel(var1,var2,var3));
}
}
}
Related
I need some advice on how to proceed with the mvc app I'm building. On my page I type out who is logged in to the page. This I first did by creating a base class where I created a user class which contained the users name and a image representing the user. Then I passed this class on to my views. But I also need to pass other models to my views depending on what view I'm in. Sure I could build a class that contain all different models I need to use on each page but there should be a easy way to pass name and image values across the pages and be persistant? I tried TempData together with TempData.Keep() but that was not persistant. How can I pass theses values between pages?
public ActionResult Validate(AccountModels.LoginModel model)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
var mu = _repo.GetUser(Membership.GetUser().ProviderUserKey.ToString());
TempData["Name"] = mu.Name;
TempData["Image"] = mu.Image;
TempData.Keep();
FormsAuthentication.RedirectFromLoginPage(model.UserName, model.RememberMe);
}
}
return View("Index");
}
As #Jyoti said, you could use of Keep() method.
To make it easy to work with TempData, I wrote these methods in my BaseController, and I use it in every controller when I need to transfer data between actions or between view and controller.
protected TReturnType GetTempDataValue<TReturnType>(PsmConstants.TempDataKey sessionName, bool peekData =false )
{
object value = peekData ? TempData.Peek(sessionName.ToString()) : TempData[sessionName.ToString()];
return (TReturnType) value;
}
protected void RemoveTempData(PsmConstants.TempDataKey sessionName)
{
if (TempData.ContainsKey(sessionName.ToString()) && TempData[sessionName.ToString()] == null) return;
TempData[sessionName.ToString()] = null;
}
protected void SetTempDataValue(PsmConstants.TempDataKey sessionName, object value)
{
if(TempData.ContainsKey(sessionName.ToString()))
TempData[sessionName.ToString()]=null;
TempData[sessionName.ToString()] = value;
}
protected void KeepTempDataValue(PsmConstants.TempDataKey sessionName)
{
if (TempData.ContainsKey(sessionName.ToString()))
TempData.Keep(sessionName.ToString());
}
And this is the Keys enumeration :
public enum TempDataKey
{
PageError = 1,
PageInfo = 2
}
And this is, the usage of these methods(Set value and Get value from TempData):
SetTempDataValue(PsmConstants.TempDataKey.PageError , 'your error message' );
var originalValues = GetTempDataValue<MyModel>(PsmConstants.TempDataKey.Info, true);
Use session instead of Temp if it is not working.but i think it should work.
TempData["Name"] = mu.Name;TempData["Image"] = mu.Image;TempData.Keep();
How you are passing this into other models,Please share the source code so that it will easy to identify.
I have written the below simple Web API method.
[HttpGet]
[HttpPost]
public int SumNumbers([FromUri]Numbers calc, [FromUri]Operation op)
{
int result = op.Add ? calc.First + calc.Second : calc.First - calc.Second;
return op.Double ? result * 2 : result;
}
Below is the model class for Numbers:
public class Numbers
{
public int First { get; set; }
public int Second { get; set; }
}
Below is the model class for Operation:
public class Operation
{
public bool Add { get; set; }
public bool Double { get; set; }
}
Below is how I am trying to test in Postman. But, as you can see I am getting "0" as output. When debugged the code, understood that values are not passing from Postman into code.
One another user also posted the same problem here. But, whatever the resolution he showed, I am doing already, but I am not getting answer.
Can anyone please suggest where I am doing wrong?
There are 2 major issues with your post, firstly your controller (due to [FromUri]
binding) is specifying that the arguments need to be passed as Query String parameters and not Http Header values.
The second issue is that you have defined two complex type parameters that you want to obtain the values form the URI, this is not supported.
How to pass in Uri complex objects without using custom ModelBinders or any serialization?
This is a great writeup on how to fully exploit the [FromUriAttribute][2] up to ASP.Net Core 2.2, many of the principals apply to the FromQueryAttribute which is still used the current in ASP.Net 6.
We can use [FromUri] to bind multiple primitive typed parameters, or we can bind 1 single complex typed parameter. You cannot combine the two concepts, the reason for this is that when a complex type is used ALL of the query string arguments are bound to that single complex type.
So your options are to create a new complex type that combines all the properties from both types, or declare all the properties of both types as primitive parameters to the method:
http://localhost:29844/api/bindings/SumNumbers1?First=3&Second=2&Add=True&Double=False
http://localhost:29844/api/bindings/SumNumbers2?First=3&Second=2&Add=True&Double=False
[HttpGet]
[HttpPost]
public int SumNumbers1([FromUri] int First, [FromUri] int Second, [FromUri] bool Add, [FromUri] bool Double)
{
int result = Add ? First + Second : First - Second;
return Double ? result * 2 : result;
}
[HttpGet]
[HttpPost]
public int SumNumbers2([FromUri] SumRequest req)
{
int result = req.Add ? req.First + req.Second : req.First - req.Second;
return req.Double ? result * 2 : result;
}
public class SumRequest
{
public int First { get; set; }
public int Second { get; set; }
public bool Add { get; set; }
public bool Double { get; set; }
}
It is also technically possible to use a nested structure, where you wrap the multiple complex with a single outer complex type. Depending on your implementation and host constraints you may need additional configuration to support using . in the query parameters, but a nested or wrapped request implementation would look like this:
http://localhost:29844/api/bindings/SumNumbers3?Calc.First=3&Calc.Second=2&Op.Add=True&Op.Double=False
[HttpGet]
[HttpPost]
public int SumNumbers3([FromUri] WrappedRequest req)
{
int result = req.Op.Add ? req.Calc.First + req.Calc.Second : req.Calc.First - req.Calc.Second;
return req.Op.Double ? result * 2 : result;
}
public class WrappedRequest
{
public Numbers Calc { get; set; }
public Operation Op { get; set; }
}
It is also possible to use a combination of Http Headers and query string parameters, however these are generally harder (less common) to manage from a client perspective.
It is more common with complex parameter scenarios (not to mention more REST compliant) to force the caller to use POST to access your calculation endpoint, then multiple complex types are simpler to support from both a client and API perspective.
If you want to receive parameters using FromUri, shouldn't you pass them in the URL when doing the GET call? A simpler call would be something like this:
[HttpGet]
[Route("{first:int}/{second:int}")]
public int SumNumbers([FromUri]int first, [FromUri]int second)
{
return first+second;
}
And in Postman your call should be more like this (the url)
http://localhost:29844/api/bindings/SumNumbers/5/7
and this would return 12!
Now if you want to pass First and Second as headers and not in the url then you don't want to use FromUri and then your code would change a little bit (you will then need to read the request and dissect it to get every header alone. Something like this:
HttpRequestMessage request = Request ?? new HttpRequestMessage();
string first = request.Headers.GetValues("First").FirstOrDefault();
string second = request.Headers.GetValues("Second").FirstOrDefault();
I'm following Scott Allen's MVC4 course on PluralSight (I'm using MVC5 and WebAPI2 but they should be the same) and I am trying to pass an object via HTTP PUT. The model binder should bind it, but I am getting NULL for the parameter.
public HttpResponseMessage PutObjective(int id, [FromBody] Objective objective)
{
if (ModelState.IsValid && id == objective.ObjectiveID)
{
//todo: update - look up id, replace text
return Request.CreateResponse(HttpStatusCode.OK, objective);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
and in my front-end javascript I am doing the following (I'm creating an object for testing, so ignore 'objective' passed in):
var updateObjective = function (objective) {
var myobj = { "ObjectiveID": "3", "ObjectiveDescription": "test" };
return $.ajax(objectiveApiUrl + "/" + objective.ObjectiveID, {
type: "PUT",
data: myobj
});
}
My class looks like this:
public class Objective
{
public int ObjectiveID { get; private set; }
public string ObjectiveDescription { get; set; }
public Objective (int Id, string Desc)
{
this.ObjectiveID = Id;
this.ObjectiveDescription = Desc;
}
}
Any thoughts on why 'objective' in the backend is always 'null' ?
I've done what Scott Allen is doing, even tried adding in [FromBody] but no luck. $.ajax should have the correct content type by default I understand, so no need to set it.
I had Fiddler2 but I'm unsure as to what I am looking at to be honest. I can see my object as JSON being sent to the backend.
Well, if you're familiar with Model Binding you'll have seen the issue in my Objective class:
public int ObjectiveID { get; private set; }
with a private set, no instance can be created of the Objective class. To make it work, the 'private' access specifier needs to be removed.
What needs to happen really is that Objective becomes ObjectiveViewModel, and we convert what comes back to an Objective domain object (which may have more properties than we need for this screen). This can have a private set.
I am trying to have a general home page that depending on the parameter passed to the control, different content (modules) will be displayed.
For example, a user may select Kentucky from the menu and the id for Kentucky is 1. The home controller gets the id (1) and determines the possible modules for that
state (a simple db call.) Perhaps there is an announcements module and a contacts module for the state. An announcements module could have several items but it's only one module. There would be a partial view for each type of module.
Here is the basic setup I have.
public interface IModuleRepository
{
IList<MenuItemModule> GetMenuItemModules(int menuItem);
IList<Announcements> GetAnnouncements(int modID);
IList<News> GetNews(int modID);
IList<Contacts> GetContacts(int modID);
}
//business object
public class MenuItemModule
{
private int _MenuItemID;
private int _ModuleID;
private int _ModuleDefID;
private string _Src;
private int _ModuleOrder;
//get, set properties for these...
}
//announcements entity
public class Announcements
{
private int _ID = -1;
private int _MenuItemID = -1;
private int _ModuleID = -1;
private string _Description = string.Empty;
//get set props ...
}
In my home controller...
public class HomeController : Controller
{
private IModuleRepository modRepository;
public HomeController(IModuleRepository modRepository)
{
this.modRepository = modRepository;
}
public ViewResult Item(string ItemID)
{
//returns a list of menuitemmodules for the page. This gives me the Src or name of each
//module on the page, i.e. Announcements, news, contacts, etc.
var modules = modRepository.GetMenuItemModules(Convert.ToInt32(ItemID));
return View(modules);
}
}
I have tried several different models to return but I always run up against some contstraint. If I pass the menuitemmodules to my Item.aspx, then I can do something like this:
foreach (var mod in Model)
{
Html.RenderPartial(mod.Src, a); //needs an announcement object though
}
That makes it somewhat dynamic because I have the Src which would basically be something like "Announcements" and I can just create an announcements.ascx partial to process the module. But I have found it difficult to pass my menuitemmodule and an announcements entity as well.
I have also messed around with passing a more complex object and then testing every Src that comes through with an If statement. This would make scaling difficult in the future as I increase the number of possible modules in the app.
How can I solve my problem? I hope I have provided enough info. I like the basic idea here - http://www.mikesdotnetting.com/Article/105/ASP.NET-MVC-Partial-Views-and-Strongly-Typed-Custom-ViewModels but that seems to only work for static modules on a page.
I did try a composite view model called ModuleViewModel. Here is that attempt:
public class ModuleViewModel
{
public IList<Announcements> announcements { get; set; }
public IList<MenuItemModule> mods { get; set; }
}
If I pass that model to the Item.aspx I can do something like this (but I must be doing something wrong because something doesn't look right.)
foreach (var mod in Model)
{
if (mod.announcements.Count > 0)
{
Html.RenderPartial("Announcements", mod.announcements);
}
}
Once again, scalability is going to haunt me. I would like to have something like this on item page:
foreach (var mod in Model)
{
Html.RenderPartial(mod.Src, mod);
}
That would the correct partial view and pass it the correct model.
Create Module classes that derive from a common Module base class:
public class AnnouncementsModule : Module
{
}
public class ContactsModule : Module
{
}
In controller:
Create your various modules and put them into your overall view module (here it has a property called Modules that is an array of Module:
var viewModel = new ComplexViewModel
{
Modules = new []
{
new ContactsModule(),
new AnnouncementsModule()
}
};
return View(viewModule);
In view:
#Html.DisplayFor(x => x.Modules);
Create the partial views for each Type of Module in the appropriate 'Shared` folder. (Run it without creating them and it will show you an exception with the locations where it's looking for them).
After messing around with this for over a week, I finally managed to figure out how MVC can do what I want dynamically. I decided to post my solution for others that are new to MVC. Hopefully, the following will clear up the misunderstandings I had (although, at this point in my understanding of MVC, I cannot say this is the best approach.)
I will include the previous code snips and modifications for clarity:
public interface IModuleRepository
{
IList<MenuItemModule> GetMenuItemModules(int menuItem);
IList<Announcements> GetAnnouncements(int modID);
IList<News> GetNews(int modID);
IList<Contacts> GetContacts(int modID);
}
//business object
public class MenuItemModule
{
private int _MenuItemID;
private int _ModuleID;
private int _ModuleDefID;
private string _Src;
private int _ModuleOrder;
//get, set properties for these...
}
//announcements entity
public class Announcements : MenuItemModule
{
private int _ID = -1;
private string _Description = string.Empty;
//get set props ...
}
I also added another class:
public class AnnouncementModule : MenuItemModule
{
private IList<Announcements> _Announcements;
//get set prop
}
...and I created a model for the view
public class HomeItemViewModel
{
public MenuItemModule[] MenuItemModules { get; set; } //collection of menuitemmodules
}
In my home controller...
var menuItemModules = modRepository.GetMenuItemModules(ItemID);
if (menuItemModules.Count > 0)
{
AnnouncementModule aMod;
MenuItemModule[] mods = new MenuItemModule[menuItemModules.Count()];
int i = 0;
//loop through each MenuItemModule assign to the appropriate model
foreach (MenuItemModule mod in menuItemModules)
{
if (mod.Src == "Announcements")
{
aMod = new AnnouncementModule();
aMod.Announcements = modRepository.GetAnnouncements(mod.ModuleID);
//now add this to the menuitemmodule collection
mods[i] = aMod;
}
if (mod.Src == "Contacts")
{
//...
}
i++;
}
}
var viewModel = new HomeItemViewModel
{
MenuItemModules = mods
};
return View(viewModel);
Then I used the suggestion to use DisplayFor in the view. The view is strongly typed to HomeItemViewModel.
<%: Html.DisplayFor(m => m.MenuItemModules) %>
This iterates through the collection and based on the type, it will call that template. In this example, it calls AnnouncementModule.ascx which is strongly typed to AnnouncementModule.
foreach (var a in Model.Announcements)
{
//a.Description will give us the description of the announcement
}
I realize there are slicker ways to code the controller, and I plan on refactoring, but this skeleton should provide the basics to solve the question I posted.
I'm using SPMetal in order to generate entity classes for my sharepoint site and I'm not exactly sure what the best practice is to use when there are multiple content types for a single list. For instance I have a task list that contains 2 content types and I'm defining them via the config file for SPMetal. Here is my definition...
<List Member="Tasks" Name="Tasks">
<ContentType Class="LegalReview" Name="LegalReviewContent"/>
<ContentType Class="Approval" Name="ApprovalContent"/>
</List>
This seems to work pretty well in that the generated objects do inherit from WorkflowTask but the generated type for the data context is a List of WorkflowTask. So when I do a query I get back a WorkflowTask object instead of a LegalReview or Approval object. How do I make it return an object of the correct type?
[Microsoft.SharePoint.Linq.ListAttribute(Name="Tasks")]
public Microsoft.SharePoint.Linq.EntityList<WorkflowTask> Tasks {
get {
return this.GetList<WorkflowTask>("Tasks");
}
}
UPDATE
Thanks for getting back to me. I'm not sure how I recreate the type based on the SPListItem and would appreciate any feedback.
ContractManagementDataContext context = new ContractManagementDataContext(_url);
WorkflowTask task = context.Tasks.FirstOrDefault(t => t.Id ==5);
Approval a = new Approval(task.item);
public partial class Approval{
public Approval(SPListItem item){
//Set all properties here for workflowtask and approval type?
//Wouldn't there be issues since it isn't attached to the datacontext?
}
public String SomeProperty{
get{ //get from list item};
set{ //set to list item};
}
Linq2SharePoint will always return an object of the first common base ContentType for all the ContentTypes in the list. This is not only because a base type of some description must be used to combine the different ContentTypes in code but also it will then only map the fields that should definitely exist on all ContentTypes in the list. It is however possible to get access to the underlying SPListItem returned by L2SP and thus from that determine the ContentType and down cast the item.
As part of a custom repository layer that is generated from T4 templates we have a partial addition to the Item class generated by SPMetal which implements ICustomMapping to get the data not usually available on the L2SP entities. A simplified version is below which just gets the ContentType and ModifiedDate to show the methodology; though the full class we use also maps Modified By, Created Date/By, Attachments, Version, Path etc, the principle is the same for all.
public partial class Item : ICustomMapping
{
private SPListItem _SPListItem;
public SPListItem SPListItem
{
get { return _SPListItem; }
set { _SPListItem = value; }
}
public string ContentTypeId { get; internal set; }
public DateTime Modified { get; internal set; }
public virtual void MapFrom(object listItem)
{
SPListItem item = (SPListItem)listItem;
this.SPListItem = item;
this.ContentTypeId = item.ContentTypeId.ToString();
this.Modified = (DateTime)item["Modified"];
}
public virtual void MapTo(object listItem)
{
SPListItem item = (SPListItem)listItem;
item["Modified"] = this.Modified == DateTime.MinValue ? this.Modified = DateTime.Now : this.Modified;
}
public virtual void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
{
SPListItem originalItem = (SPListItem)originalListItem;
SPListItem databaseItem = (SPListItem)databaseObject;
DateTime originalModifiedValue = (DateTime)originalItem["Modified"];
DateTime dbModifiedValue = (DateTime)databaseItem["Modified"];
string originalContentTypeIdValue = originalItem.ContentTypeId.ToString();
string dbContentTypeIdValue = databaseItem.ContentTypeId.ToString();
switch(mode)
{
case RefreshMode.OverwriteCurrentValues:
this.Modified = dbModifiedValue;
this.ContentTypeId = dbContentTypeIdValue;
break;
case RefreshMode.KeepCurrentValues:
databaseItem["Modified"] = this.Modified;
break;
case RefreshMode.KeepChanges:
if (this.Modified != originalModifiedValue)
{
databaseItem["Modified"] = this.Modified;
}
else if (this.Modified == originalModifiedValue && this.Modified != dbModifiedValue)
{
this.Modified = dbModifiedValue;
}
if (this.ContentTypeId != originalContentTypeIdValue)
{
throw new InvalidOperationException("You cannot change the ContentTypeId directly");
}
else if (this.ContentTypeId == originalContentTypeIdValue && this.ContentTypeId != dbContentTypeIdValue)
{
this.ContentTypeId = dbContentTypeIdValue;
}
break;
}
}
}
Once you have the ContentType and the underlying SPListItem available on your L2SP entity it is simply a matter of writing a method which returns an instance of the derived ContentType entity from a combination of the values of the base type and the extra data for the missing fields from the SPListItem.
UPDATE: I don't actually have an example converter class as we don't use the above mapping extension to Item in this way. However I could imagine something like this would work:
public static class EntityConverter
{
public static Approval ToApproval(WorkflowTask wft)
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = wft.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = wft.SPListItem["field-name"];
return a;
}
}
Or you could put a method on a partial instance of WorkflowTask to return an Approval object.
public partial class WorkflowTask
{
public Approval ToApproval()
{
Approval a = new Approval();
a.SomePropertyOnWorkflowTask = this.SomePropertyOnWorkflowTask;
a.SomePropertyOnApproval = this.SPListItem["field-name"];
return a;
}
public LegalReview ToLegalReview()
{
// Create and return LegalReview as for Approval
}
}
In either situation you would need to determine the method to call to get the derived type from the ContentTypeId property of the WorkflowTask. This is the sort of code I would normally want to generate in one form or another as it will be pretty repetitive but that is a bit off-topic.