Tambahkan anotasi data ke kelas yang dibuat oleh kerangka entitas

93

Saya memiliki kelas berikut yang dihasilkan oleh kerangka entitas:

public partial class ItemRequest
{
    public int RequestId { get; set; }
    //...

Saya ingin menjadikan ini bidang wajib

[Required]
public int RequestId { get;set; }

Namun, karena ini adalah kode yang dihasilkan, ini akan terhapus. Saya tidak dapat membayangkan cara untuk membuat kelas parsial karena properti ditentukan oleh kelas parsial yang dihasilkan. Bagaimana saya bisa mendefinisikan kendala dengan cara yang aman?

P. Brian.Mackey
sumber
Jika properti Anda adalah int, secara default diperlukan untuk modelbinder sehingga atribut [Wajib] Anda tidak akan menambahkan apa pun di sini.
Kirill Bestemyanov
@KirillBestemyanov - @ Html.ValidationMessageFor (model => model.Item.Item.ResourceTypeID) harus menggagalkan sisi klien. Itu tidak.
P. Brian.Mackey

Jawaban:

143

Kelas yang dihasilkan ItemRequestakan selalu menjadi partialkelas. Ini memungkinkan Anda untuk menulis kelas parsial kedua yang ditandai dengan anotasi data yang diperlukan. Dalam kasus Anda, kelas parsial ItemRequestakan terlihat seperti ini:

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

//make sure the namespace is equal to the other partial class ItemRequest
namespace MvcApplication1.Models 
{
    [MetadataType(typeof(ItemRequestMetaData))]
    public partial class ItemRequest
    {
    }

    public class ItemRequestMetaData
    {
        [Required]
        public int RequestId {get;set;}

        //...
    }
}
MUG4N
sumber
11
Kelas parsial tidak akan dibuat ulang. Itulah alasan mengapa ini didefinisikan sebagai parsial.
MUG4N
apakah Anda melewatkan pengubah parsial? Apakah Anda menggunakan namespace yang sama?
MUG4N
5
Pengguna .NET Core: gunakan ModelMetadataType alih-alih MetadataType.
Bob Kaufman
1
Anda dapat menempatkan kelas parsial di mana pun Anda inginkan selama namespace identik
MUG4N
40

Seperti yang dijawab MUG4N, Anda dapat menggunakan kelas parsial tetapi akan lebih baik menggunakan antarmuka sebagai gantinya. Dalam hal ini Anda akan mengalami kesalahan kompilasi jika model EF tidak sesuai dengan model validasi. Jadi, Anda dapat memodifikasi model EF Anda tanpa takut aturan validasi sudah usang.

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace YourApplication.Models
{
    public interface IEntityMetadata
    {
        [Required]
        Int32 Id { get; set; }
    }

    [MetadataType(typeof(IEntityMetadata))]
    public partial class Entity : IEntityMetadata
    {
        /* Id property has already existed in the mapped class */
    }
}

PS Jika Anda menggunakan jenis proyek yang berbeda dari ASP.NET MVC (saat Anda melakukan validasi data manual) jangan lupa untuk mendaftarkan validator Anda

/* Global.asax or similar */

TypeDescriptor.AddProviderTransparent(
    new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Entity), typeof(IEntityMetadata)), typeof(Entity));
dimonser
sumber
@dimonser solusi bagus, saya mencoba menambahkan komentar xml seperti ini juga (untuk bidang DB yang membutuhkan sedikit penjelasan dalam kode - yaitu untuk ditampilkan di intellitype) tetapi tampaknya tidak berhasil. Ada ide bagaimana melakukan itu?
Percy
Hai @Rick, Anda dapat memberi komentar pada properti antarmuka, tetapi Anda hanya akan melihatnya ketika Anda bekerja dengan variabel antarmuka. Atau Anda dapat memberi komentar di sebagian kelas. Dalam hal ini Anda akan melihatnya ketika Anda bekerja dengan sebuah instance dari kelas Anda. Tidak ada kasus lain yang tersedia. Jadi Anda dapat menggunakan keduanya untuk mencakup semua situasi, Dalam kasus pertama Anda dapat menjelaskan aturan validasi lapangan dan dalam kasus kedua mencoba menjelaskan tujuan
dimonser
Jawaban yang dipikirkan dengan sangat baik, tetapi preferensi saya adalah melihat kesalahan kompilasi jika validasi tidak lagi sinkron dengan kelas kerangka entitas yang dibuat secara otomatis. Saya kesulitan memikirkan situasi di mana Anda mungkin ingin memvalidasi properti yang tidak lagi ada di kelas kerangka entitas Anda.
Mike
1
Ini tidak berhasil untuk saya, katanya saya perlu mengimplementasikan antarmuka IEntityMetadata ...
Layak7
14

Saya menemukan solusi seperti jawaban MUG4N , tetapi sebaliknya, menumpukMetaData kelas dalam kelas entitas, sehingga mengurangi jumlah kelas dalam daftar ruang nama publik Anda, dan menghilangkan kebutuhan untuk memiliki nama unik untuk setiap kelas metadata.

using System.ComponentModel.DataAnnotations;

namespace MvcApplication1.Models 
{
    [MetadataType(typeof(MetaData))]
    public partial class ItemRequest
    {
        public class MetaData
        {
            [Required]
            public int RequestId;

            //...
        }
    }
}
Carter Medlin
sumber
Saya telah menggunakan ini di seluruh proyek saya. Jauh lebih mudah untuk diatur. Saya juga menambahkan properti khusus menggunakan [NotMapped]di dalam kelas parsial juga ketika saya membutuhkannya.
Carter Medlin
5

Ini semacam ekstensi untuk jawaban @dimonser jika Anda meregenerasi model db Anda, Anda harus menambahkan kembali antarmuka secara manual pada kelas-kelas itu.

Jika Anda ingin melakukannya, Anda juga dapat memodifikasi .tttemplate Anda :

Berikut adalah contoh antarmuka yang dibuat secara otomatis pada beberapa kelas, ini adalah fragmen dari .tthanya mengganti EntityClassOpeningmetode di milik Anda dengan mengikuti (dan jelas var stringsToMatchdengan nama entitas dan antarmuka Anda).

public string EntityClassOpening(EntityType entity)
{
    var stringsToMatch = new Dictionary<string,string> { { "Answer", "IJourneyAnswer" }, { "Fee", "ILegalFee" } };
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1}partial class {2}{3}{4}",
        Accessibility.ForType(entity),
        _code.SpaceAfter(_code.AbstractOption(entity)),
        _code.Escape(entity),
        _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)),
        stringsToMatch.Any(o => _code.Escape(entity).Contains(o.Key)) ? " : " + stringsToMatch.Single(o => _code.Escape(entity).Contains(o.Key)).Value : string.Empty);
}

Tidak ada orang normal yang boleh melakukan ini pada dirinya sendiri, telah dibuktikan dalam Alkitab bahwa seseorang pergi ke Neraka untuk ini.

Matas Vaitkevicius
sumber
2

Saya tidak yakin bagaimana melakukan apa yang Anda minta tetapi ada jalan lain. Validasi data dinamis dengan mengganti GetValidators dari DataAnnotationsModelValidatorProvider kustom Anda. Di dalamnya Anda dapat membaca aturan untuk memvalidasi setiap bidang (dari database, file konfigurasi, dll.) Dan menambahkan validator sesuai kebutuhan. Ini memiliki nilai tambah bahwa validasi Anda tidak lagi digabungkan dengan model dan dapat diubah bahkan tanpa perlu memulai ulang situs. Tentu saja ini mungkin berlebihan untuk kasus Anda, tetapi itu ideal untuk kami!

JTMon
sumber
Kami melakukannya saat pertama kali menerapkan struktur ini. Kami telah beralih ke NHibernate, tetapi ini tidak ada hubungannya dengan solusinya. Kode validasi kami berfungsi sebagaimana adanya tanpa perubahan (hanya lapisan akses data yang diubah).
JTM
1

Ubah template T4 dengan menambahkan penjelasan yang diperlukan, file ini biasanya bernama MODELNAME.tt

temukan di mana T4 membuat kelas dan metode untuk mengetahui di mana meletakkannya.

     <#=codeStringGenerator.IgnoreJson(navigationProperty)#>


//create this method in file
public string IgnoreJson(NavigationProperty navigationProperty){
            string result = navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? "" : @"[JsonIgnore]
    [IgnoreDataMember]";

            return result;
        }

Anda juga perlu menambahkan ruang nama;

<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using System.Runtime.Serialization;

Bangun kembali kelas Anda dengan menyimpan model Anda, semua metode Anda harus dianotasi.

tswales
sumber