Add a field on a foreign dictionary
Updated
Add a column to a dictionary you do not own. You never edit that model's files. The field lives in your model, on a layer strictly above theirs.
Note
This page is the field only. A handler on their object is a different door — Wrap with Chain of Command, overview in Extend another model. Bump your
modelVersion, not theirs.metaIdof existing rows does not change.
The worked JSON below is labelled. The aggregate Customer.WmsExt and field WmsCode are an example, not a product object. A real workspace pattern is Country.WMS / LegalEntity in Extend another model. Do not treat a customization pack as the default.
How to#
Preconditions, or the import refuses:
- A dependency on the owner model in your
model.json. - Your
layerIdis strictly above theirs (System0 →User4). You extend downward only. - You do not edit
Core/or a sealed model. You extend them the same way.
1. Dependency in your model.json:
"dependencies": [
{
"dependsOnModelMetaId": "<owner model guid>",
"minVersion": "1.0.0",
"name": "WmsExt->Inventory",
"metaId": "<new guid>"
}
]
Do not declare a dependency on Core. Do not create cycles. See Create a model.
2. The extension aggregate — one per (target, your model):
DictionaryExtensions/Customer.WmsExt/Customer.WmsExt.extension.json
{
"kind": "DictionaryExtension",
"object": {
"targetDictionaryMetaId": "<Customer guid>",
"description": "WMS code on the host customer (example aggregate, not a product object)",
"metaId": "<new guid>",
"name": "Customer.WmsExt",
"modelId": "<YOUR model guid>"
},
"fields": [
{
"extensionMetaId": "<aggregate guid>",
"dictionaryMetaId": "<Customer guid>",
"fieldName": "WmsCode",
"baseType": "String",
"length": 32,
"isSystem": false,
"isRequired": false,
"displayOrder": 50,
"isIndexed": false,
"isUnique": false,
"caption": { "en": "WMS code" },
"isTranslatable": false,
"isVisible": true,
"isCalculated": false,
"metaId": "<new guid>",
"name": "WmsCode",
"modelId": "<YOUR model guid>"
}
]
}
New GUIDs on the aggregate and on the field. The field's modelId is yours. The host dictionary's metaId does not change. Integer is Integer, not Int — Create a dictionary. A reference field is an EDT first, then edtMetaId on the field.
Documents use DocumentExtensions/<Target>.<YourModel>/ with targetDocumentTypeMetaId. This cookbook stops at the dictionary field.
3. Bump your modelVersion in the same change (minor: a new field). The tenant compares MetaPackages.PackageVersion to that number; an equal version skips your model as a whole. Live sync on a stand applies the file anyway. Tenants are the ones that skip — Bump the model version.
Turning your model off removes the field from the effective set.
The field is not on the host class#
The field does not appear on the generated target class. record.WmsCode does not compile. IDictionaryManager<Customer> does not return it.
Read the raw bag with the indexer:
var bag = await GetService<IDataService>().GetByIdAsync("Customer", id);
var code = bag?["WmsCode"];
Write with Db.UpdateAsync, repeating required host fields — an update rewrites the row:
await Db.UpdateAsync("Customer", id, new Dictionary<string, object?>
{
["WmsCode"] = "WH-42",
["Name"] = name, // required host fields again
});
Which host fields are required is the host dictionary's, not yours. Repeat every required field you already have in hand. Do not send only the extension column.
Put an enum into that bag as a number. Through REST the value travels by name; Db.UpdateAsync / IDataService write the integer column. A name there fails with Conversion failed when converting the nvarchar value 'VatPayer' to data type int, not "unknown enum value". A read may return either the number or the name — accept both. See Create an enum.
Warning
A handler on their object does not put the field on
Customer. CoC is a second door:[ExtensionOf("Customer")]andnextfrom every override. MissingnextisZOCOC001. Missing[ExtensionOf]on a foreign object isZOCOC004. Do not start that work on this page — Wrap with Chain of Command.
You cannot delete or rename a field of another model from yours. You cannot take it off the table. Hide it on the card with Hide field in the form layout — The form designer. A new field that is not in a saved layout still appears at the end, under Other fields.
Tip
A document posting script is typed on the document class, not on
Customer. If a posting needs this field, read the bag (GetByIdAsyncindexer). Do not writecustomer.WmsCode.
What's not there yet#
There is no way to delete or rename a field of another model from yours. There is no property on the generated host class and no typed getter on IDictionaryManager<T>. The bag and Db.UpdateAsync are the access path.