How to populate Selectlist using stored procedure - asp.net-mvc

In my MVC application I am trying to retrieve data using a stored procedure, then display it in a dropdown.
Here is My controller action
public ActionResult Register(string id )
{
RegistrationModel Student = new RegistrationModel();
using (var db = new StudentEntities())
{
var SportResultList = GetListOfSport();
var SportSelectList = new SelectList(SportResultList);
ViewBag.SportList = SportSelectList;
return View(Student);
}
Here is the Method to get the list using the stored procedure
public static List<GetSportsResult> GetListOfSport()
{
using (var db = new StudentEntities())
{
ObjectResult<GetSportsResult> SportResults = db.GetSportsByStudentIdAndSeason(11111, 1);
List<GetSportsResult> results = SportResults.ToList();
return results;
}
}
The stored procedure returns a complex type called GetSportsResults but I don't now how to access its fields.
Currently this code will display the GetSportsResults 20 times which is the right amount of records I should be getting

In the constructor for SelectList you can specify which fields are to be used for the text and for the value. This is done by passing string values to the constructor.
For example, if your GetSportsResult object has a .ID property as its identifier and a .Name property as its display value, then your code would look like this:
var SportResultList = GetListOfSport();
var SportSelectList = new SelectList(SportResultList, "ID", "Name");
ViewBag.SportList = SportSelectList;
This would indicate to the SelectList object that GetSportsResult.ID should be the value for each item in the list, and GetSportsResult.Name should be the displayed text for each item in the list.
Without specifying these fields, currently the object tries to make a "best guess" of what to display. It's probably doing this by calling .ToString() on each object by default. And the default behavior of .ToString() on a non-primative type is to display the name of the type itself, which is why you're seeing the string "GetSportsResult" for each item.

Related

Passing SItecore Item ID to Model from Controller

I am creating products from a product template. Each time a customer selects a product to view information about, the data from that product needs to get loaded. I have created a controller, model and view. The model is generated with TDS. I need to pass the item id to the [SitecoreId] from the controller. Here is the code I am using:
From the layout:
#{var id = Sitecore.Data.ID.Parse("{74A67488-8E33-47E2-86F5-25AD23FDF3D3}"); }
#Html.Sitecore().ControllerRendering("ProductOverview", "Index", new { ItemId = #id })
The controller:
public class ProductOverviewController : Controller
{
private readonly IMvcContext _mvcContext;
public ProductOverviewController(IMvcContext mvcContext)
{
_mvcContext = mvcContext;
}
// GET: ProductOverview
public ActionResult Index()
{
var itemId = string.Empty;
var rc = RenderingContext.CurrentOrNull;
if (rc != null)
{
var parms = rc.Rendering.Properties;
itemId = parms["ItemId"];
}
var dataSource = _mvcContext.GetContextItem<ProductOverviewModel> ();
return View(dataSource);
}
}
The itemId var has the correct id that I am passing from the layout (hard coded for now). From here I am at an absolute loss on how to get that into the model. I have tried dozens of suggestions from searches but the model always uses the current item (as set by GlassBase in the model itself) as opposed to the product id that contains the data for that product.
Is what I want to do even possible? Can the [SitecoreId] even be overridden?
The line where you are setting the value for dataSource using Glass Mapper is where you'll want to make your change..
Glass Mapper lets you use a number of different options to get the Item and cast to your "type" which looks to be ProductOverviewModel currently.
you can use the following for example (notice that I've used .SitecoreService.GetItem instead of .GetContextItem ):
//pass the GUID into here (you'd need to cast to a Guid first instead of ID)
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(guid);
//or if you wanted to get your ID as a Sitecore Item you could use
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(item.Paths.Path);

html.dropdownlistfor bind selected value to one column, set another column to the selected Text

I'm fairly new to mvc and razor views.
I have a DropDownListFor that successfully is populated with a list of values and text from a data table.
I also am successfully binding the selected value to a data column in my data model.
However, the database that I'm working with is not normalized and I have need to taking the Text from the selected item, and binding that to another field in my data model.
How hard is it to do this?
You have several options:
1. Call the database to get text after submit
public ActionResult Submit(int dropDownListId)
{
var text = string.Empty;
var dataItem = this._dbContext.SomeTableData.SingleOrDefault(m => m.Id == dropDownListId);
if (dataItem != null)
{
text = dataItem.Title;
}
// continue
}
2. Adjust the drop down list value and parse the value in controller/business layer:
If you're using the default DropDownListFor HtmlHelper your data source is type of IEnumerable<SelectListItem> so while you're building your Model class you're going to pass it to the view, do something like this:
//get data from database
var dataSource = this_dbContext.SomeTableData.Select(m => new SelectListItem()
{
Value = string.Format("{0}|{1}", m.Id, m.Title),
Text = m.Title
}
And after you submit data parse the value:
public ActionResult Submit(string dropDownListValue) {
string[] values = dropDownListValue.Split('|');
// values[0] = value
// values[1] = text
}
3. Add hidden field to the view and use JavaScript to store the text
$('#dropDownListId').on('change', function () {
var text = $(this).find('option:selected').text();
$('#dropDownListText').val(text); // hidden field
});
and collect the value after submit:
public ActionResult Submit(int dropDownListId, string dropDownListText)
{
//logic
}

MVC 5 Razor - Listbox that displays items passed in a Viewbag list

I am passing a ViewBag list to my view and I am trying to display this list in a listbox so as I can select items of this list.
This is the controller method for the view:
public ActionResult AddMembers(int? id)
{
ViewBag.lstMembers = db.ClubMembers.ToList();
var selClub = db.Clubs.Find(id);
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
return View(selClub);
}
This is what I have currently but it is not displaying the items correctly as shown in the photo below:
#Html.ListBoxFor(m => m.ClubMembers, new SelectList ( ViewBag.lstMembers))
How can I set the source of my listbox so that it will display the items passed in via the ViewBag.lstMembers from the controller method
You need to specify the display member and value member that will be used to render the string representation of the object, otherwise MVC will just call ToString.
SelectList has an overloaded constructor for this e.g.:
new SelectList(items, "ValuePropertyName", "DisplayPropertyName")
see here:
https://msdn.microsoft.com/en-us/library/system.web.mvc.selectlist.selectlist(v=vs.118).aspx#M:System.Web.Mvc.SelectList.
you can use MultiSelectList some thing like that
In Action
ViewBag.lstMembers = new MultiSelectList(db.ClubMembers.ToList(), "ValueProperty", "NameProperty");
in View
#Html.ListBox("lstMembers", ViewBag.lstMembers as MultiSelectList)

passing value in partial view viewdatadictionary

#Html.Partial("~/Areas/WO/Views/PartialContent/_FirstPage.cshtml", new ViewDataDictionary { { "WOID", WOID } })
In my Page i am accessing Partial view in the above way.
I need to pass WOID(view data dictionary) value from query string, For that i am using following Code
#{
var desc = Html.ViewContext.HttpContext.Request.QueryString.Get("ID");
Uri referrer = HttpContext.Current.Request.UrlReferrer;
string[] query = referrer.Query.Split('=');
int WOID = Convert.ToInt32(query[1]);
}
But the issue is this code is working in all browsers except I.E. i Need to Solve this problem.
Please help me
Instead of this you can have this value as part of you model and use that.That is the standard and recommeded way .
In your action method you can have these as parameter.Your query string value will get bind to this parameter
public ActionResult ActionMethod(int ID)
{
Model.WOID = WOID;
// Other logic
return View(Model)
}
Next step you can add this as a property to your view model or add it to ViewData dictionary and then access it in your partial view.

Exclude property from updating when SaveChanges() is called

There appears to be two ways to update a disconnected Entity Framework entity using the "attach" method.
Method One is to simply set the disconnected entity's state as modified:
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
myDbContext.SaveChanges();
This will save all fields on the "dog" object. But say you are doing this from an mvc web page where you only allow editing of Dog.Name, and the only Dog property contained on the page is Name. Then one could do Method Two:
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).Property(o => o.Name).CurrentValue = dog.Name;
myDbContext.Entry(dog).Property(o => o.Name).IsModified = true;
myDbContext.SaveChanges();
Method Two could get quite verbose when there are a lot of properties to update. This prompted me to attempt Method Three, setting IsModified = false on the properties I don't want to change. This does not work, throwing the runtime error "Setting IsModified to false for a modified property is not supported":
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
myDbContext.Entry(dog).Property(o => o.Owner).IsModified = false;
myDbContext.SaveChanges();
I'd much prefer to use Method One everywhere, but there are many instances where my asp.net mvc view does not contain every scalar property of the Dog class.
My questions are:
Are there any attributes I could use on the POCO class that would tell Entity Framework that I never want the property to up updated? Eg, [NeverUpdate]. I am aware of the [NotMapped] attribute, but that is not what I need.
Failing that, is there any way I can use Method One above (myDbContext.Entry(dog).State = EntityState.Modified;
) and exclude fields that I don't want updated?
P.S. I am aware of another way, to not use "attach" and simply fetch a fresh object from the database, update the desired properties, and save. That is what I am doing, but I'm curious if there is a way to use "attach," thus avoiding that extra trip to the database, but do it in a way that is not so verbose as Method Two above. By "fetch a fresh object" I mean:
Dog dbDog = myDbContext.Dogs.FirstOrDefault(d => d.ID = dog.ID);
dbDog.Name = dog.Name;
myDbContext.SaveChanges();
The following may work works.
myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
var objectContext = ((IObjectContextAdapter) myDbContext).ObjectContext;
foreach (var entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Where(entity => entity.Entity.GetType() == typeof(Dogs)))
{
// You need to give Foreign Key Property name
// instead of Navigation Property name
entry.RejectPropertyChanges("OwnerID");
}
myDbContext.SaveChanges();
If you want to do it in a single line, use the following extension method:
public static void DontUpdateProperty<TEntity>(this DbContext context, string propertyName)
{
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
foreach (var entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Where(entity => entity.Entity.GetType() == typeof(TEntity)))
{
entry.RejectPropertyChanges(propertyName);
}
}
And use it like this
// After you modify some POCOs
myDbContext.DontUpdateProperty<Dogs>("OwnerID");
myDbContext.SaveChanges();
As you can see, you can modify this solution to fit your needs, e.g. use string[] properties instead of string propertyName as the argument.
Suggested Approach
A better solution would be to use an Attribute as you suggested ([NeverUpdate]). To make it work, you need to use SavingChanges event (check my blog):
void ObjectContext_SavingChanges(object sender, System.Data.Objects.SavingChangesEventArgs e)
{
ObjectContext context = sender as ObjectContext;
if(context != null)
{
foreach(ObjectStateEntry entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
{
var type = typeof(entry.Entity);
var properties = type.GetProperties();
foreach( var property in properties )
{
var attributes = property.GetCustomAttributes(typeof(NeverUpdateAttribute), false);
if(attributes.Length > 0)
entry.RejectPropertyChanges(property.Name);
}
}
}
}
// Check Microsoft documentation on how to create custom attributes:
// http://msdn.microsoft.com/en-us/library/sw480ze8(v=vs.80).aspx
public class NeverUpdateAttribute: SystemAttribute
{
}
//In your POCO
public class Dogs
{
[NeverUpdate]
public int OwnerID { get; set; }
}
Warning: I did not compile this code. I'm not at home :/
Warning 2: I have just read the MSDN documentation and it says:
ObjectStateEntry.RejectPropertyChanges Method
Rejects any changes made to the property with the given name since the
property was last loaded, attached, saved, or changes were accepted.
The orginal value of the property is stored and the property will no
longer be marked as modified.
I am not sure what its behavior would be in the case of attaching a modified entity. I will try this tomorrow.
Warning 3: I have tried it now. This solution works. Property that is rejected with RejectPropertyChanges() method are not updated in the persistence unit (database).
HOWEVER, if the entity that is updated is attached by calling Attach(), the current context remains dirty after SaveChanges(). Assume that the following row exists in the database:
Dogs
ID: 1
Name: Max
OwnerID: 1
Consider the following code:
var myDog = new Dogs();
myDog.ID = 1;
myDog.Name = Achilles;
myDog.OwnerID = 2;
myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).State = EntityState.Modified;
myDbContext.SaveChanges();
The current state of database after SaveChanges():
Dogs:
ID: 1
Name: Achilles
OwnerID: 1
The current state of myDbContext after SaveChanges():
var ownerId = myDog.OwnerID; // it is 2
var status = myDbContext.Entry(myDog).State; // it is Unchanged
So what you should do? Detach it after SaveChanges():
Dogs myDog = new Dogs();
//Set properties
...
myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).State = EntityState.Modified;
myDbContext.SaveChanges();
myDbContext.Entry(myDog).State = EntityState.Detached;

Resources