Generate classes from CoreData model

2019-09-14 21:41发布

问题:

I'm trying to develop a pair of C# applications, one iOS, one Mac, that use the same CoreData database. Effectively, the desktop app populates it, and then it's distributed as part of the iOS app.

I can use XCode to generate an .xcdatamodeld file describing what I want the database to look like. I can use momc to compile that into a .momd file. I can include the .mom file from within that into my Mono project, and load it into a NSManagedObjectModel, from which I can access all the properties of the various entities.

What I haven't yet figured out how to do is create an object of a class from the database, rather than accessing the properties of the table. Any suggestions?


To clarify: I want to be able to create a table/class in XCode, call it Person. I give it two fields: Name and Phone. I want to be able to run code similar to this in Mono:

using (var context = new NSManagedObjectContext())
{
  var me = context.Person.GetByID(1);
  me.Name = "Bobson";
  context.Save();
}

Obviously, the specifics of getting it from the database and saving it back will be different, but the gist is there.

回答1:

It isn't very difficult to use CoreData in MonoTouch. You need to generate the mom using momc.exe. I have successfully done it.

If you place the file on the top level of the application directory (maybe other locations will work too) and you set the build action to "BundleResource" (possibly other actions work too). Instantiating a UIManagedDocument will cause CoreData to automatically pick up the mom file, so you won't need to create it yourself, plus you gain the advantage of iCloud support.

You will have to generate the class files yourself. It should be easy to write a script that can read off a xcdatamodel file and spit out the C#. This is an example of how it should look.

public partial class Photographer : NSManagedObject
{
    public static NSString NameKey = (NSString) "name";
    public static NSString PhotosKey = (NSString) "photos";

    public Photographer(IntPtr handle) : base(handle)
    {
    }

    public string Name
    {
        get { return (NSString) Runtime.GetNSObject(ValueForKey(NameKey)); }
        set { SetValueForKey(value, NameKey); }
    }

    public NSSet Photos
    {
        get { return (NSSet) Runtime.GetNSObject(ValueForKey(PhotosKey)); }
        set { SetValueForKey(value, PhotosKey); }
    }

    void SetValueForKey(string value, NSString key)
    {
        base.SetValueForKey((NSString)(value ?? ""), key);
    }

    public static Photographer InsertNewObject(NSManagedObjectContext context)
    {
        return (Photographer) NSEntityDescription.InsertNewObjectForEntityForName("Photographer", context);
    }

    public static Photographer WithName(string name, NSManagedObjectContext context)
    {
        Photographer photographer = null;

        // This is just like Photo(Flickr)'s method.  Look there for commentary.

        if (name.Length > 0)
        {
            var request = new NSFetchRequest("Photographer")
            {
                SortDescriptors = new[] {new NSSortDescriptor("name", true, new Selector("localizedCaseInsensitiveCompare:"))},
                Predicate =  NSPredicate.FromFormat("name = %@", new NSObject[] {(NSString) name})
            };

            NSError error;
            var matches = context.ExecuteFetchRequest(request, out error);

            if (matches == null || matches.Length > 1)
            {
                // handle error
            }
            else if (matches.Length == 0)
            {
                photographer = InsertNewObject(context);
                photographer.Name = name;
            }
            else
            {
                photographer = (Photographer) matches.First();
            }
        }

        return photographer;
    }
}

public partial class Photo : NSManagedObject
{
    public static NSString ImageUrlKey = (NSString) "imageURL";
    public static NSString TitleKey = (NSString) "title";
    public static NSString UniqueKey = (NSString) "unique";
    public static NSString SubtitleKey = (NSString) "subtitle";
    public static NSString WhoTookKey = (NSString) "whoTook";

    public Photo (IntPtr handle) : base (handle)
    {
    }

    public string ImageUrl  {
        get { return (NSString)Runtime.GetNSObject(ValueForKey(ImageUrlKey)); }
        set { SetValueForKey(value, ImageUrlKey); }
    }

    public string Subtitle  {
        get { return (NSString)Runtime.GetNSObject(ValueForKey(SubtitleKey)); }
        set { SetValueForKey(value, SubtitleKey); }
    }

    public string Title  {
        get { return (NSString)Runtime.GetNSObject(ValueForKey(TitleKey)); }
        set { SetValueForKey(value, TitleKey); }
    }

    public string Unique  {
        get { return (NSString)Runtime.GetNSObject(ValueForKey(UniqueKey)); }
        set { SetValueForKey(value, UniqueKey); }

    }

    public Photographer WhoTook  {
        get { return (Photographer)Runtime.GetNSObject(ValueForKey(WhoTookKey)); }
        set { SetValueForKey(value, WhoTookKey); }
    }

void SetValueForKey(string value, NSString key)
{
    base.SetValueForKey((NSString) (value??""), key);
}

    public static Photo InsertNewObject(NSManagedObjectContext context)
    {
        return (Photo) NSEntityDescription.InsertNewObjectForEntityForName("Photo", context);
    }

    public UIImage Image
    {
        get {
            if (string.IsNullOrEmpty(ImageUrl))
                return null;

            var imageData = NSData.FromUrl(new NSUrl(ImageUrl));
            if (imageData == null)
                return null;
            return new UIImage(imageData);
        }
    }


回答2:

I believe this is what you are after:

public partial class Item : NSManagedObject
{
   internal Item(string entityName)
      : base(NSEntityDescription)m_managedObjectModel.EntitiesByName.ObjectForKey(new NSString(entityName)), m_managedObjectContext)

Where entityName is the name from the MOM.



回答3:

As per @t9mike's comment (which I will accept if he ever makes it an answer), I gave up on using CoreData in favor of a more cross-platform approach. I ended up using Vici CoolStorage for the ORM, although I had to embed the source in my project in order to get it working.