JSF SelectOneMenu with a Object Converter and null ids - jsf-2

okay lets start step by step:
Imagin, you have a selectOneMenu component which is working with objects which are comming from the database.
To work with objects in a selectOneMenu component, you have to define a simple converter which compare the object id with the givin string value which is passed as a function parameter and return the correct object. So far so good, nothing special here.
Now, a user can add additional Objects, which will be added to the selectOneMenu component.
The problem is now, if the user select the selfdefined object in the selectOneMenu component, it will throw an error, because of id is null. A workaround is to give the manual added object (created by a user) a unique id, and all is fine. But does exist a solution where no ids are required and still working with objects ?
thanks

Related

Wrong selected option for selectOneMenu with POJOs and Converter

in our company we're hitting a serious problem which we think is a serious design flaw of the JSF spec, if it is the normal behavior.
This is our usecase:
SelectOneMenu (standard JSF or primefaces, doesn't matter, same behavior)
SelectItems with a database entity as it's value and a string as the label
A converter (via attribute on the selectOneMenu) which translates the entity to its ID (getAsString) and from the ID to the entity (getAsObject)
Everything works as expected as long as the entity inside the value attribute of the selectOneMenu is loaded using the same entityManager as the entities inside the selectItems. We have the same POJOs and therefore the same hashcode (Object#equals() returns true). But as soon as one of the entities is loaded via a different entityManager and therefore has a different hashcode, we are never able to get a match to generate the expected selected attribute of an select item (HTML <option /> ).
The cause of this behavior is, that the JSF-impl and primefaces both use the POJOs in the call
boolean selected = isSelected(context, menu, itemValue, valuesArray, converter);
for itemValue and valuesArray. The implementation of isSelected relies on Object#equals() for POJOs. In my opinion it should always use the value of Converter#getAsString if we have a Converter and pass it to isSelected. This is also the behavior for a POST request. Here we got a submittedValue for the selectOneMenu which is compared to the converted value of the POJO (Converter#getAsString).
Now the question:
Is this the expected behavior as it's described in the spec? Isn't the output of the converter the better way to handle this comparision? Now we have to modify our entity classes and overwrite the equals method to be able to use this construct.
Your mistake is that you forgot to implement/autogenerate equals() (and hashCode()) method conform the contract. This is beyond control of JSF. Pointing the accusing finger to JSF isn't making any sense.
It's handy to have a base entity where all your entities extend from so that you don't need to repeat the task over all entities (even though the average IDE/tool can easily autogenerate them). You can find an elaborate example in 2nd "See also" link.
See also:
Validation Error: Value is not valid
Implement converters for entities with Java Generics
I believe that this is the correct behavior. On Java side the values of the component and selection are POJOs. You have full control of the logic of their equality etc. It should not matter how it is converted to UI display and back. As a component user you should not bother to know how the POJO is displayed. As text, icon, color, whatever. The Java side of the component undestands and communicats with POJOs
YOUR CODE ----------> JAVA COMPONENT --------> CONVERTER ----------> HTML
Also I guess that relying on reference equality for entities is a road to problems. Setting equals/hashCode to use actual ID is the best approach.

Entity Framework complaining about required fields when saveChanges even if valid data are setted

I'm using Entity Framework (DbContext with database first) with MVC. When user save from a form, I have a condition in the controller that send the entity to the update of insert method depending of some internal flag of mine.
When sending entity to the update method, I flag it to modified using context.Entry(myEntity).State = EntityState.Modified;, I call saveChanges() and everything work well.
When sending the entity to the insert method, I flag it to added using context.Entry(myEntity).State = EntityState.Added; but when calling saveChanges() I receive error about 2 fields that are required...
The problem is that thoses 2 fields are not empty and they effectively contain valid data just before saving... I have even try to force new values to thoses 2 fields just before saving but same error.
It may be usefull to mention that I'm using Devart DotConnect For PostgreSQL as db provider.
Any idea how to debug this problem?
EDIT:
Here is the error:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
When looking for this EntityValidationErrors I receive the 2 following specific errors:
The flg_actif field is required
The user_creation field is required
As mentionned before, those fields are filled with data just before saving so I don't understand what is happening.
I'm using EF v4.0.30319 (system.data.entity=> v4.0 and EntityFramework=> v4.4)
EDIT2:
Just to clarify a little bit more: The entity I'm trying to insert already exist in database. The form show the data of this database row. When saving, I decide if I update the row (this work well) but sometime, I need to insert the edited row as a new register instead of updating it to keep an history of the change in database.
Could you verify if the EntityKey property is set or null on the items you are trying to save?
If it already has a key, the context is already aware of the item, and you should use Attach instead of setting the state to added manually.
EDIT: To summarise the point from below. It looks like what you are doing is inserting a new copy of a row already associated with a context. That is almost certainly your problem. Try creating a fresh object based on your original row (i.e. copy the variable values or use a copy constructor), then add that new object.
Additionally, you should not need to set the state manually on a newly added object. You are trying to force the state here because the context doesn't see that item as a new one.

Generating unique ID attributes in datatable with JSF2 and IceFaces2

I'm trying to use a code which looks like :
<ice:dataTable id="revisionDocuments" value="#{agendaBean.agenda.revisionsDocuments}" var="revision">
<ice:column>
<ice:inputText value="#{revision.sequenceAdresse}" id="revisionSequenceAdresse#{revision.index}" />
</ice:column>
I'd like to have a different id for my form fields. The revision object do contains an "index" field, representing the index of the object in the List. I want to see it appears in the id. However, nothing happens. The #{revision.index} expression is never interpreted (getIndex() on the revision object is never called).
You'll tell me JSF already make something that looks like :
revisionDocuments:0:revisionSequenceAdresse
revisionDocuments:1:revisionSequenceAdresse
revisionDocuments:2:revisionSequenceAdresse
True, but this affect only the clientId generated in the HTML. The UIComponent representing the form fields (in the ViewRoot from FacesContext) have all the same "id" AND "clientId" (yes, event if the HTML contains "revisionDocuments:0:revisionSequenceAdresse", the "clientId" you will find in ViewRoot is revisionDocuments:revisionSequenceAdresse).
Someone can help with my try ?
Thank you very much, any help will be greatly appreciated.
The component IDs are to be determined during view build time, not during view render time. The #{revision} is not available during view build time, so it always evaluates empty. Basically, you need to bind it to #{agendaBean} or something else which is already in the scope during the view build time. The component ID is specific to the component itself, not to its generated HTML output. You can't assign multiple different IDs to physically the one and same component.
But you actually don't need to fiddle around this approach. Your concrete problem for which you thought that this is the solution is already answered in your previous question: JSF2 + IceFaces 2 - Retrieve UIComponent from ViewRoot.

ASP.NET MVC save new record verse update existing record conventions

I'm working on my first ASP.NET MVC (beta for version 3) application (using EF4) and I'm struggling a bit with some of the conventions around saving a new record and updating an existing one. I am using the standard route mapping.
When the user goes to the page /session/Evaluate they can enter a new record and save it. I have an action defined like this:
[ActionName("Evaluate")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EvaluateSave(EvaluteSessionViewModel evaluatedSession)
{
}
When they save I grab an entity off the view model and attach it to my context and save. So far, so good. Now I want the user to be able to edit this record via the url /session/Evaluate/1 where '1' is the record ID.
Edit: I have my EF entity attached as a property to the View Model.
If I add an overloaded method, like this (so I can retrieve the '1' portion automatically).
[ActionName("Evaluate")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EvaluateSave(ID, EvaluteSessionViewModel evaluatedSession)
{
}
I get an "The current request for action 'Evaluate' on controller type 'SessionsController' is ambiguous between the following action" error. I'm not sure why they're ambiguous since they look unique to me.
I decided that I was just going to skip over this issue for now and see if I could get it to update an existing record, so I commented out the EvaluateSave that didn't have the ID parameter.
What I'd like to do is this:
// Load the original entity from EF
// Rebind the postback so that the values posted update the entity
// Save the result
Since the entity is populated as the parameter (evaluatedSession) the rebinding is happening too soon. But as I look at the approach I'd like to take I realized that it opens my code up to hacking (since a user could add in fields into the posted back page and these could override the values I set in the entity).
So it seems I'm left with having to manually check each field to see if it has changed and if it has, update it. Something like this:
if (evaluatedSession.MyEntity.myField <> savedSession.myField)
savedSession.myField = evaluatedSession.MyEntity.myField;
Or, save a copy of the entity and make sure none of the non-user editable ones have changed. Yuck.
So two questions:
First: how do I disambiguate the overloaded methods?
Second: is there a better way of handling updating a previously saved record?
Edit: I guess I could use something like Automapper...
Edit 9/22/2010 - OK, it looks like this is supposed to work with a combination of two items: you can control what fields bind (and specifically exclude some of them) via the [Bind(Exclude="field1,field2")] attribute either on the class level or as part of the method doing the saving, ex.
public ActionResult EvaluateSave([Bind(Exclude="field1")] EvaluateSessionViewModel evaluatedSession)
From the EF side of things you are supposed to be able to use the ApplyCurrentValues() method from the context, ex.
context.ApplyCurrentValues(savedEval.EntityKey.EntitySetName, evaluatedSession);
Of course, that doesn't appear to work for me. I keep getting "An object with a key that matches the key of the supplied object could not be found in the ObjectStateManager. Verify that the key values of the supplied object match the key values of the object to which changes must be applied.".
I tried attaching the original entity that I had just loaded, just in case it wasn't attached to the context for some reason (before ApplyCurrentValues):
context.AttachTo(savedEval.EntityKey.EntitySetName, savedEval);
It still fails. I'm guessing it has something to do with the type of EF entity object MVC creates (perhaps it's not filled in enough for EF4 to do anything with it?). I had hoped to enable .NET framework stepping to walk through it to see what it was attempting to do, but it appears EF4 isn't part of the deal. I looked at it with Reflector but it's a little hard for me to visualize what is happening.
Well, the way it works is you can only have one method name per httpverb. So the easiest way is to create a new action name. Something like "Create" for new records and "Edit" for existing records.
You can use the AntiForgeryToken ( http://msdn.microsoft.com/en-us/library/dd492767.aspx ) to validate the data. It doesn't stop all attempts at hacking but it's an added benefit.
Additional
The reason you can only have one action name per httpverb is because the model binders only attempt to model bind and really aren't type specific. If you had two methods with the same action name and two different types of parameters it can't just try and find the best match because your intent might be clearly one thing while the program only sees some sort of best match. For instance, your might have a parameter Id and a model that contains a property Id and it might not know which one you intend to use.

Updatemodel error when list item removed

I have an edit page to edit some info. the page fills a complex object. one of the properties of this object is a generic list.
If I just edit information and save, updatemodel works fine. if i remove (I do this using jquery to remove the form elements client side) something from the list the updatemodel fails with an "object not set to an instance".
I guess the update model is expecting the list to remain of the same length or something but cannot find any information about this, any ideas?
OK, figured out the problem (and it was of course programmer error) on the jquery remove routine I had removed all the elements EXCEPT the hidden field that the model binder uses for lists :(
The model binder will try to map your complex object properties retrieving data from:
1) values from the RouteData
2) URI query string
3) request form submission
Check this places to see why your property is null. If you're deleting your form elements your property will not receive any data.
Some info here and a bug analysis by Scott Hanselman here.

Resources