Difficulty passing object from one controller to anothher - asp.net-mvc-4

I am trying to pass an object from one controller to another controller, but it is not behaving in the way that I would like it to. In the following ApplicantMainController, I am instantiating an object called ApplicationQuestions (which contains a List<ApplicationQuestion> object as one of its members) and then attempting to pass it via the RedirectToAction method call:
public ActionResult FormAction(FormCollection collection)
{
if (collection["cmdSearch"] != null)
{
// more code above...
ApplicationQuestions questions = new ApplicationQuestions();
MultipleChoiceQuestion q1 = new MultipleChoiceQuestion("Can you lift 50 pounds?");
MultipleChoiceQuestion q2 = new MultipleChoiceQuestion("Are you at least 18 years of age?");
MultipleChoiceQuestion q3 = new MultipleChoiceQuestion("Are you legally able to work in the US?");
MultipleChoiceQuestion q4 = new MultipleChoiceQuestion("Have you ever been convicted of a felony?");
q1.AddPossibleAnswer(1, new Answer("Yes", true));
q1.AddPossibleAnswer(2, new Answer("No", false));
q2.AddPossibleAnswer(1, new Answer("Yes", true));
q2.AddPossibleAnswer(2, new Answer("No", false));
q3.AddPossibleAnswer(1, new Answer("Yes", true));
q3.AddPossibleAnswer(2, new Answer("No", false));
q4.AddPossibleAnswer(1, new Answer("Yes", false));
q4.AddPossibleAnswer(2, new Answer("No", true));
questions.AddQuestion(q1);
questions.AddQuestion(q2);
questions.AddQuestion(q3);
questions.AddQuestion(q4);
// not sure how to pass the object here??
return RedirectToAction("Apply", "ApplicantApply", new { model = questions });
}
}
When I redirect to the controller, it appears to make it:
private ApplicationQuestions m_questions;
// more code ...
public ActionResult Apply(ApplicationQuestions questions)
{
m_questions = questions;
return RedirectToAction("NextQuestion", "ApplicantApply");
}
However, though the reference is now bound to the parameter passed to the Apply method, the debugger tells me that the reference to the collection questions does not contain any elements, though clearly the caller passed a collection with four elements. I think I am misunderstanding how this works -- what is the proper way to communicate objects between controllers like this?

One option is to use TempData to hold the object during the redirect.
TempData["MyCoolData"] = questions;
return RedirectToAction("Apply", "ApplicantApply");
Then grab the object out of TempData in the other action:
m_questions = (ApplicationQuestions)TempData["MyCoolData"];
See this question and its answers for more information.

Related

Entity framework : The state value of a new object

I'm using vb.net and entity framework.
I add a new object (Myobject) and before the savechanges is called , I try to modify it.
After that I check the context.entry(Myobject).state and the result is EntityState.Modified.
But this is a new record . Why the state isn't Entitystate.Added ?
Thank you !
From the documentation
Added: The object is new, has been added to the object context, and the SaveChanges method has not been called. After the changes are saved, the object state changes to Unchanged. Objects in the Added state do not have original values in the ObjectStateEntry.
You can use the EntityState.Added like this
using (var context = new BloggingContext())
{
var blog = new Blog { Name = "ADO.NET Blog" };
context.Entry(blog).State = EntityState.Added;
context.SaveChanges();
}

my zend session name spacing does not work

I am new to Zend and very keen to learn, so I would really appreciate some help and guidance.
I am trying to create a 'method in a class' that will save the session variables of product pages visited by members to a site i.e
i,e examplesite com/product/?producttype= 6
I want to save the number 6 in a session variable. I also do not want to have a global session for the entire site; I just want it for selected pages. So, I guess I have to have Zend_Session::start() on the selected page; but I am not clear how this should be done.
Should I instantiate it in the page view page. i.e products page or do this in the indexAction() method for the products page. I have attempted to instantiate it below but it did not work.
public function rememberLastProductSearched()
{ //my attempt to start a session start for this particular page.
Zend_Session::start();
}
$session->productSearchCategory = $this->_request->getParam('product-search-category');
return" $session->productSearchCategory ";
}
else
{
//echo " nothing there
return " $session->productSearchCategory";
//";
}
}
With the rememberLastProductSearched() method I was trying to get the method to first check whether the user had searched for a new product or just arrived at the page by default. i.e whether he had used the get() action to search for a new product. If the answer is no, then I wanted the system to check whether their had been a previous saved session variable. so in procedural syntax it would have gone like this:
if(isset($_Get['producttype']))
{
//$dbc database connection
$producttype = mysqli_real_escape_string($dbc,trim($_GET['producttype']));
}
else
if(isset($_SESSION['producttype'])){
$producttype = mysqli_real_escape_string($dbc,trim($_SESSION['producttype']));
}
Can you please help me with the Zend/oop syntax. I am totally confused how it should be?
you're asking about simple work flow in an action, it should begin something like:
//in any controller
public function anyAction()
{
//open seesion, start will be called if needed
$session = new Zend_Session_Namespace('products');
//get value
$productCategory = $this->getRequest()->getParam('producttype');
//save value to namespace
$session->productType = $productCategory;
//...
}
now to move this off to a separate method you have to pass the data to the method...
protected function rememberLastProductSearched($productType)
{
//open seesion, start will be called if needed
$session = new Zend_Session_Namespace('products');
$session->productType = $productType;
}
So now if you want to test for presence of a value...
//in any controller
public function anyAction()
{
//open seesion, call the namespace whenever you need to access it
$session = new Zend_Session_Namespace('products');
if (!isset($session->productType)) {
$productCategory = $this->getRequest()->getParam('producttype');
//save value to session
$this->rememberLastProductSearched($productCategory)
} else {
$productCategory = $session->productType;
}
}
That's the idea.
Be mindful of your work flow as it can sometimes be very simple to inadvertently overwrite your session values.
$session = new Zend_Session_Namespace("productSearch");
if ($this->getRequest()->getParam('producttype')) { //isset GET param ?
$session->productType = $this->getRequest()->getParam('producttype');
$searchedProductType = $session->productType;
} else { //take the session saved value
if ($session->productType) {
$searchedProductType = $session->productType;
}
}
//now use $searchedProductType for your query

Handle navigation properties when adding new object to entity model

I have the following data model:
I am writing a WCF service that needs to support adding new Report:
public bool CreateNewReport(Report report)
{
MyEntities context = new MyEntities();
context.AddToReports(Report);
context.SaveChanges();
}
So my method gets a report object that was made on the client and adds it to the database throught the data context. (all of the members are included in the DataContract)
My question is regarding navigation properties.
Do the client also needs to create a user object and put it in the new report object before sending it ?
What is the best way to approach this ? one way i think of is adding a UserId field in the ReportEntity
when a new report is inserted, how do i update the UserEntity Report nav property that with the new Report ?
Thanks.
If you import your database, generate navigation properties (the properties in your picture) AND foreign id properties (then you have for example an User and UserID property in your report class). This way you can set the UserID in your client and send it to the server and add it with AddToReports... If you send the whole user object you have to attach it to the entity context otherwise the user will be created once again...
Attach the referenced user: (but it's better to send the user only by id)
public bool CreateNewReport(Report report)
{
using (MyEntities context = new MyEntities())
{
context.AddToReports(Report);
context.Users.Attach(report.User);
context.SaveChanges();
}
}
To change the report of a user:
public bool ChangeUserToNewReport(int userid, Report newReport)
{
using (MyEntities context = new MyEntities())
{
var user = context.Users.Single(u => u.ID = userid);
user.Report = newReport;
context.SaveChanges();
}
}
For an existing report:
public bool ChangeUserReport(int userid, Report existingReport)
{
using (MyEntities context = new MyEntities())
{
context.Reports.Attach(existingReport);
var user = context.Users.Single(u => u.ID = userid);
user.Report = existingReport;
context.SaveChanges();
}
}
This is a sample how your model should look like. Double click on the association line to open the dialog. You can see that the Person and PersonID properties are the same. If you create your model like this, VS should generate the correct SQL.

How to add a new entity to a domain context and immediately see it in data bound controls before SubmitChanges?

I've got a Silverlight 4 RIA Services (SP1) app using Entity Frameworks 4 CTP5. I can databind a grid or listbox to the IEnumerable loaded by the domain context and it shows data from the server. Great.
Now I want to create a new instance of MyEntity and add it to the client-side data so that the user can see the newly added entity. MyEntity is a true entity descendant, not a POCO.
The only Add method I can find is domainContext.EntityContainer.GetEntitySet<MyEntity>().Add(newobj)
This does add the new entity to the domain context, and the domainContext.HasChanges does become true, but the new entity doesn't show up in the databound controls.
How do I get the new entity to show up in the databound controls prior to SubmitChanges?
(Probably related to this SO question from years ago that never got an answer)
Here's the server side declarations of the domain service, per requests:
[EnableClientAccess()]
public class MyDomainService : LinqToEntitiesDomainService<MyObjectContext>
{
protected override MyObjectContext CreateObjectContext()
{
return new MyObjectContext();
}
public IQueryable<MyEntity> GetMyEntities()
{
return this.ObjectContext.MyEntities;
}
public void InsertMyEntity(MyEntity MyEntity)
{
// ...
}
public void UpdateMyEntity(MyEntity currentMyEntity)
{
// ...
}
public void DeleteMyEntity(MyEntity MyEntity)
{
// ...
}
}
I've figured this out with a combination of my own trial and error and hints provided by some of the other responses to this question.
The key point I was missing was that it's not enough for the ViewModel to keep track of the DomainContext and hand out query results to the View for databinding. The ViewModel also has to capture and retain the query results if you want entity adds and deletes performed by the ViewModel to appear in the UI before DomainContext.SubmitChanges(). The ViewModel has to apply those adds to the collection view of the query results.
The ViewModel collection property for View databinding. In this case I'm using the Telerik QueryableDomainServiceCollectionView, but other collection views can be used:
public IEnumerable<MyEntity> MyEntities
{
get
{
if (this.view == null)
{
DomainContextNeeded();
}
return this.view;
}
}
private void DomainContextNeeded()
{
this.context = new MyDomainContext();
var q = context.GetMyEntitiesQuery();
this.view = new Telerik.Windows.Data.QueryableDomainServiceCollectionView<MyEntity>(context, q);
this.view.Load();
}
The ViewModel function that adds a new entity for the UI to display:
public void AddNewMyEntity(object selectedNode)
{
var ent = new MyEntity() { DisplayName = "New Entity" };
if (selectedNode == null)
{
this.view.AddNew(ent);
}
else if (selectedNode is MyEntity)
{
((MyEntity)selectedNode).Children.Add(ent);
}
}
Other responses mentioned ObservableCollection. The query results and the collection view may not return instances of ObservableCollection. They could be just IEnumerables. What is critical is that they implement INotifyCollectionChanged and IEditableCollectionView.
Thanks to those who contributed responses. I've +1'd each response that was helpful, but since none directly solved my problem I couldn't justify marking any as the definitive answer.
Your domainContext will have a property domainContext.MyEntities. Does it not show up in there when you add it?
Bind to that collection or watch that collection for changes.
domainContext.MyEntities.PropertyChanged += MyEventHandler;
I assume you bind your control to the IEnumerable which is provided by LoadOperation<TEntity>.Entities. In that case your binding source is not the DomainContext.GetEntitySet<MyEntity>().
DomainContext.GetEntitySet<MyEntity>() holds all your currently tracked instances of MyEntity, including the one you add with .Add().
LoadOperation<TEntity>.Entities only contains the instances of MyEntity that were actually loaded by your last LoadOperation/Query.
You have two options: Either add the new entity to the ItemsSource-collection for your control (I recommend that) or rebuild the collection with the contents of DomainContext.GetEntitySet<MyEntity>(). That may contain other elements that you have not cleared out before, though.

LINQ SQL Attach, Update Check set to Never, but still Concurrency conflicts

In the dbml designer I've set Update Check to Never on all properties. But i still get an exception when doing Attach: "An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported." This approach seems to have worked for others on here, but there must be something I've missed.
using(TheDataContext dc = new TheDataContext())
{
test = dc.Members.FirstOrDefault(m => m.fltId == 1);
}
test.Name = "test2";
using(TheDataContext dc = new TheDataContext())
{
dc.Members.Attach(test, true);
dc.SubmitChanges();
}
The error message says exactly what is going wrong: You are trying to attach an object that has been loaded from another DataContext, in your case from another instance of the DataContext. Dont dispose your DataContext (at the end of the using statement it gets disposed) before you change values and submit the changes. This should work (all in one using statement). I just saw you want to attach the object again to the members collection, but it is already in there. No need to do that, this should work just as well:
using(TheDataContext dc = new TheDataContext())
{
var test = dc.Members.FirstOrDefault(m => m.fltId == 1);
test.Name = "test2";
dc.SubmitChanges();
}
Just change the value and submit the changes.
Latest Update:
(Removed all previous 3 updates)
My previous solution (removed it again from this post), found here is dangerous. I just read this on a MSDN article:
"Only call the Attach methods on new
or deserialized entities. The only way
for an entity to be detached from its
original data context is for it to be
serialized. If you try to attach an
undetached entity to a new data
context, and that entity still has
deferred loaders from its previous
data context, LINQ to SQL will thrown
an exception. An entity with deferred
loaders from two different data
contexts could cause unwanted results
when you perform insert, update, and
delete operations on that entity. For
more information about deferred
loaders, see Deferred versus Immediate
Loading (LINQ to SQL)."
Use this instead:
// Get the object the first time by some id
using(TheDataContext dc = new TheDataContext())
{
test = dc.Members.FirstOrDefault(m => m.fltId == 1);
}
// Somewhere else in the program
test.Name = "test2";
// Again somewhere else
using(TheDataContext dc = new TheDataContext())
{
// Get the db row with the id of the 'test' object
Member modifiedMember = new Member()
{
Id = test.Id,
Name = test.Name,
Field2 = test.Field2,
Field3 = test.Field3,
Field4 = test.Field4
};
dc.Members.Attach(modifiedMember, true);
dc.SubmitChanges();
}
After having copied the object, all references are detached, and all event handlers (deferred loading from db) are not connected to the new object. Just the value fields are copied to the new object, that can now be savely attached to the members table. Additionally you do not have to query the db for a second time with this solution.
It is possible to attach entities from another datacontext.
The only thing that needs to be added to code in the first post is this:
dc.DeferredLoadingEnabled = false
But this is a drawback since deferred loading is very useful. I read somewhere on this page that another solution would be to set the Update Check on all properties to Never. This text says the same: http://complexitykills.blogspot.com/2008/03/disconnected-linq-to-sql-tips-part-1.html
But I can't get it to work even after setting the Update Check to Never.
This is a function in my Repository class which I use to update entities
protected void Attach(TEntity entity)
{
try
{
_dataContext.GetTable<TEntity>().Attach(entity);
_dataContext.Refresh(RefreshMode.KeepCurrentValues, entity);
}
catch (DuplicateKeyException ex) //Data context knows about this entity so just update values
{
_dataContext.Refresh(RefreshMode.KeepCurrentValues, entity);
}
}
Where TEntity is your DB Class and depending on you setup you might just want to do
_dataContext.Attach(entity);