¿Puedo definir propiedades en clases parciales y luego marcarlas con atributos en otra clase parcial?

82

¿Hay alguna manera de que pueda tener un archivo de código generado como este?

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

y luego en otro archivo:

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

¿Para poder generar una clase a partir de la base de datos y luego usar un archivo no generado para marcarlo?

Chris McCall
fuente
¿Cuánto se "genera a partir de la base de datos"? ¿Solo definiciones de propiedad, o también código?
snemarch
1
Respuesta corta, no. Respuesta larga, dup de stackoverflow.com/questions/456624/… .
Kirk Woll
@snemarch: solo definiciones de propiedad, planeo hacer cualquier otro código a mano.
Chris McCall
1
¿Le vendría bien una división de interfaz + implementación en lugar de una clase parcial? Genere la interfaz a partir de la base de datos, implemente (y agregue atributos) en la implementación.
snemarch
sí, esto es posible, pero con el uso de metadatos, el otro hereda parcial esos metadatos
CyberNinja

Respuestas:

33

He visto algo como esto hecho en un artículo de Scott Guthrie (casi al final), aunque no lo intenté yo mismo.
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
fuente
10
Vale la pena mencionar esta respuesta, pero no es una solución general a la pregunta planteada por el OP. Los consumidores de los atributos aún deben saber buscar la clase de metadatos, es decir, estos atributos no serán devueltos por Attribute.GetCustomAttribute (...). (Afortunadamente para muchos casos de uso, los consumidores están escritos por MS y en ciertas situaciones esto funcionará).
Kirk Woll
1
Esta solución NO resuelve el problema. ¿Por qué buscamos decorar miembros en otro archivo? Porque la clase se SOBRESCRIBE cada vez que se ejecuta el diseñador. Así, su atributo [MetaDataType ...se borrará cada vez que se ejecuta de diseño
2
@Desolator - La idea es que no pones el MetadataTypeatributo en el archivo generado por el diseñador, lo pones en el otro archivo donde Personse define la clase parcial .
Dan Dumitru
1
el problema con esta solución es que la clase debería haber sido parcial
CyberNinja
85

Aquí está la solución que he estado usando para tales casos. Es útil cuando tiene clases generadas automáticamente que desea decorar con atributos. Digamos que esta es la clase generada automáticamente:

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

Y digamos, me gustaría agregar un atributo para especificar que UserId es la clave. Luego crearía una clase parcial en otro archivo como este:

[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
fuente
Gran solucion Sabía que se podía decorar la clase parcial con atributos en varios archivos e incluso agregar interfaces y una clase heredada a su declaración, pero no hice la conexión con el MetadataTypeatributo. ¡Bien hecho!
Pflugs
¿Qué pasa si quiero agregar un atributo al constructor de UserProfile?
Botis
2
MetadataTypeno existe en .NET Core
WoIIe
2
Usos principales de .netModelMetadataType
YLJ
1
Se supone que @Dave Support llegará en .NET Core 3.0
Roger Willcocks
2

Esta es mi respuesta a
archivos de clases diferentes o puede combinar los metadatas en un mismo archivo pero mantener el espacio de nombres igual ... para que puedan verse obviamente.

Tenga en cuenta que cuando actualice su modelo, como agregar más columnas, también debe actualizar la clase del proyecto.

--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
fuente
1

Necesita definir una clase parcial para su Aclase como el siguiente ejemplo

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
fuente
0

No como tal; el compilador se quejará de que el miembro está definido en varias partes. Sin embargo, como el uso de atributos personalizados es de naturaleza reflexiva, puede definir una clase de "metadatos" y usarla para contener decoradores.

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
fuente
1
¿Con qué frecuencia el actor que está agregando los atributos a sus propiedades es también la persona que los consume y por lo tanto sabrá buscar las mágicas clases "Meta"?
Kirk Woll
Muy a menudo, en mi experiencia. Esto no funcionaría para un marco existente orientado a aspectos, pero si estuviera decorando su dominio con, digamos, atributos de validación personalizados, usted es quien los busca y puede definir dónde. Mi equipo ha hecho exactamente esto en uno de nuestros proyectos. La principal desventaja es no buscar la otra clase; está manteniendo dos clases paralelas en el desarrollo, una funcional y otra decorativa. Eso también sería un problema en las clases parciales, si pudiera definir campos / propiedades parciales en primer lugar.
KeithS