How could I go about doing this programmatically? Subclass NSManagedObject and override -(void)awakeFromInsert;? Then I suppose set the attribute values in the methods implementation?
The way to get Core Data to do this, and IMO the right way to handle the situation, is to add another version of the model with the default values set on those properties, and configure automatic model migration.
I don't know whether migration will apply the new defaults to existing entities with nil values. (My guess would be not. Testing would bear the answer.)
Is there some reason you need to leave alone existing entities with nil values?
-awakeFromInsert would indeed be the right place to do this kind of thing in code.
Related
Is there a way to define which attributes of an entity should define its objects' NSManagedObjectID?
Rather than having to build extra dictionaries and loops to compare them by property values, I'd like to be able to just use -[NSManagedObjects isEqual:] directly, and the NSSet functionality that would also allow.
Put another way: if an entity has an attribute that would logically be its primary key, how to tell CoreData to use it as such for object equality tests?
You don't. And subclassing wouldn't help anyway.
Core Data doesn't care if you create duplicate records. As far as it's concerned, two managed objects are equal if they represent the same underlying instance in the persistent store. It has no other concept of "equal" between managed objects.
Subclassing doesn't help because, as the docs for NSManagedObject explain:
NSManagedObject itself customizes many features of NSObject so that managed objects can be properly integrated into the Core Data infrastructure. Core Data relies on NSManagedObject’s implementation of the following methods, which you therefore absolutely must not override: [...] isEqual:,
You can create your own methods for comparing managed objects any way you like, but any behavior that relies on isEqual: is going to get Core Data's standard behavior.
In summary, I'm trying to create instance-specific data-annotation attributes at runtime, based on database fields. What I have now works fine for creating the initial model, but falls over when the model is posted-back and the server-validation happens.
(I have the same input model being used in a collection within a viewmodel, but different validation must be applied to each instance in the collection....for example the first occurrence of the input may be restricted to a range of 1-100 but the next occurrence of the same model, prompted for on the same input page, would be a range of 1000-2000. Another may be a date, or a string that has to be 6 characters long.......)
I'll explain what I've done and where my issues are:
I've inherited DataAnnotationsModelMetadataProvider and provided my own implementation of GetMetadataForProperty (This doesn't have any bearing on the validation problem....yet)
I've inherited DataAnnotationsModelValidatorProvider and provided a facade implementation of GetValidators. What I want to do here is create new attributes based on my database-records and then pass those attributes through to the base implementation so the Validators are created accordingly.
However...... GetValidators is called at a PROPERTY level....When it is called with a propertyname that I want to apply validators to, I need to find the applicable DB record for this propertyname so I can find out what attributes I need to create....BUT...I can't get the DB record's key from just a propertyname of the value field.....In fact, the DB key is in the parent model.....So how do I get hold of it?!
I've tried using a static variable (YUK) and storing the key during a call for one property, and retrieving it during another call for my value field property....But because the model is serialised one-way and deserialised the opposite way I end up with my key being out-of-sync with my required attributes.
To add a slight complication I'm also using a custom model binder. I've overridden CreateModel as advised elsewhere on here, but I can't find a way of attaching metadata or additionalvalues to a PROPERTY of my output model....Only to the model itself....but how do I get at MODEL metadata/additionalvalues inside the GetValidators call for a PROPERTY ?
So....My question is twofold.....
1) Can anyone help me get my database-key from my custom-Model-binder to my GetValidators method on my ValidationProvider? Or maybe using my custom Metadata provider?
2) Is there a different, simpler, way of creating validators at runtime based on database records?
I think you are making this far more complicated than it needs to be. You just need to make whatever your validation criteria selectors are part of your view model. They don't necessarily have to be displayed (they can be stored in hiddens if they need to be kept for postback purposes).
Then you can use something like FluentValidation to create rules that say
RuleFor(model => model.myprop)
.When(model => model.criteria == whatever)
.GreaterThan(100)
.LessThan(1000);
Where criteria is whatever value you use to select when your property has to be in a certain range.
So that would mean you build your view model to include the criteria that is used for validation rule selection.
I'd asked this on the FluentValidation forums also and the lack of answers here as well as the advice against using Fluent from there led me to find my own solution (I understand this almost certainly means I'm doing something really bad / unusual / unnecessary!)
What I've ended up doing is assigning my controller static variable in my Custom Model Binder's CreateModel method, where I have access to the entire client model, rather than trying to do it through a custom MetaDataProvider. This seems to work just fine and gets me towards v1 of my app.
I'm not really happy with this solution though so will look to refactor this whole area in the coming months so would still appreciate any other comments / ideas people have about how to implement dynamic validation in a generic way.
I know this is an old question, but I am answering this so that many others can be benefited from this.
Please see the below article where they are loading the attributes from an xml
Loading C# MVC .NET Data Annotation Attributes From XML, Form Validation
I think you can follow the same approach and instead of reading from xml you can read from database and add these rules dynamically based on the model data type
You can refer the below approach also
DataAnnotations dynamically attaching attributes
I am trying to find a way of observing a Core Data relationship (more specifically the removed items of an unordered relationship) before it gets actually changed. You can imagine it as a "will change" notification known from other Cocoa APIs.
The reason is that I want to update the UI according to these changes and I need a parameter which isn't stored in the managed objects but in an other data structure (in my case it's the index from a NSArrayController). I can retrieve the parameter only if the items weren't removed from the relationship yet.
The only way of getting such "will change" notifications I could find was to override the KVO method -willChangeValueForKey:withSetMutation:usingObjects: in the custom NSManagedObject subclass and forward this information. The problem is the documentation says: "You must not override this method." and this makes me want to find a better solution.
Is the "will change" approach right in this case?
If so, how could I achieve it? If not, how should I solve the mentioned problem in another way?
You can just override the relationship’s setter method instead.
I would like to change the default value of some of the attributes in my core data model dynamically.
For instance, my app deals with real estate investment, and I have an attribute pertaining to interest rate (the type Float). If the user enters an interest rate of 3.5% (float value would be 3.5) for a particular property they are analyzing, I would like the value for the next property they analyze to automatically populate with 3.5.
Is there a way to accomplish this without subclassing NSManagedObject?
Good question - by default, managed objects are initialized with the default values given in the managed object model. But like you say, sometimes you might want a dynamic default value (the example Apple use in their own documentation is using the current date/time as a default value).
Unfortunately I don't believe there's a way to do this without subclassing NSManagedObject. There's an Apple recommended way to do this - rather than overriding the init method (not recommended), you instead use the awakeFromInsert method, which is called when the object in question is first inserted into the managed object context.
Here's what Apple say from their own docs:
awakeFromInsert:
You typically use this method to initialize special default property values. This method is invoked only once in the object's lifetime.
If you want to set attribute values in an implementation of this method, you should typically use primitive accessor methods (either setPrimitiveValue:forKey: or—better—the appropriate custom primitive accessors). This ensures that the new values are treated as baseline values rather than being recorded as undoable changes for the properties in question.
So to answer your original question - I can't think of a way to do this without subclassing NSManagedObject, and subclassing is the officially recommended approach for handling dynamic default values.
we are currently evaluating whether nHibernate supports the requirements for our project. We share the database with another application so that we are not completely free as regards changes to the schema.
Some columns are filled with unique and consecutive numbers (e.g. for invoices). The next number is determined by a stored procedure that also implements a locking algorithm so that the numbers are guaranteed to be consecutive.
On the one hand we could define a trigger on the respective tables that sets the value for the column when an empty or special value is provided. This would require changing the existing database definition - though it might be the most reliable way to implement this.
In order to avoid the change of the database definition we are trying to solve this in the nHibernate ORM. We've first tried to implement a user type that calls the stored procedure in NullSafeSet if an empty value is provided. Unfortunately, the connection and transaction of the provided command are not set yet when NullSafeSet is called.
How can we solve this with nHibernate?
Thanks in advance,
Markus
If you decide to go with trigger route, then you'll need to add generated attribute to your property mapping.
Generated properties are properties which have their values generated
by the database. Typically, NHibernate applications needed to Refresh
objects which contain any properties for which the database was
generating values. Marking properties as generated, however, lets the
application delegate this responsibility to NHibernate. Essentially,
whenever NHibernate issues an SQL INSERT or UPDATE for an entity which
has defined generated properties, it immediately issues a select
afterwards to retrieve the generated values.
Aside from that, I'm not quite sure how would you call stored procedure from NHibernate issued INSERT, without adding a trigger or default constraint on column.
Edit
Looks like NHibernate has a notion of class persisters, through the interface IEntityPersister. Maybe you could hack something out from that.
The persister attribute lets you customize the persistence strategy
used for the class. You may, for example, specify your own subclass of
NHibernate.Persister.EntityPersister or you might even provide a
completely new implementation of the interface
NHibernate.Persister.IClassPersister that implements persistence via,
for example, stored procedure calls, serialization to flat files or
LDAP. See NHibernate.DomainModel.CustomPersister for a simple example
(of "persistence" to a Hashtable).
You could start from NHibernate's source.
If you have the ability to add triggers to database, that would probably be the best, straightforward way, without investing too much time to fight with NHibernate's internals.