إذا كنت ترغب في ذلك أو كنت تستخدم هذا المشروع ، فيرجى إعطائه نجمة. شكرًا!
Vogen ( وضوحا "Voh Jen" ) هو مولد مصدر ومحلل. إنه يحول بداياتك (ints ، العشرية ، إلخ) إلى كائنات قيمة تمثل مفاهيم المجال (CustomerId ، الحساب وما إلى ذلك)
يضيف أخطاء تجميع C# جديدة للمساعدة في إيقاف إنشاء كائنات قيمة غير صالحة.
يغطي هذا ReadMe بعض الوظائف. يرجى الاطلاع على Wiki للحصول على معلومات أكثر تفصيلاً ، مثل البدء ، والدروس التعليمية ، و How-tos.
يولد مولد المصدر مفاهيم المجال المكتوبة بقوة. أنت تقدم هذا:
[ ValueObject < int > ]
public partial struct CustomerId ;... وتوليد Vogen مصدر مشابه لهذا:
public partial struct CustomerId :
System . IEquatable < CustomerId > ,
System . IComparable < CustomerId > , System . IComparable
{
private readonly int _value ;
public readonly int Value => _value ;
public CustomerId ( ) {
throw new Vogen . ValueObjectValidationException (
"Validation skipped by attempting to use the default constructor..." ) ;
}
private CustomerId ( int value ) => _value = value ;
public static CustomerId From ( int value ) {
CustomerId instance = new CustomerId ( value ) ;
return instance ;
}
public readonly bool Equals ( CustomerId other ) .. .
public readonly bool Equals ( int primitive ) .. .
public readonly override bool Equals ( object obj ) .. .
public static bool operator == ( CustomerId left , CustomerId right ) .. .
public static bool operator != ( CustomerId left , CustomerId right ) .. .
public static bool operator == ( CustomerId left , int right ) .. .
public static bool operator != ( CustomerId left , int right ) .. .
public static bool operator == ( int left , CustomerId right ) .. .
public static bool operator != ( int left , CustomerId right ) .. .
public readonly override int GetHashCode ( ) .. .
public readonly override string ToString ( ) .. .
} يمكنك بعد ذلك استخدام CustomerId بدلاً من int في مجالك بمعرفة كاملة بأنها صالحة وآمنة للاستخدام:
CustomerId customerId = CustomerId . From ( 123 ) ;
SendInvoice ( customerId ) ;
.. .
public void SendInvoice ( CustomerId customerId ) { .. . } int هو النوع الافتراضي لكائنات القيمة. من الجيد عمومًا أن نعلن كل نوع من أجل الوضوح. يمكنك أيضًا - بشكل فردي أو عالمي - تكوينها لتكون أنواعًا أخرى. راجع قسم التكوين لاحقًا في المستند.
إليك بعض الأمثلة الأخرى:
[ ValueObject < decimal > ]
public partial struct AccountBalance ;
[ ValueObject < string > ]
public partial class LegalEntityName ;الهدف الرئيسي من Vogen هو ضمان صحة كائنات القيمة الخاصة بك ، يساعدك محلل التعليمات البرمجية على تجنب الأخطاء التي قد تتركك مع كائنات قيمة غير ملائمة في مجالك.
يقوم بذلك عن طريق إضافة قيود جديدة في شكل أخطاء تجميع C# جديدة . هناك بعض الطرق التي يمكن أن تنتهي بها كائنات القيمة غير المعلنة. طريقة واحدة هي عن طريق إعطاء منشئي النوع الخاص بك. قد يعني توفير البنائين الخاصين أنك تنسى تعيين قيمة ، لذلك لا يسمح لك Vogen بإنشاء منشئات محددة للمستخدم :
[ ValueObject ]
public partial struct CustomerId
{
// Vogen deliberately generates this so that you can't create your own:
// error CS0111: Type 'CustomerId' already defines a member called 'CustomerId'
// with the same parameter type
public CustomerId ( ) { }
// error VOG008: Cannot have user defined constructors,
// please use the From method for creation.
public CustomerId ( int value ) { }
}بالإضافة إلى ذلك ، ستكتشف Vogen المشكلات عند إنشاء أو استهلاك كائنات القيمة:
// catches object creation expressions
var c = new CustomerId ( ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
CustomerId c = default ; // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
var c = default ( CustomerId ) ; // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
var c = GetCustomerId ( ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
var c = Activator . CreateInstance < CustomerId > ( ) ; // error VOG025: Type 'CustomerId' cannot be constructed via Reflection as it is prohibited.
var c = Activator . CreateInstance ( < CustomerId > ) ; // error VOG025: Type 'MyVo' cannot be constructed via Reflection as
it is prohibited
// catches lambda expressions
Func < CustomerId > f = ( ) => default ; // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
// catches method / local function return expressions
CustomerId GetCustomerId ( ) => default ; // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
CustomerId GetCustomerId ( ) => new CustomerId ( ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
CustomerId GetCustomerId ( ) => new ( ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
// catches argument / parameter expressions
Task < CustomerId > t = Task . FromResult < CustomerId > ( new ( ) ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
void Process ( CustomerId customerId = default ) { } // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited. أحد الأهداف الرئيسية لهذا المشروع هو تحقيق نفس السرعة والأداء الذاكرة تقريبًا مثل استخدام البدائية مباشرة . بعبارة أخرى ، إذا كانت بدائية decimal تمثل رصيد حساب ، فهناك انخفاض كبير في استخدام كائن قيمة AccountBalance بدلاً من ذلك. يرجى الاطلاع على مقاييس الأداء أدناه.
Vogen هي حزمة nuget. تثبيته مع:
dotnet add package Vogen
عند إضافته إلى مشروعك ، يقوم المولد المصدر بإنشاء الأغلفة لأولك البدائية وسيعلمك محلل التعليمات البرمجية ما إذا كنت تحاول إنشاء كائنات قيمة غير صالحة.
فكر في مفاهيم المجال الخاصة بك وكيف تستخدم البدائية لتمثيلها ، على سبيل المثال بدلاً من هذا:
public void HandlePayment ( int customerId , int accountId , decimal paymentAmount )... هل لديك هذا:
public void HandlePayment ( CustomerId customerId , AccountId accountId , PaymentAmount paymentAmount )الأمر بسيط مثل إنشاء أنواع مثل هذا:
[ ValueObject ]
public partial struct CustomerId ;
[ ValueObject ]
public partial struct AccountId ;
[ ValueObject < decimal > ]
public partial struct PaymentAmount ; مولد المصدر يولد كائنات القيمة. تساعد كائنات القيمة على مكافحة الهوس البدائي من خلال لف بدائل البديهيات البسيطة مثل int و string و double وما إلى ذلك في نوع قوي من النوع.
الهوس البدائي (المعروف أيضًا باسم الشاق) يعني أن تكون مهووسًا بالبدائل. إنها رائحة رمز تحط من جودة البرمجيات.
" الهوس البدائي يستخدم أنواع البيانات البدائية لتمثيل أفكار المجال " #
بعض الأمثلة:
int age - سنكون Age age . قد يكون لدى Age التحقق من أنه لا يمكن أن يكون سلبيًاstring postcode - سيكون لدينا Postcode postcode . قد يكون Postcode التحقق من صحة تنسيق النصمولد المصدر هو رأي. تساعد الآراء في ضمان الاتساق. الآراء هي:
From سبيل المثال Age.From(12)Age.From(12) == Age.From(12) )Validate تعيد نتيجة ValidationValidation.Ok . يؤدي ValueObjectValidationException من الشائع تمثيل أفكار المجال كحوالي ، ولكن قد لا تكون البدائية قادرة على وصف فكرة المجال بالكامل.
لاستخدام كائنات القيمة بدلاً من البدائية ، نقوم ببساطة بتبديل رمز مثل هذا:
public class CustomerInfo {
private int _id ;
public CustomerInfo ( int id ) => _id = id ;
}.. لهذا:
public class CustomerInfo {
private CustomerId _id ;
public CustomerInfo ( CustomerId id ) => _id = id ;
} هناك منشور مدونة هنا يصفها ، ولكن لتلخيص:
إن الهوس البدائي مهووس بالطريقة التي تبدو ملائمة على ما يبدو أن البدائل ، مثل
intsstrings، تسمح لنا بتمثيل كائنات وأفكار المجال.
هذا :
int customerId = 42ما الخطأ في ذلك؟
من المحتمل أن يتم تمثيل معرف العميل بالكامل بواسطة int . يمكن أن يكون int سلبيًا أو صفرًا ، ولكن من غير المرجح أن يكون معرف العميل. لذلك ، لدينا قيود على معرف العميل. لا يمكننا تمثيل أو فرض تلك القيود على int .
لذلك ، نحتاج إلى بعض التحقق من الصحة لضمان استيفاء قيود معرف العميل. نظرًا لأنه في int ، لا يمكننا أن نكون متأكدين مما إذا كان قد تم فحصه مسبقًا ، لذلك نحتاج إلى التحقق منه في كل مرة نستخدمها. نظرًا لأنها بدائية ، فقد قام شخص ما بتغيير القيمة ، لذلك حتى لو كنا متأكدين بنسبة 100 ٪ أننا فحصناها من قبل ، فقد لا يزال يحتاج إلى التحقق مرة أخرى.
حتى الآن ، استخدمنا كمثال ، معرف العميل من القيمة 42 . في C#، قد لا يكون مفاجئًا أن " 42 == 42 " ( لم أتحقق من ذلك في JavaScript! ). ولكن ، في مجالنا ، هل يجب على 42 دائمًا أن يساوي 42 دائمًا؟ ربما لا إذا كنت تقارن معرف المورد من 42 بمعرف العميل 42 ! لكن البدائية لن تساعدك هنا (تذكر ، 42 == 42 !).
( 42 == 42 ) // true
( SuppliedId . From ( 42 ) == SupplierId . From ( 42 ) ) // true
( SuppliedId . From ( 42 ) == VendorId . From ( 42 ) ) // compilation error لكن في بعض الأحيان ، نحتاج إلى الإشارة إلى أن كائن القيمة غير صالح أو لم يتم تعيينه. لا نريد أي شخص خارج الكائن يفعل هذا لأنه يمكن استخدامه عن طريق الخطأ. من الشائع أن يكون لديك حالات Unspecified ، على سبيل المثال
public class Person {
public Age Age { get ; } = Age . Unspecified ;
} يمكننا القيام بذلك مع سمة Instance :
[ ValueObject ]
[ Instance ( "Unspecified" , - 1 ) ]
public readonly partial struct Age {
public static Validation Validate ( int value ) =>
value > 0 ? Validation . Ok : Validation . Invalid ( "Must be greater than zero." ) ;
} هذا يولد public static Age Unspecified = new Age(-1); . المنشئ private ، لذلك يمكن لهذا النوع فقط (عمدا) إنشاء مثيلات غير صالحة .
الآن ، عندما نستخدم Age ، يصبح التحقق من صحةنا أكثر وضوحًا:
public void Process ( Person person ) {
if ( person . Age == Age . Unspecified ) {
// age not specified.
}
}يمكننا أيضًا تحديد خصائص مثيل أخرى:
[ ValueObject < float > ]
[ Instance ( "Freezing" , 0 ) ]
[ Instance ( "Boiling" , 100 ) ]
public readonly partial struct Celsius {
public static Validation Validate ( float value ) =>
value >= - 273 ? Validation . Ok : Validation . Invalid ( "Cannot be colder than absolute zero" ) ;
} يمكن أن يكون لكل كائن قيمة التكوين الاختياري الخاص به. يشمل التكوين:
إذا لم يتم تحديد أي من تلك المذكورة أعلاه ، فسيتم استنتاج التكوين العالمي. يبدو أن هذا:
[ assembly : VogenDefaults (
underlyingType : typeof ( int ) ,
conversions : Conversions . Default ,
throws : typeof ( ValueObjectValidationException ) ) ]تلك مرة أخرى اختيارية. إذا لم يتم تحديدها ، فسيتم تخلفها عن:
typeof(int)Conversions.Default ( TypeConverter و System.Text.Json )typeof(ValueObjectValidationException)هناك العديد من تحذيرات تحليل التعليمات البرمجية للتكوين غير صالح ، بما في ذلك:
System.Exception (لتشغيل هذه بنفسك: dotnet run -c Release --framework net9.0 -- --job short --filter * in the Vogen.Benchmarks )
كما ذكرنا سابقًا ، فإن هدف Vogen هو تحقيق أداء مشابه جدًا مقارنة باستخدام البدائية أنفسهم. فيما يلي معيار يقارن استخدام كائن قيمة تم التحقق من صحة مع نوع أساسي من int vs باستخدام int أصلاً ( بدائي ؟)
BenchmarkDotNet =v0.13.2, OS =Windows 11 (10.0.22621.1194)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK =7.0.102
[Host] : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
ShortRun : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
Job =ShortRun IterationCount =3 LaunchCount =1
WarmupCount =3 | طريقة | يقصد | خطأ | Stddev | نسبة | نسبة | Gen0 | مخصص |
|---|---|---|---|---|---|---|---|
| باستخدام مكسرة | 14.55 ns | 1.443 ns | 0.079 ns | 1.00 | 0.00 | - | - |
| باستخدام ValueObjectStruct | 14.88 NS | 3.639 ns | 0.199 ns | 1.02 | 0.02 | - | - |
لا يوجد فرق واضح بين استخدام int الأصلي وهيكل VO ؛ كلاهما متشابهان إلى حد كبير من حيث السرعة والذاكرة.
السيناريو الأكثر شيوعًا هو استخدام فئة VO لتمثيل String أصلية. هذه النتائج هي:
BenchmarkDotNet =v0.13.2, OS =Windows 11 (10.0.22621.1194)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK =7.0.102
[Host] : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
ShortRun : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
Job =ShortRun IterationCount =3 LaunchCount =1
WarmupCount =3 | طريقة | يقصد | خطأ | Stddev | نسبة | نسبة | Gen0 | مخصص | تخصيص نسبة |
|---|---|---|---|---|---|---|---|---|
| usingstringnister | 151.8 NS | 32.19 | 1.76 | 1.00 | 0.00 | 0.0153 | 256 ب | 1.00 |
| باستخدام ValueObjectArstruct | 184.8 ns | 12.19 | 0.67 | 1.22 | 0.02 | 0.0153 | 256 ب | 1.00 |
هناك قدر ضئيل من الأداء النفقات العامة ، ولكن هذه القياسات صغيرة بشكل لا يصدق. لا يوجد ذاكرة النفقات العامة.
بشكل افتراضي ، تم تزيين كل VO مع Serializer TypeConverter (STJ System.Text.Json . هناك محولات أخرى/مسلسل ل:
يتم التحكم فيها من خلال التعداد Conversions . فيما يلي مسلسلات لـ NSJ و STJ:
[ ValueObject < float > ( conversions : Conversions . NewtonsoftJson | Conversions . SystemTextJson ) ]
public readonly partial struct Celsius ; إذا كنت لا تريد أي تحويلات ، فحدد Conversions.None .
إذا كنت تريد تحويلك الخاص ، فحدد مرة أخرى لا شيء ، وقم بتنفيذها بنفسك ، تمامًا مثل أي نوع آخر. لكن كن على دراية أنه حتى المسلسلات ستحصل على أخطاء التجميع نفسها new default عند محاولة إنشاء VOS.
إذا كنت ترغب في استخدام Dapper ، تذكر أن يسجلها - شيء من هذا القبيل:
SqlMapper . AddTypeHandler ( new Customer . DapperTypeHandler ( ) ) ;انظر مجلد الأمثلة لمزيد من المعلومات.
ما يلي هو مقتطف من صفحة الأسئلة الشائعة الكاملة في الويكي.
نعم ، هذا هنا: https://stevedunn.github.io/vogen/vogen.html
مولد المصدر هو .NET Standard 2.0. الرمز الذي ينشئه يدعم جميع إصدارات اللغة C# من 6.0 وما بعده
إذا كنت تستخدم المولد في مشروع .NET Framework واستخدام مشاريع النمط القديم (المشروع قبل مشاريع "SDK Style") ، فستحتاج إلى القيام ببعض الأشياء بشكل مختلف:
PackageReference في ملف .csproj: < ItemGroup >
< PackageReference Include = " Vogen " Version = " [LATEST_VERSION_HERE - E.G. 1.0.18] " PrivateAssets = " all " />
</ ItemGroup >latest (أو أي شيء 8 أو أكثر): <PropertyGroup>
+ <LangVersion>latest</LangVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
يمثل كائن القيمة المصطلح كائنًا صغيرًا يعتمد المساواة على القيمة وليس الهوية. من ويكيبيديا
في علوم الكمبيوتر ، يكون كائن القيمة هو كائن صغير يمثل كيانًا بسيطًا لا تعتمد مساواةه على الهوية: أي كائنات قيمة متساوية عندما يكون لها نفس القيمة ، وليس بالضرورة كائن نفس الكائن.
في DDD ، كائن القيمة هو (مرة أخرى ، من ويكيبيديا)
... كائن القيمة هو كائن ثابت يحتوي على سمات ولكن ليس له هوية مفاهيمية
public record struct CustomerId(int Value); ؟ هذا لا يمنحك التحقق من الصحة. للتحقق من صحة Value ، لا يمكنك استخدام بناء الجملة المختصرة (المُنشئ الأساسي). لذلك عليك القيام بذلك:
public record struct CustomerId
{
public CustomerId ( int value ) {
if ( value <= 0 ) throw new Exception ( .. . )
}
} يمكنك أيضًا توفير مُنشئين آخرين قد لا يتحققون من صحة البيانات ، وبالتالي السماح ببيانات غير صالحة في مجالك . قد لا يرمي هؤلاء المنشئون الآخرون استثناء ، أو قد يرمون استثناءات مختلفة. واحدة من الآراء في Vogen هي أن أي بيانات غير صالحة تُعطى لكائن القيمة ترمي ValueObjectValidationException .
يمكنك أيضًا استخدام default(CustomerId) للتهرب من التحقق من الصحة. في Vogen ، هناك محللون يلتقطون هذا ويخفقون في البناء ، على سبيل المثال:
// error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
CustomerId c = default ;
// error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
var c2 = default ( CustomerId ) ; نعم. بشكل افتراضي ، تم تزيين كل VO مع Serializer TypeConverter (STJ System.Text.Json . هناك محولات/مسلسلات أخرى من أجل:
نعم ، على الرغم من وجود بعض الاعتبارات. يرجى الاطلاع على صفحة efcore على الويكي ، ولكن TL ؛ DR هو:
OnModelCreating على سبيل المثال ، [ValueObject<string>(conversions: Conversions.EfCoreValueConverter)] builder . Entity < SomeEntity > ( b =>
{
b . Property ( e => e . Name ) . HasConversion ( new Name . EfCoreValueConverter ( ) ) ;
} ) ;يمكنك ، ولكن لضمان الاتساق في جميع أنحاء المجال ، يجب عليك التحقق من صحة في كل مكان . ويقول قانون ضالو أن هذا غير ممكن:
⚖ قانون الضحلة "عندما تحتاج الأشياء إلى التغيير و n> 1 ، ستجد الضحلة في معظم هذه الأشياء."
بشكل ملموس: "عندما تحتاج 5 أشياء إلى التغيير ، ستجد الضحلة على الأكثر ، 4 من هذه الأشياء."
struct ، فهل يمكنني حظر استخدام CustomerId customerId = default(CustomerId); ؟نعم . المحلل يولد خطأ التجميع.
struct ، فهل يمكنني حظر استخدام CustomerId customerId = new(CustomerId); ؟نعم . المحلل يولد خطأ التجميع.
لا . يتم إنشاء مُنشئ أقل المعلمة تلقائيًا ، ويتم إنشاء المُنشئ الذي يأخذ القيمة الأساسية أيضًا تلقائيًا.
إذا قمت بإضافة مزيد من البنائين ، فستحصل على خطأ في التجميع من مولد الرمز ، على سبيل المثال
[ ValueObject < int > ) ]
public partial struct CustomerId {
// Vogen already generates this as a private constructor:
// error CS0111: Type 'CustomerId' already defines a member called 'CustomerId' with the same parameter type
public CustomerId ( ) { }
// error VOG008: Cannot have user defined constructors, please use the From method for creation.
public CustomerId ( int value ) { }
}يمكنك ذلك ، لكنك ستحصل على التحذير من برنامج التحويل البرمجي CS0282-لا يوجد أي ترتيب محدد بين الحقول في إعلانات متعددة من الطبقة الجزئية أو البنية "
أرغب في تطبيع/تطهير القيم المستخدمة ، على سبيل المثال ، تقليص الإدخال. هل هذا ممكن؟
نعم ، أضف طريقة NormalizeInput ، على سبيل المثال
private static string NormalizeInput ( string input ) => input . Trim ( ) ;انظر ويكي لمزيد من المعلومات.
سيكون من الرائع لو كان كذلك ، لكنه ليس حاليًا. لقد كتبت مقالًا حول هذا الموضوع ، ولكن باختصار ، هناك اقتراح لغة طويل الأمد يركز على أنواع القيمة غير القابلة للوفق. يعد وجود أنواع القيمة غير القابلة للتفاصيل خطوة أولى رائعة ، ولكن سيكون من السهل أيضًا الحصول على شيء في اللغة لفرضه على التحقق. لذلك أضفت اقتراح لغة للسجلات الثابتة.
يقول أحد الردود في الاقتراح أن فريق اللغة قرر أن سياسات التحقق من الصحة لا ينبغي أن تكون جزءًا من C#، ولكن المقدمة من قبل مولدات المصدر.
Strongllytypedid هذا يركز أكثر على الهوية. يركز Vogen أكثر من "مفاهيم المجال" والقيود المرتبطة بتلك المفاهيم.
هذه هي محاولتي الأولى وهي محاولتي الأولى وهي غير مصدر. هناك ذاكرة النفقات العامة لأن النوع الأساسي هو فئة. لا يوجد أيضا تحليلات. تم تمييزه الآن على أنه تم إهماله لصالح Vogen.
قيمة مشابهة للضغط - غير المولدة المصدر وليس تحليلات. هذا أيضًا أكثر استرخاءً ويسمح بأنواع مركبة "أساسية".
ValueBjectGenerator مشابه لـ Vogen ، ولكنه أقل تركيزًا على التحقق من الصحة وعدم وجود محلل رمز.
يمكن لف أي نوع. التحويلات التسلسلية والكتب لها تطبيقات ل:
خيط
int
طويل
قصير
بايت
تعويم (واحد)
عشري
مزدوج
DateTime
Dateonly
الوقت
DateTimeOffset
GUID
بول
بالنسبة للأنواع الأخرى ، يتم تطبيق تحويل النوع العام والتسلسل. إذا كنت تقوم بتزويد المحولات الخاصة None لتحويل النوع وتسلسل
[ ValueObject < SpecialPrimitive > ( conversions : Conversions . None ) ]
[ System . Text . Json . Serialization . JsonConverter ( typeof ( SpecialPrimitiveJsonConverter ) ) ]
[ System . ComponentModel . TypeConverter ( typeof ( SpecialPrimitiveTypeConverter ) ) ]
public partial struct SpecialMeasurement ; نعم ، من خلال تحديد نوع الاستثناء إما في سمة ValueObject ، أو عالميًا ، مع VogenConfiguration .
TimeOnly الذي يقول فيه إن DateTime لا يمكن تحويله إلى TimeOnly - ماذا أفعل؟ LINQ2DB 4.0 أو أكبر يدعم DateOnly و TimeOnly . يولد Vogen محولات القيمة لـ LINQ2DB ؛ بالنسبة إلى DateOnly ، فهي تعمل فقط ، ولكن ل `time ، تحتاج إلى إضافة هذا إلى تطبيقك:
MappingSchema.Default.SetConverter<DateTime, TimeOnly>(dt => TimeOnly.FromDateTime(dt));
نعم. أضف التبعية إلى protobuf-net وتعيين سمة بديلة:
[ ValueObject < string > ]
[ ProtoContract ( Surrogate = typeof ( string ) ) ]
public partial class BoxId {
//...
}سيتم الآن تسلسل نوع Boxid كسلسلة في جميع الرسائل/مكالمات GRPC. إذا كان المرء يولد ملفات .proto للتطبيقات الأخرى من C# Code ، فستتضمن ملفات Proto نوعًا بديلًا كنوع. شكرًا لك على Pommasm على هذه المعلومات .
شكرا لجميع الأشخاص الذين ساهموا!
أخذت الكثير من الإلهام من أندرو لوك القوي.
لقد حصلت أيضًا على بعض الأفكار الرائعة من Gérald Barré's Meziantou.analyzer