Tengo algunas clases modelo de EF que quiero crear. Cada clase tiene algunas propiedades comunes que quiero establecer antes de insertar una nueva entidad, por ejemplo:
public partial class BlogPost { public DateTime CreatedTime { get; set; } public string CreatorName { get; set; } public string PostTitle { get; set; } public string PostText { get; set; } } public partial class Comment { public DateTime CreatedTime { get; set; } public string CreatorName { get; set; } public string CommentText { get; set; } } ...Cuando creo estas clases, las estoy instanciando así:
var blogPost = new BlogPost { CreatedTime = DateTime.UtcNow, CreatorName = creatorName, PostTitle = postTitle, PostText = postText, }; var comment = new Comment { CreatedTime = DateTime.UtcNow, CreatorName = creatorName, ... }; ...Quiero crear un método para establecer automáticamente algunas de las propiedades comunes para que no tenga que escribirlas manualmente para cada clase con las mismas propiedades. Como no amplían la misma clase ni implementan la misma interfaz, me pregunto cómo se puede lograr esto. Mi primer pensamiento fue usar un método genérico; sin embargo, no sé si hay una manera de especificar qué propiedades debería tener el tipo genérico sin que extiendan la misma clase (similar al "escribiendo pato" de TypeScript). Mi método deseado se parece a esto:
public void SetInitialProperties<T>(T dbEntity, DateTime createdTime, string creatorName) where T : ??? { dbEntity.CreatedTime = createdTime; dbEntity.CreatorName = creatorName; } ... var blogPost = new BlogPost { PostTitle = postTitle, PostText = postText }; SetInitialProperties(blogPost, createdTime, creatorName); En el peor de los casos, si no puedo usar un genérico, siempre podría usar dynamic ; sin embargo, me gustaría mantener la verificación de tipos si es posible.
Puedes lograr lo que quieras usando la reflexión. Puede pasar un objeto y resolver su tipo, luego obtener todas las propiedades públicas de ese tipo dado y encontrar si tiene uno llamado CreatedTime por ejemplo. Luego puede establecer el valor de la propiedad dada en el objeto dbEntity pasado. Sin embargo, no recomiendo esta solución:
public void SetInitialProperties(object dbEntity, DateTime createdTime, string creatorName) { // get the passed object's properties and find the one called CreatedTime var createdTimePropertyInfo = dbEntity.GetType().GetProperties().Where(i => i.Name == "CreatedTime").FirstOrDefault(); // below line is equal to: dbEntity.CreatedTime = createdTime; createdTimePropertyInfo.SetValue(dbEntity, createdTime); var creatorNamePropertyInfo = dbEntity.GetType().GetProperties().Where(i => i.Name == "CreatorName").FirstOrDefault(); creatorNamePropertyInfo.SetValue(dbEntity, creatorName); }Sería mejor a largo plazo si crea una interfaz común o incluso una clase base abstracta para que no tenga que implementar CreatedTime y CreatorName y otras propiedades para cada modelo EF. Se vería como lo siguiente:
public interface IUserEntry { DateTime CreatedTime { get; set; } string CreatorName { get; set; } } public abstract class UserEntryBase : IUserEntry { public DateTime CreatedTime { get; set; } public string CreatorName { get; set; } } public partial class BlogPost : UserEntryBase { public string PostTitle { get; set; } public string PostText { get; set; } } public partial class Comment : UserEntryBase { public string CommentText { get; set; } }Y su SetInitialProperties sería bastante simple:
public void SetInitialProperties(IUserEntry dbEntity, DateTime createdTime, string creatorName) { dbEntity.CreatedTime = createdTime; dbEntity.CreatorName = creatorName; }Una vez que desarrollas una interfaz, logras mucha más flexibilidad que al usar la reflexión o un tipo dinámico, ya que obtienes la verificación en tiempo de compilación que se mencionó antes y puedes ver las propiedades comunes de tus modelos.
No puede hacer eso en C# porque C# usa un sistema de tipo nominal y no un sistema de tipo estructural .
Para su caso particular, debe crear una interfaz que contenga las propiedades en común y que ambas entidades implementarán, luego use esa nueva interfaz como su restricción de parámetro de función genérica.
Si está absolutamente seguro de que las propiedades tendrán el mismo nombre, puede pasar una dynamic para establecer los valores de propiedad. Sin embargo, esto evita que se verifique el tipo en tiempo de compilación, por lo que si pasa accidentalmente un tipo incompatible, no se detectará hasta el tiempo de ejecución.
public void SetInitialProperties(dynamic dbEntity, DateTime createdTime, string creatorName) { dbEntity.CreatedTime = createdTime; dbEntity.CreatorName = creatorName; }