เพิ่มคำอธิบายประกอบข้อมูลไปยังคลาสที่สร้างโดยกรอบงานเอนทิตี


94

ฉันมีคลาสต่อไปนี้ที่สร้างโดยเอนทิตีเฟรมเวิร์ก:

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

ฉันต้องการกำหนดให้เป็นฟิลด์บังคับ

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

อย่างไรก็ตามเนื่องจากรหัสนี้ถูกสร้างขึ้นสิ่งนี้จะถูกลบออก ฉันนึกไม่ออกว่าจะสร้างคลาสบางส่วนได้อย่างไรเนื่องจากคุณสมบัติถูกกำหนดโดยคลาสบางส่วนที่สร้างขึ้น ฉันจะกำหนดข้อ จำกัด อย่างปลอดภัยได้อย่างไร?


หากคุณสมบัติของคุณเป็น int ค่าเริ่มต้นจำเป็นสำหรับ modelbinder ดังนั้นแอตทริบิวต์ [จำเป็น] ของคุณจะไม่เพิ่มอะไรที่นี่
Kirill Bestemyanov

@KirillBestemyanov - @ Html.ValidationMessageFor (model => modelItemItem.ResourceTypeID) ควรล้มเหลวในฝั่งไคลเอ็นต์ มันไม่ใช่.
P. Brian.Mackey

คำตอบ:


144

คลาสที่สร้างขึ้นItemRequestจะเป็นpartialคลาสเสมอ สิ่งนี้ช่วยให้คุณสามารถเขียนคลาสบางส่วนที่สองซึ่งมีเครื่องหมายคำอธิบายประกอบข้อมูลที่จำเป็น ในกรณีของคุณคลาสบางส่วนItemRequestจะมีลักษณะดังนี้:

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;}

        //...
    }
}

12
คลาสบางส่วนจะไม่ถูกสร้างใหม่ นั่นเป็นเหตุผลว่าทำไมจึงกำหนดเป็นบางส่วน
MUG4N

คุณพลาดตัวปรับแต่งบางส่วนหรือไม่? คุณใช้เนมสเปซเดียวกันหรือไม่?
MUG4N

5
ผู้ใช้. NET Core: ใช้ModelMetadataTypeแทน MetadataType
Bob Kaufman

1
คุณสามารถใส่คลาสบางส่วนได้ทุกที่ที่คุณต้องการตราบเท่าที่เนมสเปซเหมือนกัน
MUG4N

40

ตามที่MUG4Nตอบคุณสามารถใช้คลาสบางส่วนได้แต่จะดีกว่าใช้อินเทอร์เฟซแทน ในกรณีนี้คุณจะมีข้อผิดพลาดในการคอมไพล์หากโมเดล EF ไม่สอดคล้องกับโมเดลการตรวจสอบความถูกต้อง คุณจึงปรับเปลี่ยนโมเดล EF ได้โดยไม่ต้องกลัวว่ากฎการตรวจสอบจะล้าสมัย

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 */
    }
}

ป.ล. หากคุณใช้ประเภทโครงการที่แตกต่างจาก ASP.NET MVC (เมื่อคุณดำเนินการตรวจสอบข้อมูลด้วยตนเอง) อย่าลืมลงทะเบียน validators ของคุณ

/* Global.asax or similar */

TypeDescriptor.AddProviderTransparent(
    new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Entity), typeof(IEntityMetadata)), typeof(Entity));

@dimonser ทางออกที่ดีฉันลองเพิ่มความคิดเห็น xml เช่นนี้ด้วย (สำหรับฟิลด์ DB ที่ต้องการคำอธิบายเล็กน้อยในโค้ด - เช่นจะแสดงใน intellitype) แต่ดูเหมือนจะไม่ได้ผล มีความคิดอย่างไรที่จะทำ?
Percy

สวัสดี @Rick คุณสามารถแสดงความคิดเห็นเกี่ยวกับคุณสมบัติของอินเทอร์เฟซได้ แต่คุณจะเห็นได้ก็ต่อเมื่อคุณทำงานกับตัวแปรอินเทอร์เฟซ หรือคุณสามารถแสดงความคิดเห็นในชั้นเรียนบางส่วน ในกรณีนี้คุณจะเห็นเมื่อคุณทำงานกับตัวอย่างของชั้นเรียนของคุณ ไม่มีกรณีอื่น ๆ ดังนั้นคุณสามารถใช้ทั้งสองอย่างเพื่อให้ครอบคลุมทุกสถานการณ์ในกรณีแรกคุณสามารถอธิบายกฎการตรวจสอบความถูกต้องของฟิลด์และในกรณีที่สองพยายามอธิบายวัตถุประสงค์
dimonser

คำตอบที่คิดออกมาเป็นอย่างดี แต่ความชอบของฉันคือจะเห็นข้อผิดพลาดในการคอมไพล์หากการตรวจสอบความถูกต้องไม่ตรงกับคลาสเฟรมเวิร์กเอนทิตีที่สร้างอัตโนมัติอีกต่อไป ฉันกำลังดิ้นรนที่จะนึกถึงสถานการณ์ที่คุณอาจต้องการตรวจสอบคุณสมบัติที่ไม่มีอยู่ในคลาสกรอบงานเอนทิตีของคุณอีกต่อไป
Mike

1
สิ่งนี้ไม่ได้ผลสำหรับฉันมันบอกว่าฉันต้องใช้อินเทอร์เฟซ IEntityMetadata ...
คุ้มค่า 7

14

ฉันพบวิธีแก้ปัญหาเช่นคำตอบของMUG4Nแต่การซ้อนMetaDataคลาสภายในคลาสเอนทิตีจึงเป็นการลดจำนวนคลาสในรายการเนมสเปซสาธารณะของคุณและไม่จำเป็นต้องมีชื่อเฉพาะสำหรับคลาสเมทาดาทาแต่ละคลาส

using System.ComponentModel.DataAnnotations;

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

            //...
        }
    }
}

ฉันใช้สิ่งนี้มาตลอดในโครงการของฉัน จัดระเบียบง่ายกว่ามาก ฉันยังเพิ่มคุณสมบัติที่กำหนดเองโดยใช้[NotMapped]ภายในคลาสบางส่วนเมื่อฉันต้องการ
Carter Medlin

5

นี่เป็นส่วนขยายของคำตอบ @dimonser หากคุณสร้างโมเดลฐานข้อมูลของคุณใหม่คุณจะต้องเพิ่มอินเทอร์เฟซในคลาสเหล่านั้นด้วยตนเอง

หากคุณมีกระเพาะอาหารคุณสามารถปรับเปลี่ยน.ttเทมเพลตของคุณได้:

นี่คือตัวอย่างของอินเทอร์เฟซที่สร้างขึ้นโดยอัตโนมัติในบางคลาสซึ่งเป็นส่วนที่มาจาก.ttเพียงแค่แทนที่EntityClassOpeningเมธอดในของคุณด้วยสิ่งต่อไปนี้ (และเห็นได้ชัดว่าเป็นvar stringsToMatchชื่อเอนทิตีและอินเทอร์เฟซของคุณ)

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);
}

ไม่มีคนปกติควรทำเช่นนี้กับตัวเองแม้ว่าจะมีการพิสูจน์แล้วในคัมภีร์ไบเบิลว่ามีคนไปนรกเพื่อสิ่งนี้


2

ฉันไม่แน่ใจว่าจะทำตามที่คุณขอได้อย่างไร แต่มีวิธีแก้ไข การตรวจสอบข้อมูลแบบไดนามิกโดยการลบล้าง GetValidators ของ DataAnnotationsModelValidatorProvider ที่กำหนดเองของคุณ ในนั้นคุณสามารถอ่านกฎสำหรับการตรวจสอบความถูกต้องของแต่ละฟิลด์ (จากฐานข้อมูลไฟล์กำหนดค่า ฯลฯ ) และเพิ่มตัวตรวจสอบความถูกต้องได้ตามต้องการ มีค่าเพิ่มเติมที่การตรวจสอบของคุณไม่ได้อยู่คู่กับโมเดลอีกต่อไปและสามารถเปลี่ยนแปลงได้โดยไม่จำเป็นต้องรีสตาร์ทไซต์ แน่นอนว่ามันอาจจะมากเกินไปสำหรับกรณีของคุณ แต่มันก็เหมาะสำหรับเรา!


เราทำเมื่อเรานำโครงสร้างนี้มาใช้ครั้งแรก เราได้เปลี่ยนไปใช้ NHibernate แล้ว แต่สิ่งนี้ไม่มีผลต่อการแก้ปัญหา รหัสตรวจสอบของเราทำงานเหมือนเดิมโดยไม่มีการเปลี่ยนแปลง (มีการเปลี่ยนแปลงเฉพาะชั้นการเข้าถึงข้อมูลเท่านั้น)
JTM ใน

1

แก้ไขเทมเพลต T4 เพิ่มคำอธิบายประกอบที่จำเป็นไฟล์นี้มักมีชื่อว่า MODELNAME.tt

ค้นหาตำแหน่งที่ T4 สร้างคลาสและเมธอดเพื่อให้ทราบว่าจะวางไว้ที่ใด

     <#=codeStringGenerator.IgnoreJson(navigationProperty)#>


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

            return result;
        }

คุณจะต้องเพิ่มเนมสเปซด้วย

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

สร้างคลาสของคุณใหม่โดยการบันทึกโมเดลของคุณวิธีการทั้งหมดของคุณควรมีคำอธิบายประกอบ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.