change attribute type in entity and migrate the coredata - core-data

I need to change my CoreData attribute type from one type to another and generate the NSManagedObject subclasss manually.
how to migrate the older version data to new one [enter image description here](https://i.stack.imgur.com/SZEkU.png)

since I need to change the attribute type this come under the heavyweight
migration. so I need to add new version for my coredata.
1.create new model version
follow only for creating new model version
Changing Attribute Type in Core Data with NSInferMappingModelAutomaticallyOption
2.core datamapping model
i) new file->mapping model (Core Data -> Mapping Model)
ii) Choose the source (from model) and target (to model) version of your model
iii) Create class(any name) as a subclass of NSEntityMigrationPolicy
iv) override a method called createDestinationInstances. Below is my override method which transfer id from string to int
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
let destMOC = manager.destinationContext
guard let destinationEntity = mapping.destinationEntityName else{
fatalError("Destination Entity name error")
}
print(destinationEntity)
guard let sId = sInstance.value(forKey: "id") as? String,
let name = sInstance.value(forKey: "name"),
let dId = Int(sId)
else{
fatalError("source object didnt have valube id")
}
let context = NSEntityDescription.insertNewObject(forEntityName: "Entity", into: destMOC)
context.setValue(name, forKey: "name")
context.setValue(dId, forKey: "id")
}
v) Register this class as custom entity migration policy in the mapping model (Model.xcmappingmodel -> File inspector -> third column -> Custom policy put class name as Module.className
(eg for registering class)
(eg for module and className)
[[3
that's it. if you run it will migrate to new version.

Related

NSArrayController NSTableView Core Data Binding integers

I have an NSTableView bound to an NSArrayController, which in turn is bound to Core Data. The table displays integer values from core data nicely, but if I edit the numbers in the table I get an error:
Unacceptable type of value for attribute: property = "armorclass"; desired type = NSNumber; given type = NSTaggedPointerString; value = 10.
Any suggestions of how I can convert this pointer string back to an Int16 before the Array Controller tries to save it back to Core Data?
I wrote the following ValueTransformer but it's not working properly. I always get the error: Cannot find value transformer with name StringIntegerValueTransformer
class StringIntegerValueTransformer: ValueTransformer {
override class func transformedValueClass() -> AnyClass { //What do I transform
return String.self as! AnyClass
}
override class func allowsReverseTransformation() -> Bool { //Can I transform back?
return false
}
override func transformedValue(_ value: Any?) -> Any? {
if let val = value {
return String(describing: val)
}
return "nil"
}
override func reverseTransformedValue(_ value: Any?) -> Any? { //Revert transformation
if let val = value {
return val as? Int16
}
return nil
}
}
fff
To register the value transformer override init in AppDelegate
override init()
{
let stringIntegerValueTransformer = StringIntegerValueTransformer()
ValueTransformer.setValueTransformer(stringIntegerValueTransformer, forName:NSValueTransformerName(rawValue: "StringIntegerValueTransformer"))
super.init()
}
And consider this note from the documentation
Your NSValueTransformer subclasses are not automatically listed in the Interface Builder bindings inspector. When inspecting a binding you can enter the name that the value transformer is registered with, but the functionality will not be present in Interface Builder’s test mode. When your application is compiled and run the transformer will be used
Add a number formatter to the text field.

How to create edge label in Arangodb?

java-driver 4.1.10 I've created database named mydatabase1 I've created a graph using Java so my question is that how to set edge label using Java code?
If you mean with "edge label" a field in an edge-document, you can set it when you call insertEdge like in the code below.
ArangoDB arangoDB = new ArangoDB.Builder().build();
// create database
arangoDB.createDatabase("myDatabase");
// create named graph
Set<EdgeDefinition> edgeDefinitions = Collections.singleton(
new EdgeDefinition().collection("myEdgeCollection").from("myVertexCollection").to("myVertexCollection"));
arangoDB.db("myDatabase").createGraph("myGraph", edgeDefinitions);
// create from vertex
BaseDocument from = new BaseDocument("myFromKey");
arangoDB.db("myDatabase").graph("myGraph").vertexCollection("myVertexCollection").insertVertex(from);
// create to vertex
BaseDocument to = new BaseDocument("myToKey");
arangoDB.db("myDatabase").graph("myGraph").vertexCollection("myVertexCollection").insertVertex(to);
// create edge
BaseEdgeDocument edge = new BaseEdgeDocument("myVertexCollection/myFromKey",
"myVertexCollection/myToKey");
edge.addAttribute("label", "value");
edge.addAttribute("whatever", 42);
arangoDB.db("myDatabase").graph("myGraph").edgeCollection("myEdgeCollection").insertEdge(edge);
Instead of using BaseEdgeDocument you can also use Map<String, Object>
Map<String, Object> edge = new HashMap<String, Object>();
edge.put("_from", "myVertexCollection/myFromKey");
edge.put("_to", "myVertexCollection/myToKey");
edge.put("label", "value");
edge.put("whatever", 42);
arangoDB.db("myDatabase").graph("myGraph").edgeCollection("myEdgeCollection").in sertEdge(edge);
or create your own POJO representing your edge. The edge needs at least the fields _from and _to. If you don't want to name the fields in your POJO _from and _to, you can use the annotation DocumentField with the values Type.FROM and Type.TO on two String fields of your choice.
public class MyEdge {
#DocumentField(Type.FROM)
private String from;
#DocumentField(Type.TO)
private String to;
public MyEdge() {}
..setter & getter
}

OpenXava populating Collection from database

I have a problem populating a detail view from the database.
application.xml snippet:
<module name="ModuleB">
<model name="B"/>
<view name="ViewB"/>
...
<mode-controller name="DetailOnly"/>
</module>
I have three entity classes:
#Entity
#Table(name="A")
class A {
#OneToMany(mappedBy = "a")
#ListProperties("col1")
#CollectionView("cs")
private Collection<C> cs;//+getter/setters
}
#Entity
#Table(name="A")
#View(name = "ViewB", ...)
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<C> cs;//+getter/setters
}
#Entity
#Table(name="C")
#View(name = "ViewC", ...)
class C {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
}
I want to read an existing B instance from the database by clicking on a link, then editing/modify it.
When I set the model object of the view with getView().setModel() or even using getView().findObject(), on the screen everything looks good, the collection shows its proper content.
On the other hand when i try to save it back, in the save action the getView().getEntity().getCs() collection is null.
What do I have to do to make the view being correspond to the entity behind?
I am using OpenXava 5.0.1, java 1.7
Important note: I am not allowed to change OpenXava version, since it is a legacy project.
My editing (20170126)
I've made a new class to avoid a reference problem:
#Entity
#Table(name="C")
#View(name = "ViewC", ...)
class D {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private B a;
}
and modified the class B accordingly:
#Entity
#Table(name="A")
#View(name = "ViewB", ...)
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<D> cs;//+getter/setters
}
But the result is the same: The records of the subview (Collection ViewC) are loaded from the DB,
and shown on the screen correctly, but I get an error message if I want to edit the Collection ViewC
( eg: add a new entry ):
"Impossible to execute Save action: Impossible to create: an object with that key already exists"
+ As before: in the save action the getView().getEntity().getCs() collection is null
OpenXava uses JPQL to obtain collection data instead of just calling the collection getter, this is for allowing filtering and ordering. The problem here is that the query sentence generated by OpenXava gets the data but the getter of the JPA entity not. Surely JPA/Hibernate is not very happy with a mappedyBy to a reference to another type. That is you write:
class B {
#OneToMany(mappedBy = "a")
#ListProperties(...)
#CollectionView("ViewC")
private Collection<C> cs;//+getter/setters
}
Where a is a reference to A, not to B.
Given that A and B map the same table, why not to use just one entity?

Building a NSManagedObjectModel from several models

There are several reasons why somebody wants to merge multiple NSManagedObjectModel's. If you search the web, all responses are that it is not possible or that it is only possible for two unrelated entities that share one or more relationships. See this and this link for example.
However with a bit or more work it is (I think) possible to merge NSManagedObjectModels, even if the entities are related (as in parent-child) or if the attributes are spread out across multiple models.
Though it will not show as readily in the Xcode model editor and out-of-box transitions (probably) won't work.
In the answer below my observations about core data and my code on merging several models. If you find any bugs or have suggestions for improvements, please respond here.
Some things I noticed:
Copying a NSPropertyDescription (attribute, relationship) copies all its values, but not the entity to which it belongs. Same for the destinationEntity and inverseRelationship.
Thus a copied NSPropertyDescription should be added to an entity. As a result, all the children entities of that entity automatically get the property as well.
Copying a NSEntityDescription does not include the parent entity. So the tree (of NSManagedObjectEntity) has to rebuild manually.
If you set the parent of an entity, that (child) entity will immediately and automatically inherit all its parent properties. In other words when you ask an entity for its attributes, this entity already knows about all its attributes. It will not first query its parent. (reasonable assumption)
Adding entities to a model fills in the destination entities and inverse relationship descriptions of the relationsDescriptions of the added entities.
if you do not set the name of any entity or property before using it, core data will complain. That is the copy by name instead of value aspect.
Adding a property to an entity which already has a property with the same name (either from itself or inherited from its ancestor) will make core data complain.
This translates into the following code:
extension NSPropertyDescription
{
var isPlaceholder : Bool { return self.userInfo?["isPlaceholder"] != nil }
}
extension NSEntityDescription
{
var isPlaceholder : Bool { return self.userInfo?["isPlaceholder"] != nil }
}
func mergeModels(models: [NSManagedObjectModel]) -> NSManagedObjectModel?
{
var entities : [String : NSEntityDescription] = [:]
//support functions
let makeEntity : String -> NSEntityDescription = { entityName in
let newEntity = NSEntityDescription()
entities[entityName] = newEntity
newEntity.name = entityName
return newEntity
}
let setParent : (String, NSEntityDescription) -> () = { parentName, child in
if let parent = entities[parentName]
{
parent.subentities.append(child)
}
else //parent has not yet been encountered, so generate it
{
let newParentEntity = makeEntity(parentName)
newParentEntity.subentities.append(child)
}
}
//rebuild model: generate new description for each entity and add non-placeholder properties
for model in models
{
for entity in model.entities
{
guard let entityName = entity.name else { fatalError() }
let mergedEntity = entities[entityName] ?? makeEntity(entityName)
//set entity properties
if !entity.isPlaceholder
{
mergedEntity.abstract = entity.abstract
mergedEntity.managedObjectClassName = entity.managedObjectClassName
}
//set parent, if any
if mergedEntity.superentity == nil, //no parent set
let parentName = entity.superentity?.name //but parent is required
{
setParent(parentName, mergedEntity)
}
//set properties
for property in entity.properties
{
if property.isPlaceholder ||
mergedEntity.properties.contains({$0.name == property.name})
{ continue }
let newProperty = property.copy() as! NSPropertyDescription
mergedEntity.properties.append(newProperty)
}
}
}
//generate final model
let mergedModel = NSManagedObjectModel()
mergedModel.entities = Array(entities.values) //sets the destination entity and inverse relationship descriptions
return mergedModel
}
In the managedObjectModel (xcode editor) set the "placeholder" flag in the user info dictionary of the entity and/or the property.
The model generation can be refined by setting additional keys in the user info dictionary to specify which model has the prime entity/attribute/relationship (settings) and appropriately adjusting this code fragment.
However, if you can avoid using multiple models then avoid it. Your life will be much simpler by sticking to the standard single Model approach.
[Disclaimer: as far as I can tell, this code should work. No guarantees though.]
The NSManagedObjectModel class has the following factory methods / constructors
class func mergedModel(from: [Bundle]?)
class func mergedModel(from: [Bundle]?, forStoreMetadata: [String : Any])
init?(byMerging: [NSManagedObjectModel]?)
init?(byMerging: [NSManagedObjectModel], forStoreMetadata: [String : Any])
The optional forStoreMetadata attribute allows to specify the models' version.
see https://developer.apple.com/documentation/coredata/nsmanagedobjectmodel
(I suspect these methods not being available at the time the op asked & answered the question.)

Automapper - How to reuse objects instead of creating new ones (this objects are in a list)

I got the following problem:
I got a Entity (from EF Code First) that looks like this.
public class Movie {
public ICollection<AudioQuality> AudioQualities {get;set;}
}
public class AudioQuality {
public Guid Id{get;set;}
public int Channels{get;set;}
}
//Automapper Init:
Mapper.CreateMap<Movie, MovieDto>();
Mapper.CreateMap<MovieDto, Movie>().ForMember(dest => dest.AudioQualities,opt => opt.UseDestinationValue());
Mapper.CreateMap<VideoQuality, VideoQualityDto>();
Mapper.CreateMap<AudioQuality, AudioQaulityDto>();
Mapper.CreateMap<VideoQualityDto, VideoQuality>();
Mapper.CreateMap<AudioQaulityDto, AudioQuality>()
.ForMember(dest => dest.Movies, opt => opt.Ignore());
And I also got a DTO that looks similar!
I map from the DTO to the Entity like so:
//Get Movie from DB and than update it with the dto values
Movie movieFromDb = GetMoviesWithAllChilds().Single(mov => mov.Id == movieDto.Id);
//use update the entity from db with dto
Mapper.Map(movieDto, movieFromDb);
Now the problem is, that Automapper creates a new AudioQuality-Object for each item in AudioQualities.
But I want that he just copies the values from the AudioQuality-Objects from the DTO to the Entities instead of creating new Objects with the values from the DTO.
Is there a way to do that??
FYI: UseDestinationValue works on the collection (so the collection is not copied).
br rene_r
Get your entity from the context and pass it in to the Mapper function as the second parameter. The updates will eb applied from the source to the destination.
mapper.Map<SourceType, DestType>(source, dest);

Resources