I'm not able to change the class name of a Realm Object that has properties pointing to other Realm Objects. A class like this, for example.
class OldClass: Object {
var id: String!
var dog: Dog! //this is a Realm Object (with its own table)
}
I've seen the simple examples of how to do this.
migration.enumerateObjects(ofType: "OldClass", { (oldObject, newObject) in
migration.create("NewClass", value: oldObject!)
})
I expect the above would work if the schemas for both OldClass and NewClass were the same, AND if all the properties were non-Realm objects. If the schemas are different, I've gathered that you can do something like this.
migration.enumerateObjects(ofType: "OldClass", { (oldObject, newObject) in
let obj = migration.create("NewClass")
obj["id"] = (oldObject["id"] as! String)
obj["newPropertyName"] = (oldObject!["oldPropertyName"] as! Int)
})
Neither of these example seem to work when your object has a property pointing to another Realm object though. At least this is what I suspect, since I get RLMException 'Can't create object with existing primary key value'.
My suspicion is that the 'existing primary key' is referring to the Dog object, and that in migrating from NewClass to OldClass, the migration is trying to re-create the Dog object (which already exists).
How do I properly perform this type of migration?
Unfortunately this feature is not implemented, we track it in https://github.com/realm/realm-cocoa/issues/2162. You can also find some useful info at https://github.com/realm/realm-cocoa/issues/4366.
Related
Here as you can see class SameData there are two instances data1 and data2. If I change data2 instances userName that is stored in the class properties it doesn't get updated in data1 instance of the same class.
In swift class should get updated in all instances when properties change in one instance. But why this is happening?
Code:
class SameData {
var userName = "Anonymous"
}
var data1 = SameData()
var data2 = SameData()
data2.userName = "Not Anonymous"
print(data2.userName)
print(data1.userName)
Result:
"Not Anonymous\n"
"Anonymous\n"
As this is a class and one instance is changing userName then all the instances of the class should adopt the change am I right? So, the result should be something like this.
Expected Result:
"Not Anonymous\n"
"Not Anonymous\n"
in the class properties
These aren't class properties. These are just instance properties, on two different instances. Naturally, changing one doesn't change the other. That's the whole point of instances. They're separate instances. These are just instance properties with default values, similar to writing:
class SameData {
var userName: String
init() {
self.userName = "Anonymous"
}
}
Class properties are marked with class (or similarly, static, which is roughly equivalent to class final).
That said, marking a username as a class constant doesn't really make sense. Presumably, each user should have their own username, and they should be independent. You should add more detail on the kind of data you're trying to model, so we can give more concrete advice on thow to handle it.
I will explain as an answer, since I can't fit it into comment.
In var data1 = SameData() the SameData() is the object, and data1 is like a finger pointing to that object.
Then when you say var data2 = SameData(): you create a second object, and point a finger data2 to that object.
So you have 2 objects, and 2 fingers pointing to each object respectively, neither object knows about existence of the other.
Now, if you do var data2 = data1, you are not creating a new object, instead you are pointing another finger data2 to the same object you previously created. So here you have 2 fingers pointing to the same object.
Hence in first case, by changing data1 you are changing the first object, but the second object (to which data2 points) remains intact.
In second case, by changing data1, you are changing the only object you have. And since both fingers point to that same object, they both see the change.
Lets look at the case of structures:
struct SomeData {
var userName = "Anonymous"
}
Structures, just like arrays and dictionaries are value types in swift, which means they have no finger pointing to the object (and there's no object), just the structure itself. So unlike the classes, the structures are copied on assignment. I.e. in
var data1 = SameData()
var data2 = data1
we create a structure data1, then we copy it to data2. As the result we have 2 identical instances of the structure. If we were to change one of the structures, the other structure won't change. E.g.
data2.userName = "Not Anonymous"
means we changed data2 and not data1
By the way, here's an excellent article from Apple on this topic:
How does Entity Framework work when something like the following occurs:
var myInstance = new MyObject();
// Do stuff
_myContext.MyObjects.Add(myInstance);
myInstance = null;
_myContext.SaveChanges();
I ran into this happening in a complex foreach-call and it still seem to do what was expected. But I am curious how it handles it and if it gives up tracking the object when the instance is null.
I am curious how it handles it and if it gives up tracking the object when the instance is null
In your example, the instance is not null - just the variable myInstance value is null, i.e. the variable does not hold reference to the object you've created.
What about how EF Core tracks the instances, in simplified form you can think of that as MyObjects being a List<MyObject> (the actual implementation of course is different). So what happens when you do something like this:
var myObjects = new List<MyObject>();
var myInstance = new MyObject();
// Do stuff
myObjects.Add(myInstance);
myInstance = null;
myInstance variable is null, but myObjectList holds a reference to the created object (i.e. is "tracking" it), so it can always be get back, in this case with
var trackedInstance = myObjects[0];
Again, the actual implementation is different, but the concept is the same - the DbContext instance contains some sort of a list with all "tracked" entity instances and their state (Added, Deleted, etc.).
Using the (modified) examples in the Realm Swift documentation:
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
let puppies = List<Puppies>()
}
class Person: Object {
dynamic var name = ""
dynamic var picture: NSData? = nil // optionals supported
let dogs = List<Dog>()
}
class Puppies: Object {
dynamic var name = ""
}
Let's assume that the Person.name = Bob, and that Bob has several dogs added to his dogs List. I have added another model class called Puppies, which would represent puppies that belong to Bob's dogs. (Apparently Bob owns a kennel.)
How would I get the values to display the names of Bob's dogs and the number of puppies belonging to each dog in a UITableview?
More specifically, what is the code to extract the property values of the List of dogs that belong to Bob. I assume that once I get those values it won't be difficult to list them in the tableview cells.
I decide to use the slightly modified example from the documentation instead of my own code so that those who read this won't have to try and interpret my code, and be able to focus on the solution.
I have been able to save my data and believe I have made the relationships between the objects link properly, but don't know how to get the values of the List objects, based on the primary key I have in my top level model. The problem I have is that (using the example above): the puppies know what dog they belong to, and the dog knows the person it belongs to, but the inverse relationships don't seem to work.
(By the way; I used the LinkingObject examples in the documentation in a playground and it throws and error. I'm not sure if the examples are incomplete in some way.)
In the Realm Browser (displaying the Person object) I can see the data as entered but the link that shows [Dog] has a 0 next to it and when I click on the link, the table that shows is blank. Maybe solving that issues will be the answer to make everything else work.
Please excuse my ignorance. I'm still learning.
Thanks to Ahmad F. for pointing me in the right direction.
Here is the answer:
I did not know how to append to the list property in each of the object classes. Following the example above, it is done by creating a variable that holds the Person object. Then the realm.write function would look something like this.
newDog = Dog()
newDog.name = "Phydeaux"
.....
try! realm.write {
currentPerson?.dogs.append(newDog)
I am study about Realm db, this db is nice as compare with core data but I am stuck on one place as follows:
I have two RLMObject in that I created relationship and I want to run join query (sub query) on that, but I can't do that.
first object (table) in Ralm
class Dog : RLMObject
{
dynamic var name = ""
dynamic var age = 0
// create variable of Owner object
dynamic var owner = RLMArray(objectClassName: "Owner")
override class func primaryKey() -> String!{
return "name"
}
}
second object (table) in Ralm
class Owner : RLMObject{
dynamic var myName = ""
}
so I want to fetch only those Dog names which belong with owner name 'ram'
I tried following sub query
var dog = Dog.allObjects().objectsWithPredicate(NSPredicate(format:"SUBQUERY(Owner, $owner, $owner.myName = 'ram')", argumentArray: nil))
but app is crashing with following error
RealTest[1701:17960] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "SUBQUERY(owner, $owner, $owner.myName = 'ram')"'
also I search it on net I found that realm.objects but it gave me error about not found.
Thanks in advance!
Your predicate should look like this:
let predicate = NSPredicate(format:"SUBQUERY(Owner, $owner, $owner.myName = 'ram') .#count > 0", argumentArray: nil)
The idea here is to make sure you add .# count > 0 at the end, as the predicate needs to return true or false for it to work.
This can be achieved using a query like:
Dog.allObjects().objectsWhere("ANY owner.myName = 'ram'")
SUBQUERY is only necessary if you have multiple constraints on the target of the relationship that must all be fulfilled by a single row, or if you wish to express a constraint other than ANY / ALL / NONE on the number of rows that match.
That said, as of Realm Objective-C and Swift 0.98.0 SUBQUERY is now supported.
While Realm supports filtering objects via NSPredicate, at this time of writing, Realm's implementation of NSPredicate yet support every single type of keyword that the native Apple frameworks do, including SUBQUERY. Realm provides an NSPredicate cheat sheet on its website, outlining which types of queries it presently supports.
That being said, if you already have an Owner object at this point, you can actually use another Realm method on the Owner object ([RLMObject linkingObjectsOfClass:forProperty:]) to find out which Dog objects are referencing it.
Finally, that realm.objects error is because that syntax is from the native Swift version of Realm, and the code you're using here is the Objective-C version of Realm, bridged over to Swift.
Let me know if you need any more clarification!
I have a situation where I'm trying to filter a LINQ select using a derived sub class.
ctx.BaseEntity.OfType<SubClass>() - this works fine.
However I'd like to do this using a string value instead. I've come across a performance barrier when I have lots (>20) Sub Classes and selecting an Entity without using OfType just isn't an option. I have a generic UI that renders from the base class, so I don't know what Class Type will be returned at compile time.
So what I'd like to do is this:
Perform a projected Select where I
return just the SubClassType from
the database
Perform a second select
using this value as the OfType to
only select the relevant related
entity from the database (No mass
unions generated)
int id = 1;
var classType = (from c in ctx.BaseClass.Include("ClassType")
where c.id == id
select new
{
c.ClassType.TypeName
}).First();
BaseClass caseQuery = ctx.BaseClass.OfType<classType.TypeName>()
.Include("ClassType")
.Include("ChildEntity1")
.Include("ChildEntity2")
.Where(x => x.id== id);
But obviously this won't work because OfType requires a Type and not a string.
Any ideas on how I can achieve this?
Update:
As a side note to the original question, it turns out that the moment you project a query that uses a Navigation Property - it builds the monster SQL too, so I've ended up using a stored procedure to populate my ClassType entity from the BaseClass Id.
So I've just got it to work using eSQL, which I'd never used before. I've posted the code here just in case it helps someone. Has anyone else got a more strongly typed solution they can think of?
BaseClass caseQuery = ctx.BaseClass.CreateQuery<BaseClass>("SELECT VALUE c FROM OFTYPE(Entities.[BaseClass],namespace.[" + classType.TypeName + "]) as c")
.Include("ClassType")
.Include("ChildEntity1")
.Include("ChildEntity2")
.Where(x => x.id== id).FirstOrDefault();
To answer the headline question about calling OfType with a string / runtime type, you can do the following:
// Get the type, assuming the derived type is defined in the same assembly
// as the base class and you have the type name as a string
var typeToFilter = typeof(BaseClass)
.Assembly
.GetType("Namespace." + derivedTypeName);
// The use reflection to get the OfType method and call it directly
MethodInfo ofType = typeof(Queryable).GetMethod("OfType");
MethodInfo ofTypeGeneric = method.MakeGenericMethod(new Type[] { typeToFilter });
var result = (IQueryable<Equipment>)generic.Invoke(null, new object[] { equipment });
Combine this with your stored procedure to get the class name and you (should?) avoid the massive join - I don't have table-per-type implementation to play with so I can't test.