Within my entity framework model I have:
<Required(), Range(0, Double.MaxValue, ErrorMessage:="Weight must be numeric and cannot be negative")> _
Public Property Weight() As Double
<Required(), Range(0, Double.MaxValue, ErrorMessage:="Recycled content must be numeric and between 0 and 100")> _
Public Property RecycledContent() As Double
And in my viewmodel I have:
if (!editComponent().entityAspect.validateProperty("recycledContent")) {
/* do something about errors */
var msg = 'Recycled content is invalid!';
logger.logError(msg, error, system.getModuleId(lt_articleEdit), true);
}
And yet when I enter a value greater than 100 (in the recycled content field) it still passes validation somehow! I have used the script debugger to step through and in the breeze validation routine there are two validators registered which are "required" and "number" but nothing that I can see mentions the range.
Can breeze do range validation? All I'm trying to do is pick up a data validation error based on metadata from the data annotations of the model and use this to trigger a client-side highlight on the field in error and log an error message.
It's a very reasonable request but we aren't quite there yet.
Right now Breeze doesn't YET pick up Range validations from Entity Framework metadata. Please vote for this on the Breeze User Voice . This matters because we do prioritize our work based on this venue.
Related
I have a model written using Entity Framework Code First called Project.
In my UI, there is a page called ProjectEdit as shown in the image below. As seen in the image below, Customer and BOMs are Lookup Edit.
I'm trying to load Customer and BOMs to Lookup Edit but it's not working. Below is my code.
//New
if (entity == null)
{
Entity = new Project();
}
//Edit
else
{
ProjectCodeTextEdit.DataBindings.Add("EditValue", entity, "ProjectCode");
DescriptionTextEdit.DataBindings.Add("EditValue", entity, "Description");
CustomerLookUpEdit.DataBindings.Add("EditValue", entity, "CustomerId");
BOMsLookUpEdit.DataBindings.Add("EditValue", entity, "BOMs");
}
Below is my LookUpEdit Properties.
Generally LookUpEdit object's data binding is not implemented the same way as a TextEdit object's. While in TextEdits's case you just need to assign the variable value to EditValue property (I suppose your TextEdits binding work fine, isn't it?), with LookUp Edit you should assign variables to ValueMember and a DisplayMember properties of the object. That is why we usually display data rows with LookUpEdit objects, where ValueMember is the identification field of the row and DisplayMember is the field of the row whose value you wish to be displayed.
In your case you should be more clear about what you wish to display in your lookupedits. Each Project instance has one Customer property and many BOMs, right? So CustomerLookUpEdit will show one record and BOMsLookUpEdit a list of values according to the Project object that was chosen for edit, correct? I suppose that both your Customer and BOM classes have some kind of ID property and description property of their own. In this case you should bind these values to the LookUpEdits. eg. in your initialization function code add these lines
CustomerLookUpEdit.Properties.DataSource = entity.Customer;
CustomerLookUpEdit.Properties.ValueMember = "someCustomerIDpropertyName" ;
CustomerLookUpEdit.Properties.DisplayMember = "someCustomerDescriptionpropertyName";
BOMsLookUpEdit.Properties.DataSource = entity.BOMs;
BOMsLookUpEdit.Properties.ValueMember = "someBOMIDpropertyName" ;
BOMsLookUpEdit.Properties.DisplayMember = "someBOMDescriptionpropertyName" ;
You can read more in this topic https://documentation.devexpress.com/#WindowsForms/clsDevExpressXtraEditorsLookUpEdittopic
When we are adding entities to a List, we have to take care of our DataSource if is a DBContext or a DBSet, each one has implications in the compiler, that was your case, in this case you had to especify your DataSource like a DBSet and get the Entities
Add<TEntity>(TEntity entity)
The type parameter omitted is posible because the compiler will infer it.
I've written a class validator as a service and passed the doctrine entity manager to this validator. At this point everything works fine.
But now I need the unchanged object of $entry which is been updated in the form - or at least the previous values. I've tried some approaches, but did not succeed:
public function isValid($entry, Constraint $constraint)
{
$oldEntry = $this->em->getRepository('SomeBundle:Entry')->findOneBy(array('id' => $entry->getId()));
Doctrine fetches the same (changed) object as expected. But trying to refresh the object will reset both versions of the object:
$newEntry = clone $entry;
$this->em->detach($newEntry);
$this->em->refresh($entry);
$hoursOfOldEntry = $entry->calculateHours();
$this->em->merge($newEntry);
Another option could be to save the values of the object as array, refresh the object and reassign the saved values again after working on the original values. But this does not seem to be the best way, especially if the are many relations. I don't wont to touch the object within a validator, I just need the previous values!
Another approach could be using Doctrine\ORM\UnitOfWork#recomputeSingleEntityChangeSet(Doctrine\ORM\ClassMetadata $meta, $entity). But I don't think it's a good idea to use internal doctrine methods in a validator!
So how do I get the original object or the change set in a class validator?
This won't get you the original entity, but should get you a key/value array of the original fields:
$uow = $em->getUnitOfWork();
$originalData = $uow->getOriginalEntityData($entry);
http://www.doctrine-project.org/api/orm/2.0/source-class-Doctrine.ORM.UnitOfWork.html#2210
I have a code first EF model with concurrency tokens on some of the entities.
These tokens are defined as byte[] properties and are decorated with [Timestamp] attributes.
[Timestamp]
public byte[] ConcurrencyStamp { get; set; }
(I've also tried any combination of Timestamp and ConcurrencyCheck)
The properties are also marked as concurrency tokens in OnModelCreating of my context:
modelBuilder.Entity<Room>().Property(x => x.ConcurrencyStamp).IsConcurrencyToken();
So, here's the scenario:
Some of the entities are serialized as JSON and passed on to external clients. When a client updates an object, that changed object is received again (as Json) and the changes are applied to a freshly fetched object. In this process, I also update the concurrencytoken value received from the client to the object just fetched from the db. Then, when saving changes, no concurrency error is thrown even if the values don't match.
So, to summarize:
1. fetch object from DB
2. serialize object to JSON (including concurrencytoken)
3. client messes with object
4. server receives updated object as json
5. fetch object (by id) from DB
6. apply json values to fetched object (including concurrencytoken)
7. context.savechanges
--> no error if token was changed
Checking the log, it seems that EF is doing the update statement with the "fetched" concurrencytoken when saving changes, not the token set manually from the external object.
UPDATE [dbo].[Rooms]
SET [RoomName] = #0, [ConcurrencyStamp] = #1
WHERE (([RoomId] = #2) AND ([ConcurrencyStamp] = #3))
-- #0: 'new room name' (Type = String, Size = -1)
-- #1: '1500' (Type = Int64)
-- #2: '1' (Type = Int32)
-- #3: '1999' (Type = Int64)
(I've used longs here, but the same applies to byte[] stamps, which I tried initially).
1999 is the current concurrencytoken value in the DB. 1500 is the token coming from the JSON object, which was set manually by setting the property.
Even though you can see EF updating the token in the statement (because I set the property) it is still using the original token value to do the check.
Changing the properties through the change tracker doesn't help, the behaviour stays the same.
Any clues? Is this scenario not supported? Am I doing something wrong?
Update
The check does work. When creating a new context in a separate thread and doing a change between the fetch and savechanges (thus between step 5 and step 7), the savechanges in step 7 barfs with a ConcurrencyException.
So it appears it works as described, but there's no way to "force" the token to be updated externally (which might make sense in a way, I guess).
You actually can force it.
You just need to set timestamp like this:
customerRequest.RowVersion = detachedRequest.RowVersion;
Context.Entry(customerRequest).Property(p => p.RowVersion).OriginalValue = customerRequest.RowVersion;
Context.Entry(customerRequest).Property(p => p.RowVersion).IsModified = false;
After that ef will think that its not updated and will throw concurrency exception on update.
tested on ef 6 code first.
EF always uses the OriginalValue of the timestamp fetched in step 5 in its UPDATE statement in step 7.
Setting entity.ConcurrencyStamp = viewModel.ConcurrencyStamp in step 6 only updates the CurrentValue.
To set the OriginalValue, do this in step 6 instead:
dbContext.Entry(entity).Property(e => e.ConcurrencyStamp).OriginalValue =
viewModel.ConcurrencyStamp;
I'm using Play Framework 2 and Ebean. When a user submits a form to edit an existing object in the database, it doesn't save null values. I guess this is to prevent overwriting fields that aren't in the form with null. But how can I let them set fields in the form to null if they need to?
For example, the user edits an Event object. Event.date is 1/1/13. The user sets the Event.date field in the form to empty and submits the form. Inspecting Event.date in the debugger shows its value is null. I save the Event. If I look at the Event in the database, its value is still 1/1/13.
Edit: It seems there is a method for this. The only problem is it doesn't work on nested entities. Any solutions for this?
update(Object bean,Set<String> properties)
Create an ebean.properties file right next to the application.conf file and add this line to it:
ebean.defaultUpdateNullProperties=true
Null properties in Ebean are considered as unloaded, so to prevent accidental nulling properties that shouldn't be nulled, they are just excluded.
Because of this reverting Date (and other fields) to null in Ebean is... hard :). Last time when I had to do the same thing (revert Date) I used second query to do just... nulling the Date (after event.update(Object o)):
public static Result updateEvent(){
Form<Event> eventForm = form(Event.class).bindFromRequest();
// do some validation if required...
Event event = eventForm.get();
event.update(event.id);
if (eventForm.get().date == null){
Ebean
.createUpdate(Event.class, "UPDATE event SET date=null where id=:id")
.setParameter("id", page.id).execute();
}
}
On the other hand, if you are using comparison, for filtering events (always selecting newer than X), you can just set the date to very 'old' value, which also should do the trick. In this case you'll update the object only once.
private static final Date VERY_OLD_DATE = new GregorianCalendar(1, 0, 1).getTime();
public static Result updateEvent(){
Form<Event> eventForm = form(Event.class).bindFromRequest();
Event event = eventForm.get();
if (eventForm.get().date == null){
event.date = VERY_OLD_DATE;
}
event.update(event.id);
}
In this case in your HTML form you will need to clear the value of the form's field (or just send every time date like 0001-01-01), however it can be done easily even with JavaScript.
I'm using Symfony 1.2 with Doctrine. I have a Place model with translations in two languages. This Place model has also a nested set behaviour.
I'm having problems now creating a new place that belongs to another node. I've tried two options but both of them fail:
1 option
$this->mergeForm(new PlaceTranslationForm($this->object->Translation[$lang->getCurrentCulture()]));
If I merge the form, what happens is that the value of the place_id field id an array. I suppose is because it is waiting a real object with an id. If I try to set place_id='' there is another error.
2 option
$this->mergeI18n(array($lang->getCurrentCulture()));
public function mergeI18n($cultures, $decorator = null)
{
if (!$this->isI18n())
{
throw new sfException(sprintf('The model "%s" is not internationalized.', $this->getModelName()));
}
$class = $this->getI18nFormClass();
foreach ($cultures as $culture)
{
$i18nObject = $this->object->Translation[$culture];
$i18n = new $class($i18nObject);
unset($i18n['id']);
$i18n->widgetSchema['lang'] = new sfWidgetFormInputHidden();
$this->mergeForm($i18n); // pass $culture too
}
}
Now the error is:
Couldn't hydrate. Found non-unique key mapping named 'lang'.
Looking at the sql, the id is not defined; so it can't be a duplicate record (I have a unique key (id, lang))
Any idea of what can be happening?
thanks!
It looks like the issues you are having are related to embedding forms within each other, which can be tricky. You will likely need to do things in the updateObject/bind methods of the parent form to get it to pass its values correctly to its child forms.
This article is worth a read:
http://www.blogs.uni-osnabrueck.de/rotapken/2009/03/13/symfony-merge-embedded-form/comment-page-1/
It gives some good info on how embedding (and mergeing) forms work. The technique the article uses will probably work for you, but I've not used I18n in sf before, so it may well be that there is a more elegant solution built in?