4
\$\begingroup\$

Purpose:

I've on occasion required a method for changing a (or a set of) Display names (formats, order, etc.). The problem was I'd have to push to production just to change something as simple as display name of a column. To fix this, we decided on storing this metadata information into the database (or really anywhere else).

IMetadata:

public interface IMetadata
{
    String ObjectFullName { get; set;  } // Including namespace, used as key.
    String PropertyName { get; set; } // Excluding namespace, used as short-key.
    String DisplayName { get; set; }
    String DisplayFormat { get; set; }
    Int32 DisplayOrder { get; set; }
}

IMetadataContainer:

public interface IMetadataContainer
{
    IDictionary<String, IMetadata> Metadata { get; set; }
}

IMetadataProvider:

public interface IMetadataProvider
{
    String ObjectFullName { get; set; }
    void Fill(IMetadataContainer container, IEnumerable<IMetadata> metadataSource, Boolean prefixObjectFullName);
}

MetadataBase:

public class MetadataBase : IMetadata
{
    public String ObjectFullName { get; set; }
    public String PropertyName { get; set; }
    public String DisplayName { get; set; }
    public String DisplayFormat { get; set; }
    public Int32 DisplayOrder { get; set; }

    public MetadataBase(String objectFullName, String propertyName) : this(objectFullName, propertyName, "", "{0}", Int32.MaxValue) { }
    public MetadataBase(String objectFullName, String propertyName, String displayName = "", String displayFormat = "{0}", Int32 displayOrder = Int32.MaxValue)
    {
        this.ObjectFullName = objectFullName;
        this.PropertyName = propertyName;
        this.DisplayName = String.IsNullOrWhiteSpace(displayName) ? this.PropertyName : displayName;
        this.DisplayFormat = displayFormat;
        this.DisplayOrder = displayOrder;
    }
}

MetadataContainerBase:

public class MetadataContainerBase : IMetadataContainer
{
    public IDictionary<String, IMetadata> Metadata { get; set; }

    public MetadataContainerBase(IEnumerable<IMetadata> metadata)
    {
        this.Metadata = metadata.ToDictionary(m => m.PropertyName, m => m);
    }
}

MetadataProviderBase:

public class MetadataProviderBase : IMetadataProvider
{
    public String ObjectFullName { get; set; }

    public MetadataProviderBase(Type type) : this(type.FullName) { }
    public MetadataProviderBase(String objectFullName)
    {
        this.ObjectFullName = objectFullName;
    }

    /// <summary>
    /// Fills the container with the metadata.
    /// </summary>
    /// <param name="fullName">Full name of the object whose metadata you'd like to put into the container.</param>
    /// <param name="container">Object receiving the metadata.</param>
    /// <param name="metadataSource">Collection of some/all metadata available.</param>
    /// <param name="prefixObjectFullName">If true, the key for each piece of metadata will be the object's full name followed by the property's name.</param>
    public static void Fill(String fullName, IMetadataContainer container, IEnumerable<IMetadata> metadataSource, Boolean prefixObjectFullName)
    {
        // VALIDATE:
        if (container == null) { return; }

        container.Metadata = container.Metadata ?? new Dictionary<String, IMetadata>();

        // FILL:
        IEnumerable<IMetadata> metas = metadataSource.Where(m => m.ObjectFullName == fullName);
        if (metas == null || !metas.Any()) { return; }

        foreach (IMetadata meta in metas)
        {
            String key = prefixObjectFullName ? meta.ObjectFullName + meta.PropertyName : meta.PropertyName;
            container.Metadata.AddOrReplace(key, meta);
        }
    }
}

My Area of concern

Is there any areas of this structure that may make it less friendly to extension or resuse? That being my primary goal, I do try my best to write very readable code as well.

If you have any tips on those areas (or any other areas really), I would love to hear them!

\$\endgroup\$
  • \$\begingroup\$ Minor quibble: I'd use the C# language alias string instead of the framework type String. \$\endgroup\$ – Jesse C. Slicer Apr 22 '15 at 14:23
1
\$\begingroup\$
if (container == null) { return; }

The brackets here are unnecessary and can be removed. This is probably down to your preferred style though (frankly, I prefer all if bodies to be on their own line surrounded by braces).

public interface IMetadata
{
    String ObjectFullName { get; set;  } // Including namespace, used as key.
    String PropertyName { get; set; } // Excluding namespace, used as short-key.
    String DisplayName { get; set; }
    String DisplayFormat { get; set; }
    Int32 DisplayOrder { get; set; }
}

You've used occasional comments to clear up intention in your interfaces, but these would be significantly more useful if incorporated into proper XML comment structures. If you're aiming to reuse these interfaces, then the benefits of full XML commenting of your interfaces will be apparent, even more so if you intend for others to use them.

Additionally, Stylecop recommends you use string and int and bool over String and Int32 and Boolean. This is, obviously, a matter of style, but I certainly consider int more readable than Int32.

Your // VALIDATE: and // FILL: comments add nothing. It's obvious from the code what bits do what. Use your non-XML comments to explain why you're doing something the way you are, and not how.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your feedback. I do have plans to add more xml-comments. \$\endgroup\$ – Shelby115 Apr 22 '15 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.