2

I designed a database, generated c# code with sqlmetal, and it all works.
Now, I'm working on a UI, and I want to add some attributes to the classes created by sqlmetal.
after reading this SO Q&A: Can I define properties in partial classes, then mark them with attributes in another partial class? and this atrticle: MSDN - MetadataTypeAttribute Class I tried the following:

[MetadataType(typeof(GUI.metadata.BooksMetaData))]
public partial class Book
{
    public void fun()
    {
        
    }
}

namespace GUI.metadata
{
    public class BooksMetaData
    {
        [DisplayName("hello")]
        public object Shelf { get; set; }
    }
}

I checked if VS reconizes the attributes of Book in the function fun, and it dosen't, so I'm not suprised that the DisplayName attribute made no change.

what I'm doing wrong and how to fix it?

(I'm using c#, VS 2010 pro and the code generated by sqlmetal is in a different .dll than the GUI is in)

Community
  • 1
  • 1
elyashiv
  • 3,623
  • 2
  • 29
  • 52
  • You will save yourself a lot of pain by creating ViewModels in your UI to represent your entities. They will automagically allow the attributes too. – Simon Whitehead Sep 18 '13 at 08:59

2 Answers2

2

Please note that in the thread you mentioned one of the comments say: "it is not a general solution to the question posed by the OP. Consumers of the attributes still need to know to look for the meta data class -- i.e. these attributes will not be returned by Attribute.GetCustomAttribute(...)." Which means it's not a generic solution to add attributes. It's a solution which requires that methods which reads attributes understands "MetadataType" a just goes to another class for additional attributes. It does not happen automatically.

I know it's not a full answer, but I would suggest some kind of post-processing with Mono.Cecil, for example:

// pseudo code!

// for every type in assembly...
foreach (var targetType in assembly.Types)
{
    // find type which adds attributes to original type
    var sourceTypeName = targetType.Name + "Attributes";
    var sourceType = assembly.Types.FirstOfDefault(t => t.Name = sourceTypeName);

    if (sourceType == null) continue; // no type adding attributes

    // for each property in this type...
    foreach (var targetProperty in targetType.Properties)
    {
        // find property which is supposed to have additional attributes
        var sourceProperty = sourceType.Properties.FirstOfDefault(
            p => p.Name = targetProperty.Name);
        if (sourceProperty == null) continue; // no such property

        // copy attributes
        foreach (var sourceAttribute in sourceProperty.Attributes)
        {
            targetProperty.Attributes.Add(sourceAttribute);
        }
    }
}
Milosz Krajewski
  • 1,160
  • 1
  • 12
  • 19
  • thanks. I don't really like this approach. Is there any other way? I'm not fixed on attributes, I just need a simple way to customize the table. – elyashiv Sep 19 '13 at 18:19
0

Other approach is to use interfaces. You can extract the interface from the generated class into another file. Then add the attributes you want to the interface members.

Example:

public IGeneratedClassInterface {
     [SomeAtribute("valx")]
     public string field1{get;}
     ...
}

//must be on the same namespace of the generated class
public partial Class GeneratedClassInterface :IGeneratedClassInterface {}

Any field you want to hide, just remove from the interface.

Any schema modification will result on a compile error before break your interface.

MiguelSlv
  • 14,067
  • 15
  • 102
  • 169