Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added mutation conventions. #4475

Merged
merged 25 commits into from
Nov 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ba2c688
Added Error, Payload and Input Helpers
PascalSenn Oct 26, 2021
4e36dfe
Cleanup
PascalSenn Oct 26, 2021
418f3e3
Added Default Payload Behaviour & Type Name
PascalSenn Nov 23, 2021
71ec10b
Added Type Names to [Input]`
PascalSenn Nov 24, 2021
a7f751c
Add factory interface and rename Exceptions to Errors
PascalSenn Nov 24, 2021
f3b5516
Merge branch 'main' into pse/add-error-input-and-payloads-main
michaelstaib Nov 26, 2021
4993d1f
Add possibility to customize error interface type
PascalSenn Nov 27, 2021
d83b300
Merge branch 'pse/add-error-input-and-payloads-main' of github.com:Ch…
PascalSenn Nov 27, 2021
31ed00b
Added unshipped API
PascalSenn Nov 27, 2021
dbc5c53
Add documentation
PascalSenn Nov 27, 2021
1fd8875
Update parameter name in unshipped API
PascalSenn Nov 27, 2021
324e3a6
Merge branch 'main' into pse/add-error-input-and-payloads-main
PascalSenn Nov 27, 2021
9d04bc9
Add more XML documentation
PascalSenn Nov 27, 2021
9d8460d
Merge branch 'pse/add-error-input-and-payloads-main' of github.com:Ch…
PascalSenn Nov 27, 2021
d14c443
Unify context data classes
PascalSenn Nov 27, 2021
1273a8f
Update snapshots
PascalSenn Nov 27, 2021
f7d68f8
Started work to restructure project
michaelstaib Nov 29, 2021
22a720e
Added separate test project
michaelstaib Nov 29, 2021
5ffe483
Moved resources
michaelstaib Nov 29, 2021
4edeb1f
Moved Tests
michaelstaib Nov 29, 2021
7c7c8be
Removed ConfigureContextData
michaelstaib Nov 29, 2021
9969e68
Removed ConfigureContextData
michaelstaib Nov 29, 2021
334f208
More refinements
michaelstaib Nov 29, 2021
b05d585
Reworked Tests
michaelstaib Nov 29, 2021
b3c9fc8
Fixed Docs
michaelstaib Nov 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 45 additions & 15 deletions src/HotChocolate/Core/HotChocolate.Core.sln
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Subscriptions.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Subscriptions.Redis", "src\Subscriptions.Redis\HotChocolate.Subscriptions.Redis.csproj", "{35C5F3EF-B192-4A07-B664-A3D360283907}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types", "src\Types\HotChocolate.Types.csproj", "{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Validation", "src\Validation\HotChocolate.Validation.csproj", "{29FEE730-0D2C-4F31-934E-9BE2E9DC729A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Abstractions.Tests", "test\Abstractions.Tests\HotChocolate.Abstractions.Tests.csproj", "{80F8703D-804D-4921-A409-AF4BB26E59BE}"
Expand Down Expand Up @@ -97,6 +95,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.NodaTime
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.AspNetCore", "..\AspNetCore\src\AspNetCore\HotChocolate.AspNetCore.csproj", "{4AD904D1-7727-48EF-88D9-7B38D98EB314}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Types.Mutations", "src\Types.Mutations\Types.Mutations.csproj", "{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Mutations", "src\Types.Mutations\HotChocolate.Types.Mutations.csproj", "{2B7E7416-E093-4763-B839-504CBFBC8F1C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Mutations.Tests", "test\Types.Mutations.Tests\HotChocolate.Types.Mutations.Tests.csproj", "{F6FFA925-8C49-4594-9FF2-8F571C2D6389}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -251,18 +255,6 @@ Global
{35C5F3EF-B192-4A07-B664-A3D360283907}.Release|x64.Build.0 = Release|Any CPU
{35C5F3EF-B192-4A07-B664-A3D360283907}.Release|x86.ActiveCfg = Release|Any CPU
{35C5F3EF-B192-4A07-B664-A3D360283907}.Release|x86.Build.0 = Release|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Debug|x64.ActiveCfg = Debug|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Debug|x64.Build.0 = Debug|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Debug|x86.ActiveCfg = Debug|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Debug|x86.Build.0 = Debug|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Release|Any CPU.Build.0 = Release|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Release|x64.ActiveCfg = Release|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Release|x64.Build.0 = Release|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Release|x86.ActiveCfg = Release|Any CPU
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63}.Release|x86.Build.0 = Release|Any CPU
{29FEE730-0D2C-4F31-934E-9BE2E9DC729A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29FEE730-0D2C-4F31-934E-9BE2E9DC729A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29FEE730-0D2C-4F31-934E-9BE2E9DC729A}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -611,6 +603,42 @@ Global
{4AD904D1-7727-48EF-88D9-7B38D98EB314}.Release|x64.Build.0 = Release|Any CPU
{4AD904D1-7727-48EF-88D9-7B38D98EB314}.Release|x86.ActiveCfg = Release|Any CPU
{4AD904D1-7727-48EF-88D9-7B38D98EB314}.Release|x86.Build.0 = Release|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Debug|x64.ActiveCfg = Debug|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Debug|x64.Build.0 = Debug|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Debug|x86.ActiveCfg = Debug|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Debug|x86.Build.0 = Debug|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Release|Any CPU.Build.0 = Release|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Release|x64.ActiveCfg = Release|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Release|x64.Build.0 = Release|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Release|x86.ActiveCfg = Release|Any CPU
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB}.Release|x86.Build.0 = Release|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Debug|x64.ActiveCfg = Debug|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Debug|x64.Build.0 = Debug|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Debug|x86.ActiveCfg = Debug|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Debug|x86.Build.0 = Debug|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Release|Any CPU.Build.0 = Release|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Release|x64.ActiveCfg = Release|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Release|x64.Build.0 = Release|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Release|x86.ActiveCfg = Release|Any CPU
{2B7E7416-E093-4763-B839-504CBFBC8F1C}.Release|x86.Build.0 = Release|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Debug|x64.ActiveCfg = Debug|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Debug|x64.Build.0 = Debug|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Debug|x86.ActiveCfg = Debug|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Debug|x86.Build.0 = Debug|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Release|Any CPU.Build.0 = Release|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Release|x64.ActiveCfg = Release|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Release|x64.Build.0 = Release|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Release|x86.ActiveCfg = Release|Any CPU
{F6FFA925-8C49-4594-9FF2-8F571C2D6389}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -628,7 +656,6 @@ Global
{350488E5-F800-4CBD-9278-1D5C84C78958} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{CF768605-BAB8-42D8-A748-66791575BCFD} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{35C5F3EF-B192-4A07-B664-A3D360283907} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{1C16FAE5-16F7-4AAD-ABE5-4DA29D925F63} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{29FEE730-0D2C-4F31-934E-9BE2E9DC729A} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{80F8703D-804D-4921-A409-AF4BB26E59BE} = {7462D089-D350-44D6-8131-896D949A65B7}
{AADFF9B1-B275-48B4-8F32-F6CD837619E5} = {7462D089-D350-44D6-8131-896D949A65B7}
Expand Down Expand Up @@ -658,6 +685,9 @@ Global
{07F3E312-EC4C-4B71-A095-E478DF4CB52B} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{414B6DBE-1A3A-42DC-9116-3F9A433C6BBB} = {7462D089-D350-44D6-8131-896D949A65B7}
{4AD904D1-7727-48EF-88D9-7B38D98EB314} = {7637D30E-7339-4D4E-9424-87CF2394D234}
{F3621170-A3D6-4BE7-9E16-E32BA9ABA1DB} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{2B7E7416-E093-4763-B839-504CBFBC8F1C} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{F6FFA925-8C49-4594-9FF2-8F571C2D6389} = {7462D089-D350-44D6-8131-896D949A65B7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E4D94C77-6657-4630-9D42-0A9AC5153A1B}
Expand Down
5 changes: 3 additions & 2 deletions src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Subscriptions.InMemory\HotChocolate.Subscriptions.InMemory.csproj" />
<ProjectReference Include="..\Types\HotChocolate.Types.csproj" />
<ProjectReference Include="..\Fetching\HotChocolate.Fetching.csproj" />
<ProjectReference Include="..\Types.Mutations\HotChocolate.Types.Mutations.csproj" />
<ProjectReference Include="..\Types.CursorPagination\HotChocolate.Types.CursorPagination.csproj" />
<ProjectReference Include="..\Types.OffsetPagination\HotChocolate.Types.OffsetPagination.csproj" />
<ProjectReference Include="..\Validation\HotChocolate.Validation.csproj" />
<ProjectReference Include="..\Fetching\HotChocolate.Fetching.csproj" />
<ProjectReference Include="..\Execution\HotChocolate.Execution.csproj" />
<ProjectReference Include="..\Subscriptions.InMemory\HotChocolate.Subscriptions.InMemory.csproj" />
</ItemGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using HotChocolate.Execution.Configuration;

namespace Microsoft.Extensions.DependencyInjection;

public static class MutationRequestExecutorBuilderExtensions
{
/// <summary>
/// Enables mutation conventions which will simplify creating GraphQL mutations.
/// </summary>
/// <param name="builder">
/// The request executor builder
/// </param>
/// <returns>
/// The request executor builder
/// </returns>
/// <exception cref="ArgumentNullException">
/// The <paramref name="builder"/> is null.
/// </exception>
public static IRequestExecutorBuilder EnableMutationConvention(
this IRequestExecutorBuilder builder)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}

builder
.TryAddTypeInterceptor<ErrorTypeInterceptor>()
.TryAddTypeInterceptor<InputArgumentTypeInterceptor>()
.TryAddTypeInterceptor<PayloadTypeInterceptor>()
.Services
.AddSingleton<IParameterExpressionBuilder, InputParameterExpressionBuilder>();

return builder;
}

/// <summary>
/// Defines the common interface that all errors implement.
/// To specify the interface you can either provide a interface runtime type or a HotChocolate
/// interface schema type.
///
/// This has to be used together with <see cref="ErrorAttribute"/> or
/// <see cref="ErrorObjectFieldDescriptorExtensions.Error"/>
/// </summary>
/// <param name="builder">
/// The request executor builder
/// </param>
/// <typeparam name="T">
/// The type that is used as the common interface
/// </typeparam>
/// <returns>
/// The schema builder
/// </returns>
public static IRequestExecutorBuilder AddErrorInterfaceType<T>(
this IRequestExecutorBuilder builder) =>
builder.ConfigureSchema(x => x.AddErrorInterfaceType<T>());

/// <summary>
/// Defines the common interface that all errors implement.
/// To specify the interface you can either provide a interface runtime type or a HotChocolate
/// interface schema type.
///
/// This has to be used together with <see cref="ErrorAttribute"/> or
/// <see cref="ErrorObjectFieldDescriptorExtensions.Error"/>
/// </summary>
/// <param name="builder">
/// The request executor builder
/// </param>
/// <param name="type">
/// The type that is used as the common interface
/// </param>
/// <returns>
/// The request executor builder
/// </returns>
public static IRequestExecutorBuilder AddErrorInterfaceType(
this IRequestExecutorBuilder builder,
Type type) =>
builder.ConfigureSchema(x => x.AddErrorInterfaceType(type));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace HotChocolate.Types;

internal delegate object? CreateError(Exception exception);
187 changes: 187 additions & 0 deletions src/HotChocolate/Core/src/Types.Mutations/Errors/ErrorAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
namespace HotChocolate.Types;

/// <summary>
/// The <see cref="ErrorAttribute"/> registers a middleware that will catch all exceptions of
/// type <see cref="ErrorAttribute.ErrorType"/>. By annotating the attribute the response type
/// of the annotated resolver, will be automatically extended by a field of type
/// <c>errors:[Error!]</c>. This field will return errors that are caught by the middleware.
/// All the other fields on this type will be rewritten to nullable types. In case of a error
/// these fields will be set to null.
/// <para>
/// There are three different ways to map exceptions to GraphQL errors.
/// </para>
/// </summary>
/// <remarks>
/// The idea of the error middleware is to keep the resolver clean of any error handling code
/// and use exceptions to signal a error state. The HotChocolate schema is automatically
/// rewritten into a common error handling pattern.
/// <a href="https://xuorig.medium.com/a-guide-to-graphql-errors-bb9ba9f15f85">Learn More</a>
/// </remarks>
/// <example>
/// <para>
/// There are three different ways to map exceptions to GraphQL errors.
/// </para>
/// <list type="number">
/// <item>
/// <para>
/// <b>Catching exceptions directly</b>
/// </para>
/// If <see cref="ErrorAttribute.ErrorType"/> is a exception, the exception is automatically
/// mapped into a GraphQL error and the middleware will catch this exception
/// <code>
/// public class Mutation
/// {
/// [Error(typeof(SomeSpecificDomainError))]
/// [Error(typeof(SomeOtherError))]
/// public CreateUserPayload CreateUser(CreateUserInput input)
/// {
/// // ...
/// }
/// }
///
/// public record CreateUserInput(string UserName);
///
/// public record CreateUserPayload(User User);
/// </code>
/// This will generate the following schema
/// <code>
/// type Mutation {
/// createUser(input: CreateUserInput!): CreateUserPayload!
/// }
///
/// input CreateUserInput {
/// userName: String!
/// }
///
/// type CreateUserPayload {
/// user: User
/// errors: [CreateUserError!]
/// }
///
/// type User {
/// username: String
/// }
///
/// interface Error {
/// message: String!
/// }
///
/// type SomeSpecificDomainError implements Error {
/// message: String!
/// }
///
/// type SomeOtherDomainError implements Error {
/// message: String!
/// }
///
/// union CreateUserError = SomeSpecificDomainError | SomeOtherDomainError
/// </code>
/// </item>
/// <item>
/// <para>
/// <b>Map Exceptions with a factory method</b>
/// </para>
/// <para>
/// If there should be any translation between exception and error, you can defined a class
/// with factory methods. These factory methods receive a <see cref="Exception"/> and return
/// a object which will be used as the representation of the error
/// </para>
/// <para>
/// A factory method has to be `public static` and the name of the method has to be
/// `CreateErrorFrom`. There should only be one parameter of type <see cref="Exception"/> and
/// it can return a arbitrary class/struct/record that will be used as the representation
/// of the error.
/// </para>
/// <code>
/// public class MyCustomError
/// {
/// public static MyCustomError CreateErrorFrom(DomainExceptionA ex)
/// {
/// return new MyCustomError();
/// }
///
/// public static MyCustomError CreateErrorFrom(DomainExceptionB ex)
/// {
/// return new MyCustomError();
/// }
///
/// public string Message => "My custom error Message";
/// }
///
/// public class Mutation
/// {
/// [Error(typeof(MyCustomError))]
/// public CreateUserPayload CreateUser(CreateUserInput input)
/// {
/// // ...
/// }
/// }
///
/// public record CreateUserInput(string UserName);
///
/// public record CreateUserPayload(User User);
/// </code>
/// </item>
/// <item>
/// <para>
/// <b>Map exceptions with a constructors</b>
/// </para>
/// <para>
/// As a alternative to mapping exceptions with factory methods, you can also map the exception
/// in the constructor of the object that should be used to represent the error in the schema.
/// </para>
/// <code>
/// public class MyCustomError
/// {
/// public MyCustomError(MyCustomDomainException exception)
/// {
/// Message = exception.Message;
/// }
///
/// public MyCustomError(MyCustomDomainException2 exception)
/// {
/// Message = exception.Message;
/// }
///
/// public string Message { get; }
/// }
///
/// public class Mutation
/// {
/// [Error(typeof(MyCustomError))]
/// public CreateUserPayload CreateUser(CreateUserInput input)
/// {
/// // ...
/// }
/// }
/// </code>
/// </item>
/// </list>
/// </example>
public class ErrorAttribute : ObjectFieldDescriptorAttribute
{
/// <inheritdoc cref="ErrorAttribute"/>
/// <param name="errorType">
/// The type of the exception, the class with factory methods or the error with an exception
/// as the argument. See the examples in <see cref="ErrorAttribute"/>.
/// </param>
public ErrorAttribute(Type errorType)
{
ErrorType = errorType;
}

/// <summary>
/// The type of the exception, the class with factory methods or the error with an exception
/// as the argument. See the examples in <see cref="ErrorAttribute"/>.
/// </summary>
public Type ErrorType { get; }

/// <inheritdoc />
public override void OnConfigure(
IDescriptorContext context,
IObjectFieldDescriptor descriptor,
MemberInfo member)
{
descriptor.Error(ErrorType);
}
}
Loading