Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Alexander Oksman 4 posts 85 karma points
    Jan 21, 2021 @ 15:17
    Alexander Oksman
    0

    Overwrite Property in Partial Class (Create fallback Property)

    Hello! First timer here, gonna keep it short as I can!

    I'm trying to implement a second part of the partial class SeoControls in one of my projects, SeoControls is a composition with different metadata properties that I define in Backoffice.

    What I'd like to do is, for example, if I don't have the Meta Description defined in backoffice in the SeoControls, it will fallback to another property of the same page, so for example my IntroContent property (Short Description).

    https://our.umbraco.com/forum/using-umbraco-and-getting-started/81273-extend-modelsbuilder-generated-classes

    I attempted to follow this thread, but was unable to get results, this below is the first generated property that I want to "overwrite" in my second Seocontrols file, so that I can manipulate the return and point towards another property if it's empty.

            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Umbraco.ModelsBuilder.Embedded", "8.9.1")]
        [ImplementPropertyType("metaDescription")]
        public string MetaDescription => GetMetaDescription(this);
    
        /// <summary>Static getter for Meta Description</summary>
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Umbraco.ModelsBuilder.Embedded", "8.9.1")]
        public static string GetMetaDescription(ISeocontrols that) => that.Value<string>("metaDescription");
    

    This below is my attempt at "overwriting" the property in another partial class in the same namespace

        public partial class Seocontrols
    {
    
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Umbraco.ModelsBuilder.Embedded", "8.9.1")]
        [ImplementPropertyType("metaDescription")]
        public string MetaDescription
        {
            get
            {
                if (String.IsNullOrWhiteSpace(GetMetaDescription(this)))
                {
                    var test = (string)this.Parent.GetProperty("introContent").Value();
                    if(String.IsNullOrWhiteSpace(test))
                    {
                        return test;
                    }
                    return "";
                }
                else
                {
                    return GetMetaDescription(this);
                }
            }
        }
    }
    

    I hope I've explained it well enough for you to understand, I'm currently a student thus I have very limited knowledge :)

    Thanks in advance!

    Edit:

    I'm using AppData as the current models builder setup, and the issue is currently it complains of it being duplicate MetaDescription property as they are both inside each of the SeoControl classes.

    If I remove it from the generated one it doesn't give any compilation errors, obviously it will be recreated when models are generated once again, making sure the errors come back.

    If I rename the property to something else, for example: FindMetaDescription, I can't seem to reach that property in my view via @Model.GetProperty("findMetaDescription").

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Jan 21, 2021 @ 16:01
    Søren Gregersen
    0

    Hi,

    A partial class, is just a part of the class in another file. This means you cant "override" a property in the way that you propose, since it is the same a having two properties in the same file, with the same name.

    What we (the company I work for) normally do, is to put the logic either into the view rendering that meta-tags, or in a controller that can expose the data via the rules you specify.

    The closest to your original proposal is to create a property with another name, or create a GetMetaDescription() method in your partial class. You could also implement GetMetaDescription() as an extension method.

    HTH :)

  • Isaiah Pellarp 19 posts 73 karma points
    Jan 21, 2021 @ 16:06
    Isaiah Pellarp
    0

    I'm not sure that qualifies as an answer to the question? Even creating a property with a different name in this scenario wouldn't result in the issue OP has being resolved. It seems he even wrote:

    If I rename the property to something else, for example: FindMetaDescription, I can't seem to reach that property in my view via @Model.GetProperty("findMetaDescription").

    So renaming won't work.

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Jan 21, 2021 @ 17:54
    Søren Gregersen
    0

    I missed that part ;)

  • Alexander Oksman 4 posts 85 karma points
    Jan 21, 2021 @ 16:07
    Alexander Oksman
    0

    Hello thanks for such a quick reply!

    My first solution I tried for this issue is probably the same to what you do at your company, all of this was implement in a PartialView for MetaTags and then a service class to do all the logic: check if exist, if not use this etc, but I was told by my tutor that there's a better way to manipulate this within the modelsbuilder straight away and is best practice in Umbraco 8.

    The closest to your original proposal is to create a property with another name, or create a GetMetaDescription() method in your partial class. You could also implement GetMetaDescription() as an extension method.

    How would I go about doing as you describe? I tried adding an property with another name namely findMetaDescription and then tried to implement in the view @Model.findMetaDescription / GetProperty("FindMetaDescription") but was unable to get anything from those properties.

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Jan 21, 2021 @ 17:58
    Søren Gregersen
    0

    Hi,

    It seems you are trying to invoke the properties as if they are value on the published content. They are not, they are properties created in your code, and need to be called like Model.[Property], in this case @Model.FindMetaDescription() or @Model.MetaDescription depending on what you implement.

    HTH :)

  • Alexander Oksman 4 posts 85 karma points
    Jan 21, 2021 @ 18:40
    Alexander Oksman
    0

    I tried that, this is the Seocontrols.extended that I have:

        public partial class Seocontrols
    {
    
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Umbraco.ModelsBuilder.Embedded", "8.9.1")]
        [ImplementPropertyType("findMetaDescription")]
        public string FindMetaDescription
        {
            get
            {
                if (String.IsNullOrWhiteSpace(GetMetaDescription(this)))
                {
                    var test = (string)this.Parent.GetProperty("introContent").Value();
                    if(String.IsNullOrWhiteSpace(test))
                    {
                        return test;
                    }
                    return "";
                }
                else
                {
                    return GetMetaDescription(this);
                }
            }
        }
    }
    

    So here I take GetMetaDescription() from the original generated file and return that in a different property namely FindMetaDescription. In one of the views that have this "composition" (seocontrols.generated.cs) I tried using @Model.FindMetaDescription, @Model.FindMetaDescription() and @Model.FindMetaDescription and all of them return compilation error:

    CS1061  'Country' does not contain a definition for 'FindMetaDescription' and no accessible extension method 'FindMetaDescription' in the specific page view. Country who inherits SeoControls.
    

    This below happens inside my search page, when I try to post the results of the search and then show that property.

    CS1061  'IPublishedContent' does not contain a definition for 'FindMetaDescription' and no accessible extension method 'FindMetaDescription'
    

    If you'd like to see specifics on how the search is built it's following a tutorial from Paul Seal (stripped of extra code, just barebones):

    var results = Umbraco.ContentQuery.Search(searchQuery, string.Empty);
                            results = results.Take(count);
    
                            if (resultCount > 0)
                            {
                                foreach (var result in results)
                                {
                                                @result.Content.FindMetaDescription
                                }
    
                            }
    

    Thanks again for putting in the time, really doing my head in this one :)

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Jan 21, 2021 @ 19:11
    Søren Gregersen
    0

    Hi,

    The objects your are working with are exposed as IPublishedContent. You are creating a new property on a different type.

    Starting with Seocontrols:

    You have implemented a property, named like a method. This only exists on the Seocontrols class. In order to use the property, you need to make sure your content inherits from Seocontrols, otherwise you get the error you mention:

    CS1061  'IPublishedContent' does not contain a definition for 'FindMetaDescription' and no accessible extension method 'FindMetaDescription'
    

    In the error message, you will also find another solution to this.

    Normally you will never get content that it the type of your composition, Seocontrols, but rather something that implements the Seocontrols, as defined in ISeocontrols.

    You could then create an extension method, to allow this:

    public static class PublishedContentExentions{
        public static string GetMetaDescription(this IPublishedContent content){
            //find description
        }
    }
    

    in the method, you can then check if the content implements ISeocontrols:

        public static string GetMetaDescription(this IPublishedContent content){
             if(content is ISeocontrols seocontrols){
               return seocontrols.MetaDescription;
             }
            return null;
        }
    

    You can also add more checks, matching other types:

        public static string GetMetaDescription(this IPublishedContent content){
             if(content is ICategory category){
               return category.CategoryDescription;
             }
             if(content is ISeocontrols seocontrols){
               return seocontrols.MetaDescription;
             }
            return null;
        }
    

    HTH :)

  • Isaiah Pellarp 19 posts 73 karma points
    Jan 22, 2021 @ 09:06
    Isaiah Pellarp
    0

    Hi,

    I'm in a similar situation here where to be able to call the custom "FindMetaDescription" string that was just added I needed to extend the interface ISeoControls, like such:

    public partial interface ISeoControls : IPublishedContent
    {
        string FindMetaDescription { get; }
    }
    

    This made reaching the property possible, but opened up new issues where I am now getting the following errors:

    'CountryPage' does not implement interface member 'ISeoControls.FindMetaDescription'
    'Search' does not implement interface member 'ISeoControls.FindMetaDescription'
    

    Unfortunately there is no way to generate this new property in the generated models from umbraco as it will just overwrite this on build. Any suggestions? :p

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Jan 22, 2021 @ 09:43
    Søren Gregersen
    0

    This is why extension method where invented :)

  • Isaiah Pellarp 19 posts 73 karma points
    Jan 22, 2021 @ 09:45
    Isaiah Pellarp
    0

    haha indeed, but for the sake of broadening our knowledge a bit, let us say I want to do this without implementing extension methods (in the way you do in your example) I feel like it should be possible by just extending the partials from umbracos generated files.

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Jan 22, 2021 @ 09:46
    Søren Gregersen
    0

    You can, but you need just need to implement the interface as the error says. In your CountryPage class you need to create a FindMetaDescription property with a getter, and the same in Search class.

  • Isaiah Pellarp 19 posts 73 karma points
    Jan 22, 2021 @ 09:54
    Isaiah Pellarp
    0

    In the corresponding generated.cs ? I will try it out and see if I can get it working :)

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Jan 22, 2021 @ 10:15
    Søren Gregersen
    101

    no - in another file, that you create

    public partial class CountryPage {
        public string FindMetaDescription{ get; }
    }
    

    https://our.umbraco.com/documentation/reference/templating/modelsbuilder/Understand-And-Extend

    Also, when a member starts with "Find" it sounds like it is an action than can do something. Normally properties are just "read / write". Find sounds like a method :)

  • Isaiah Pellarp 19 posts 73 karma points
    Jan 22, 2021 @ 10:24
    Isaiah Pellarp
    0

    Ahh okay, makes more sense now that you say it tbh. I tried implementing directly into the generated and it actually worked (not being overwritten) but it jump straight into the generated GetMetaDescription() method, so extending the other classes would make more sense to make it point to the correct method.

    Will update shortly :p

  • Isaiah Pellarp 19 posts 73 karma points
    Jan 22, 2021 @ 14:16
    Isaiah Pellarp
    1

    Heyo,

    Finally solved it!

    public partial class CountryPage : IPublishedContent, ISeoControls
        {
            public string FindMetaDescription 
            { 
                get 
                {
                    SeoControls test = new SeoControls(this);
                    return test.FindMetaDescription; 
                } 
            }
       }
    

    and then in the SeoControls:

    public partial class SeoControls : PublishedContentModel, ISeoControls
        {
            public string FindMetaDescription
            {
                get
                {
                    if (String.IsNullOrWhiteSpace(GetMetaDescription(this)))
                    {
                        var test = 
                        (string)this.AncestorOrSelf().GetProperty("ingress").Value();
                        if (test.Length > 0)
                        {
                            var finalIngress = test.Length <= 250 ? test : test.Substring(0, 
                                                         250);
                            var pattern = @"\(.*?\)";
                            var final = Regex.Replace(finalIngress, pattern, " ");
                            var finalString = final + "...";
                            return finalString;
                        }
                        return "";
                    }
                    else
                    {
                        return GetMetaDescription(this);
                    }
                }
            }
        }
    

    Thanks for all the help along the way to this solution, much appreciated! :)

  • Alexander Oksman 4 posts 85 karma points
    Feb 01, 2021 @ 13:44
    Alexander Oksman
    1

    Sorry for late answer, Isaiah have the final solution to our common issue, thanks for pointing us in the right direction Sören Gregersen =)

Please Sign in or register to post replies

Write your reply to:

Draft