Czy mogę zdefiniować właściwości w klasach częściowych, a następnie oznaczyć je atrybutami w innej klasie częściowej?

84

Czy istnieje sposób na wygenerowanie pliku kodu w następujący sposób:

public partial class A {
public string a {get; set;}
}

a potem w innym pliku:

public partial class A {
[Attribute("etc")]
public string a {get; set;}
}

Więc mogę mieć klasę wygenerowaną z bazy danych, a następnie użyć nie wygenerowanego pliku, aby ją oznaczyć?

Chris McCall
źródło
Ile jest „generowane z bazy danych”? Tylko definicje właściwości czy też kod?
snemarch,
1
Krótka odpowiedź, nie. Długa odpowiedź, kopia stackoverflow.com/questions/456624/… .
Kirk Woll,
@snemarch: tylko definicje właściwości, planuję zrobić każdy inny kod ręcznie.
Chris McCall,
1
Czy mógłbyś zrobić z podziałem interfejsu + implementacji zamiast częściowej klasy? Wygeneruj interfejs z bazy danych, zaimplementuj (i dodaj atrybuty) w implementacji.
snemarch
tak, jest to możliwe, ale przy użyciu metadanych inne częściowe dziedziczą te metadane
CyberNinja,

Odpowiedzi:

33

Widziałem coś takiego zrobionego w artykule Scotta Guthrie (pod koniec) - chociaż sam tego nie próbowałem.
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx

[MetadataType(typeof(Person_Validation))]
public partial class Person
{
    // Partial class compiled with code produced by VS designer
}

[Bind(Exclude="ID")]
public class Person_Validation
{
    [Required(ErrorMessage = "First Name Required")]
    [StringLength(50, ErrorMessage = "Must be under 50 characters")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Last Name Required")]
    [StringLength(50, ErrorMessage = "Must be under 50 characters")]
    public string LastName { get; set; }

    [Required(ErrorMessage = "Age Required")]
    [Range(0, 120, ErrorMessage = "Age must be between 0 and 120")]
    public int Age { get; set; }

    [Required(ErrorMessage = "Email Required")]
    [Email(ErrorMessage = "Not a valid email")]
    public string Email { get; set; }
}
Dan Dumitru
źródło
10
Warto wspomnieć o tej odpowiedzi, ale nie jest ona ogólnym rozwiązaniem pytania postawionego przez PO. Konsumenci atrybutów nadal muszą wiedzieć, aby szukać klasy metadanych - tj. Atrybuty te nie zostaną zwrócone przez Attribute.GetCustomAttribute (...). (Na szczęście w wielu przypadkach konsumenci są pisani przez firmę MS iw pewnych sytuacjach to zadziała).
Kirk Woll
1
To rozwiązanie NIE rozwiązuje problemu. Dlaczego staramy się ozdobić członków w innym pliku? Ponieważ klasa jest OVERWRITTEN przy każdym uruchomieniu projektanta. W ten sposób atrybut [MetaDataType ...zostanie wyczyszczony za każdym razem, gdy projektant uruchomi się
2
@Desolator - Pomysł polega na tym, że nie umieszczasz MetadataTypeatrybutu w pliku wygenerowanym przez projektanta, ale umieszczasz go w innym pliku, w którym Personzdefiniowana jest klasa częściowa .
Dan Dumitru
1
Problem z tym rozwiązaniem polega na tym, że zajęcia powinny być częściowe
CyberNinja
85

Oto rozwiązanie, którego używałem w takich przypadkach. Jest to przydatne, gdy masz automatycznie generowane klasy, które chcesz ozdobić atrybutami. Powiedzmy, że to jest klasa wygenerowana automatycznie:

public partial class UserProfile
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}

Powiedzmy, że chciałbym dodać atrybut określający, że UserId jest kluczem. Utworzyłbym wtedy klasę częściową w innym pliku, takim jak ten:

[Table("UserProfile")]
[MetadataType(typeof(UserProfileMetadata))]
public partial class UserProfile
{
    internal sealed class UserProfileMetadata
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
    }
}
Jean-François Beauchamp
źródło
Świetne rozwiązanie. Wiedziałem, że możesz ozdobić klasę częściową atrybutami w wielu plikach, a nawet dodać interfejsy i klasę dziedziczoną do jej deklaracji, ale nie utworzyłem połączenia z MetadataTypeatrybutem. Dobra robota!
Pflugs
A co jeśli chcę dodać atrybut do konstruktora UserProfile?
Botis,
2
MetadataTypenie istnieje w .NET Core
WoIIe
2
Zastosowania rdzenia .netModelMetadataType
YLJ
1
@Dave Support ma pojawić się w .NET Core 3.0
Roger Willcocks
2

To jest moja odpowiedź na
różne pliki klas lub możesz połączyć metadane w tym samym pliku, ale zachowaj przestrzeń nazw taką samą ... więc oczywiście mogą się widzieć.

Pamiętaj, że podczas aktualizowania modelu, np. dodawania kolejnych kolumn, musisz również zaktualizować klasę projektu.

--your model class
public partial class A {
    public string a {get; set;}
}

--your project class 
public class Ametadata {
     [Attribute("etc")]
     public string a {get; set;}
}


[MetadataType(typeof(Ametadata))]
public partial class A
{
}
CyberNinja
źródło
1

Musisz zdefiniować częściową klasę dla swojej Aklasy, tak jak w poniższym przykładzie

using System.ComponentModel.DataAnnotations;

// your auto-generated partial class
public partial class A 
{
    public string MyProp { get; set; }
}

[MetadataType(typeof(AMetaData))]
public partial class A 
{

}

public class AMetaData
{
    [System.ComponentModel.DefaultValue(0)]
    public string MyProp { get; set; }
}
Masoud Darvishian
źródło
0

Nie jako takie; kompilator będzie narzekał, że element członkowski jest zdefiniowany w wielu częściach. Ponieważ jednak użycie atrybutów niestandardowych ma charakter refleksyjny, można zdefiniować klasę „metadanych” i użyć jej do umieszczenia dekoratorów.

public class A
{
   public string MyString;
}

public class AMeta
{
   [TheAttribute("etc")]
   public object MyString;
}

...

var myA = new A();
var metaType = Type.GetType(myA.GetType().Name + "Meta");
var attributesOfMyString = metaType.GetMember("MyString").GetCustomAttributes();
KeithS
źródło
1
Jak często zdarza się, że aktor, który dodaje atrybuty do swoich właściwości, jest jednocześnie osobą je konsumującą i dlatego będzie wiedział, że należy szukać magicznych klas „Meta”?
Kirk Woll
Z mojego doświadczenia wynika, że ​​dość często. To nie zadziała w przypadku istniejącej platformy zorientowanej na aspekty, ale gdybyś dekorował swoją domenę, powiedzmy, niestandardowymi atrybutami walidacji, to ty ich szukasz i możesz określić, gdzie. Mój zespół zrobił dokładnie to w jednym z naszych projektów. Główną wadą jest brak szukania drugiej klasy; przy projektowaniu zachowuje dwie równoległe klasy, jedną funkcjonalną, drugą dekoracyjną. Byłby to problem również w klasach częściowych, gdybyś mógł w pierwszej kolejności zdefiniować częściowe pola / właściwości.
KeithS