Realm The property type Tender[] cannot be expressed as a Realm schema type (Parameter 'type')

Good day. Struggling a bit to get this REALM to sync. I’m seeing some erronious, what feels to me, errors and maybe my backed instance has an issue, but lets see what the smart people can see.

I have a pretty basic collection for sales in C# with two nested arrays of objects.

[Serialized]
public class Sale : RealmObject
    {
        private DateTime _businessDay;
        private DateTime _startTime;
        private DateTime _endTime;
        public Sale()
        {
            _id = ObjectId.GenerateNewId();
            Payment = new List<Tender>().ToArray();
            SalesItems = new List<LineItem>().ToArray();
        }
        public enum SaleIndexes
        {
            SALE_REF_ID_INDEX,
            BUSINESS_DATE_INDEX,
            LOCATION_REF_ID_INDEX,
            WHO_START_INDEX,
            WHO_END_INDEX,
            SALE_TYPE_INDEX,
            SERVICE_AREA_INDEX,
            START_TIME_INDEX,
            END_TIME_INDEX,
            GUEST_COUNT_INDEX,
            SALE_TIME_BLOCK_INDEX,
            NET_TOTAL_INDEX,
            GROSS_TOTAL_INDEX,
            TENDER_TYPE_INDEX,
            TENDER_INDEX,
            TIP_INDEX,
            GRATUITY_INDEX,
            TAX_INDEX,
            ITEMS_INDEX
        }
        
        [BsonId]
        [PrimaryKey]
        public ObjectId _id { get; set; }

        [BsonElement("sale_ref_id")]
        public int SaleRefId { get; set; }
        [BsonElement("business_day")]
        public DateTime BusinessDay
        {
            get { return DateTime.SpecifyKind(_businessDay, DateTimeKind.Utc); }
            set { _businessDay = (DateTime)value; }
        }
        
        [BsonElement("location_ref_id")]
        public ObjectId LocationRefId;
        [BsonElement("who_start")]
        public int? WhoStart { get; set; }
        [BsonElement("who_end")]
        public int? WhoEnd { get; set; }
        [BsonElement("sale_type")]
        public string? SaleType { get; set; }
        [BsonElement("service_area")]
        public int? ServiceArea { get; set; }
        [BsonElement("start_time")]
        public DateTime? StartTime
        {
            get { return DateTime.SpecifyKind(_startTime, DateTimeKind.Utc); }
            set { _startTime = (DateTime)value; }
        }
        [BsonElement("end_time")]
        public DateTime? EndTime
        {
            get { return DateTime.SpecifyKind(_endTime,DateTimeKind.Utc); }
            set { _endTime = (DateTime)value; }
        }
        [BsonElement("guest_count")]
        public double GuestCount { get; set; }
        [BsonElement("time_block")]
        public double TimeBlock { get; set; }
        [BsonElement("net_total")]
        public double NetTotal { get; set; }
        [BsonElement("gross_total")]
        public double GrossTotal { get; set; }
        [BsonElement("payment")]

        public Tender[] Payment { get; set; }
        [BsonElement("tax")]
        public double Tax { get; set; }
        [BsonElement("gratutity")]
        public double Gratuity { get; set; }
        [BsonElement("sales_items")]
        public LineItem[] SalesItems { get; set; }

    }

The error in the subject is thrown when I try to initialize the REALM and the sync should happen, but we never get there.

The Tender model is as such. Nothing crazy?

[Serializable]
    public class Tender : EmbeddedObject
    {
        public Tender()
        {
            this._id = ObjectId.GenerateNewId();
        }
        [BsonId]
        public ObjectId _id { get; set; }
        [BsonElement("sale_ref_id")]
        public int SaleRefId { get; set; }
        [BsonElement("tender_type")]
        public string TenderType { get; set; } = "";
        [BsonElement("value")]
        public double Value { get; set; }
        [BsonElement("tip")]
        public double Tip { get; set; }
    }
}

There’s a similar model for the LineItem[] SalesItems property but that’s not complaining…yet.

Is this error familiar to some of the Mongo Champs out there?

Thanks in advance.
CPT

Realm doesn’t support arrays of objects - instead you should use IList<T> with a getter only. I realize this may be tricky if you also want to use the same models using the C# drivers, but you can probably work around it similarly to what you’re doing with dates.

1 Like

Thanks for the response. I though I’d give it a crack but if Realm doesn’t support arrays of objects then what’s the practicle point of that? Is this on the Engineer’s whiteboard?

Meaning, if I understand correctly; for example, if I want to do a Sales collection with lineItems in the sale (i.e. array of IList that’s a non-starter inheriting from RealmObject?

Correct me if I’m wrong, as this question is important for me to continue, but… Is this the only way to have my Atlas database design workable with REALM? Meaning, that I’d need two separate collections? One for Sales and one for the LineItems. Then on the client side they’d need to be constucted.

I must be mis-understanding something fundamental? That seems crazy.
I though I could hack this by doing constructors passing in the IList objects since they can only be initialized in a construct since “set;” is disabled. Then just swapping out the new object with a new IList. nope.

That doesn’t seem to work as soon as you inherit from RealmObject it just ignores the lists when writing to Atlas.

OR, do I need separate objects with the exact same properties:

  1. One object model inheriting from RealmObject to access the local Realm except it can’t write and I’d have to write back to atlas via some direct method? Which means Realm is likely to get circular timing issues, I’m possitive.
  2. One object model that does NOT inherit from RealmObject to do my writting to Atlas (at least the IList properties.

That seems like a lot o’ work.

Nonetheless, I’ve been trying to get that straigt as it doesn’t seem intuitive. Or I just need to keep hacking until it falls into place. Feels like I’m going backwards.

Best regards,
CPT

Hacking along, I can push into Atlas with Embeded objects in a IList and it actually makes it to the REALM visibly, but accessing them isn’t supported even though the viewer shows them?

image
This example shows the List with the 7 expected LineItems, but sure…the program which loads to atlas then initialzes the REALM I can see the data.

I built a similar Model that inherits Realm Object (SyncSales). Of course I can’t use “set” on the IList properties and it complains on properties that aren’t part of that Sales Model.

I assume its reflecting on other data in the program (Product, Employee’s collections)

So, it’s there but not accessible is what is happening? And it’s back to generating a collection around the mirrored SyncSales model I created for the client side access which inherits from RealmObject. I suspect I won’t be able to delete that collection until I kill the Realm from App services like last time.

Interesting :frowning:

Maybe I’m misunderstanding something here - can you show some code examples of what you’re trying to achieve? It seems like you’re using Device Sync via the Realm SDK, but you’re also trying to use the .NET driver in the same project - while there’s nothing that inherently prevents it, it’s not clear to me why you need this. I.e. Device Sync will synchronize data two way, so whatever you write to the Realm on the device will eventually make it into Atlas.

Regarding collections in Realm - they are implemented as lists rather than arrays to convey their mutability and to encourage people to add/remove/update items rather than replace the entire collection. For example, if you want to add an item, you can do:

realm.Write(() =>
{
    sale.SaleItems.Add(new LineItem(...));
});

Then only that change will get synchronized and the document in atlas will be updated to reflect that. If you replace the contents of the collection every time, you’re going to

  1. synchronize a lot more data and
  2. make conflict resolution less precise - e.g. imagine two clients that want to add an item to a sale - if both replace everything in the collection, conflict resolution will select the last client to do it and set that as the state, whereas if they just add an item, you’ll end up with a collection that contains the items from both clients.

Finally, if you do want to reuse the models between the Realm SDK and the .NET driver, you can do something like that for the collection properties:

public class Sale : RealmObject
{
    public IList<LineItem> SaleItems { get; }

    [BsonElement("sale_items")]
    public LineItem[] SaleItemsBson
    {
        get => SaleItems.ToArray();
        set
        {
            SaleItems.Clear();
            foreach (var item in value)
            {
                SaleItems.Add(item);
            }
        }
    }
}

Again, while this is possible, I would discourage it as it is really inefficient.

1 Like

Thanks for that insight I think it gives me ideas for sure.

Well, I ended up trying to separate the two as you mentioned but while I “thought” I had a path I hit another wall. Essentially, I copied the models that were used to populate Atlas which did not inherit RealmObject, but the nested IList objects inherited EmbeddedObject.

I created a console app that just opened and downloaded the Sales. Worked!!
Then I took the copied Sales Model and inherited from RealmObject and removed the setters so it would compile. Then I just did a

var sales = _Realm_Sales.All();

Then it told me the schema’s didn’t match

ERROR “Invalid schema change (UPLOAD): failed to add app schema for ns=‘mongodb-atlas.ONE.Sales’ for new top-level schema “Sales”: sync-incompatible app schema for the same collection already exists. This app schema is incompatible with error: %!q(). To continue, delete the app schema in Atlas App Services, or update it to match the app schema defined on the device” (error_code=225, try_again=false, error_action=ApplicationBug)

As to why I’d have both? It’s for a restaurant (several. 100 to start and my parent company is 1500 with plans to double in 5 years in the US). The “idea” is that I’d put together a service that “upgrades” a POS from stand-alone to a real-time analytics (and more I could tell you;)) via MongoDB Atlas and Realm. The “problem” this company faces is that it buys 100’s or restaurants at a time but integration into central accounting systems etc is an incredably un-scalable problem. Yes, I’m just a lowly IT director of the 100 sub-companies, but have a dream. I’m far removed from “code” but love it!! :wink: So, everything is new!!

So, on start it loads the entire history of Sales, Employee and product data. So far that happens just fine.

Once loaded which can be 1M Sales but not limited to that. I’m working with a small set (one week) but there are 500,000 sitting there on my test box while I try to sort this next step out.

Once it loads it flips over to production mode and should monitor a sliding window of two weeks of sales.
That’s a business requirement as sometimes people make changes late and accounting always needs to reconcile that. It’s daunting with that many restaurants. So wouldn’t it be nice it it captured the change and it was NEVER out? That’s the basic idea.

So, now that the service is running everything from that day forward is Realm reads/writes.

That’s the basic principal

Of course I could share models code etc. All here?

Thanks for responding btw!!