breezejs: overriding displayname

淺唱寂寞╮ 提交于 2019-12-03 14:32:33

This is not YET well documented but you can simply set the 'displayName' property on any dataProperty and this will override the autogenerated display name and will be used for all validation messages for this property. So

 var custType = myEntityManager.metadataStore.getEntityType("Customer");
 var dp = custType.getProperty("companyName");
 dp.displayName = "My custom display name";

Also, please see "Customize the message templates" at the bottom of this page: Breeze Validation

I was wanting to do this also but I wanted to use the [DisplayName] attribute from my EF model. I couldn't find anyone that had an example of doing this so after I found a way I thought I would share.

First, I extended the metadata returned from my BreezeController:

[HttpGet]
public string Metadata()
{
    // Extend metadata with extra attributes
    JObject metadata = JObject.Parse(contextProvider.Metadata());
    string nameSpace = metadata["schema"]["namespace"].ToString();
    foreach (var entityType in metadata["schema"]["entityType"])
    {
        string typeName = entityType["name"].ToString();
        Type t = Type.GetType(nameSpace + "." + typeName);
        foreach (var prop in t.GetProperties())
        {
            foreach (var attr in prop.CustomAttributes)
            {
                string name = attr.GetType().Name;
                foreach (var p in entityType["property"])
                {
                    if (prop.Name == p["name"].ToString()) {
                        if (attr.AttributeType.Name == "DisplayNameAttribute") {
                            DisplayNameAttribute a = (DisplayNameAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayNameAttribute));
                            p["displayName"] = a.DisplayName;
                            break;
                        }
                    }
                }
            }
        }
    }

    return metadata.ToString();
}

Then I added a little javascript after the metadata load to poke the display names from the augmented metadata where Breeze wants to find them.

manager.fetchMetadata().then(function (md) {
    angular.forEach(md.schema.entityType, function (et) {
        var etype = manager.metadataStore.getEntityType(et.name);
        angular.forEach(et.property, function (p) {
            var prop = etype.getProperty(p.name);
            prop.displayName = p.displayName;
        });
    });

    console.log("starting app");
    angular.bootstrap($("#app"), ["app"]);
});

I'm using angular so if you aren't you can ignore the angular stuff and probably get the idea. This seems to work rather nicely. It should be pretty easy to extend this to other model attributes as well like the RegularExpression validation attribute. I'll probably work on that next.

FYI, some of this code is not optimized and could probably be refactored, prettied up a bit but I just got it working and thought I would share. If anyone has any suggestions of a better way let me know. Hopefully Breeze will allow extending the metadata in a more supported way in the future. This does seem like a bit of a hack.

Following jpcoder request for suggestions, here goes my slightly improved server portion:

JObject metadata = JObject.Parse(contextProvider.Metadata());
string nameSpace = metadata["schema"]["namespace"].ToString();
foreach (var entityType in metadata["schema"]["entityType"])
{
    string typeName = entityType["name"].ToString();
    Type t = Type.GetType(nameSpace + "." + typeName);

    IEnumerable<JToken> metaProps = null;
    if (entityType["property"].Type == JTokenType.Object)
        metaProps = new[] { entityType["property"] };
    else
        metaProps = entityType["property"].AsEnumerable();

    var props = from p in metaProps
                let pname = p["name"].ToString()
                let prop = t.GetProperties().SingleOrDefault(prop => prop.Name == pname)
                where prop != null
                from attr in prop.CustomAttributes
                where attr.AttributeType.Name == "DisplayNameAttribute"
                select new
                {
                    Prop = p,
                    DisplayName = ((DisplayNameAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayNameAttribute))).DisplayName
                };
    foreach (var p in props)
        p.Prop["displayName"] = p.DisplayName;
}

Looking at http://www.breezejs.com/sites/all/apidocs/files/a40_entityMetadata.js.html#l1452,

could this be improved by renaming the existing "name" value to nameOnServer (to satisfy the getDataProperty call) and inserting the DisplayNameAttribute value as "name"?

A necessary change to the server code is that, if your model classes are in different assemblies, you cannot use

Type t = Type.GetType(nameSpace + "." + typeName);

You need the namespace per type (which is in the metadata), and (I think) to use BuildManager to locate the appropriate types in different assemblies. The mapping from cSpaceOSpaceMapping might be achieved more elegantly, but I didn't have time to research the different json formatting options.

JObject metadata = JObject.Parse(UnitOfWork.Metadata());
string EFNameSpace = metadata["schema"]["namespace"].ToString();
string typeNameSpaces = metadata["schema"]["cSpaceOSpaceMapping"].ToString();
typeNameSpaces = "{" + typeNameSpaces.Replace("],[", "]|[").Replace("[", "").Replace("]", "").Replace(",", ":").Replace("|", ",") + "}";
        JObject jTypeNameSpaces = JObject.Parse(typeNameSpaces);

        foreach (var entityType in metadata["schema"]["entityType"])
        {
            string typeName = entityType["name"].ToString();
            string defaultTypeNameSpace = EFNameSpace + "." + typeName;
            string entityTypeNameSpace = jTypeNameSpaces[defaultTypeNameSpace].ToString();
            Type t = BuildManager.GetType(entityTypeNameSpace, false);

            IEnumerable<JToken> metaProps = null;
            if (entityType["property"].Type == JTokenType.Object)
                metaProps = new[] { entityType["property"] };
            else
                metaProps = entityType["property"].AsEnumerable();

            var props = from p in metaProps
                        let pname = p["name"].ToString()
                        let prop = t.GetProperties().SingleOrDefault(prop => prop.Name == pname)
                        where prop != null
                        from attr in prop.CustomAttributes
                        where attr.AttributeType.Name == "DisplayNameAttribute"
                        select new
                        {
                            Prop = p,
                            DisplayName = ((DisplayNameAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayNameAttribute))).DisplayName
                        };
            foreach (var p in props)
            {
                p.Prop["displayName"] = p.DisplayName;
            }
        }
        JObject metadata = JObject.Parse(this._context.Metadata());
        string EFNameSpace = metadata["schema"]["namespace"].ToString();
        string typeNameSpaces = metadata["schema"]["cSpaceOSpaceMapping"].ToString();
        typeNameSpaces = "{" + typeNameSpaces.Replace("],[", "]|[").Replace("[", "").Replace("]", "").Replace(",", ":").Replace("|", ",") + "}";
        JObject jTypeNameSpaces = JObject.Parse(typeNameSpaces);

        foreach (var entityType in metadata["schema"]["entityType"])
        {
            string typeName = entityType["name"].ToString();
            string defaultTypeNameSpace = EFNameSpace + "." + typeName;
            string entityTypeNameSpace = jTypeNameSpaces[defaultTypeNameSpace].ToString();
            Type t = BuildManager.GetType(entityTypeNameSpace, false);

            IEnumerable<JToken> metaProps = null;
            if (entityType["property"].Type == JTokenType.Object)
                metaProps = new[] { entityType["property"] };
            else
                metaProps = entityType["property"].AsEnumerable();

            var props = from p in metaProps
                        let pname = p["name"].ToString()
                        let prop = t.GetProperties().SingleOrDefault(prop => prop.Name == pname)
                        where prop != null
                        from attr in prop.CustomAttributes
                        where attr.AttributeType.Name == "DisplayAttribute"
                        select new
                        {
                            Prop = p,
                            DisplayName = ((DisplayAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayAttribute))).Name
                        };
            foreach (var p in props)
            {
                p.Prop["displayName"] = p.DisplayName;
            }
        }

        return metadata.ToString();

Improving jpcoder's answer ...

For me most of my DisplayName changes were to replace "PascalCaseFieldName" or "camelCaseFieldName" with "Upper Case Field Name". So rather than set every property DisplayName on the server, I applied a default function to set displayName.

End result was much less EF annotation required. My TypeScript is:

    manager.metadataStore.getEntityTypes().forEach(function (storeEntityType) {
        if (!(storeEntityType instanceof breeze.EntityType)) {
            throw new Error("loadExtendedMetadata found '" + storeEntityType
                + "' StructuralType that is not an EntityType (e.g. a ComplexType)");
        }

        var extEntityType = extendedMetadata.entitiesExtended.find((extendedEntityType) => {
            return extendedEntityType.shortName + ":#" + extendedEntityType.nameSpace === storeEntityType.name;
        });


        (storeEntityType as breeze.EntityType).getProperties().forEach((storeProperty) => {
            //Both NavigationProperty & DataProperty have displayName & nameOnServer properties
            var storeDataProperty = <breeze.DataProperty>storeProperty;

            var extProperty;
            if (extEntityType) {
                extProperty = extEntityType.propertiesExtented.find((extendedProperty) => {
                    return extendedProperty.name === storeDataProperty.nameOnServer;
                });
            }

            //Smart default: nameOnServer "PascalCaseFieldName" or "camelCaseFieldName" converted to "Upper Case Field Name"
            storeDataProperty.displayName = (extProperty && extProperty.displayName)
                || storeDataProperty.nameOnServer.replace(/^./, function (str) {
                    // first ensure the first character is uppercase
                    return str.toUpperCase();
                    // insert a space before all caps, remove first character (added space)
                }).replace(/([A-Z])/g, " $1").substring(1);
        });
    });
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!