学习010-01-04 Integrate Application Builders into Existing Applications(将应用程序构建器集成到现有应用程序中 )

Integrate Application Builders into Existing Applications(将应用程序构建器集成到现有应用程序中 )

In .NET applications, you can use fluent API-based application builders to configure your ASP.NET Core Blazor and WinForms applications (add extra Modules, enable the Security System, and so on). The Solution Wizard creates application builders automatically when you create an XAF solution in v22.1+.
在. NET应用程序中,您可以使用基于API的流畅应用程序构建器来配置您的ASP.NETCore Blazor和WinForms应用程序(添加额外模块、启用安全系统等)。在v22.1+中创建XAF解决方案时,解决方案向导会自动创建应用程序构建器。

This help topic describes how to integrate an application builder into your project created in v21.2 and earlier.
本帮助主题介绍如何将应用程序构建器集成到在v21.2及更早版本中创建的项目中。

Step 1. Move Application Initialization Code(步骤1.移动应用程序初始化代码)

1.Implement the ILegacyInitializationXafApplication interface in your BlazorApplication / WinApplication descendants and comment out the InitializeComponents method in their constructors:
在BlazorApplication/WinApplication后代中实现ILegacyInitializationXafApplication接口,并在其构造函数中注释掉InitializeComponents方法:

Files:
ASP.NET Core Blazor - MySolution.Blazor.Server\BlazorApplication.cs
WinForms - MySolution.Win\WinApplication.cs

C# 
using DevExpress.ExpressApp.ApplicationBuilder;
// ...
public partial class MySolutionBlazorApplication : BlazorApplication, ILegacyInitializationXafApplication {
    public MySolutionBlazorApplication() {
        // InitializeComponent();
    }
    void ILegacyInitializationXafApplication.InitializeComponent() => InitializeComponent();
    // ...
}

ILegacyInitializationXafApplication.InitializeComponent is called during application initialization.
ILegacyInitializationXafApplication在应用程序初始化过程中,初始化组件被调用。

2.In the InitializeComponent method, comment out the registration of SystemModule, SystemBlazorModule, and SystemWindowsFormsModule:
在OrializeComponent方法中,注释掉SystemModule、SystemBlazorModule和SystemWindowsFormsModule的注册:

Files:
ASP.NET Core Blazor - MySolution.Blazor.Server\BlazorApplication.Designer.cs
WinForms - MySolution.Win\WinApplication.Designer.cs

C#
partial class MySolutionBlazorApplication {
// …
private void InitializeComponent() {
// this.module1 = new DevExpress.ExpressApp.SystemModule.SystemModule();
// this.module2 = new DevExpress.ExpressApp.Blazor.SystemModule.SystemBlazorModule();
// …
// this.Modules.Add(this.module1);
// this.Modules.Add(this.module2);
// …
// private DevExpress.ExpressApp.SystemModule.SystemModule module1;
// private DevExpress.ExpressApp.Blazor.SystemModule.SystemBlazorModule module2;
}
}

Step 2. Implement an Application Builder in Your Project(步骤2.在您的项目中实现应用程序构建器)

1.In the Windows Forms application project, add a new file with the application builder:
在Windows窗体应用程序项目中,使用应用程序构建器添加一个新文件:

File: MySolution.Win\Startup.cs.

C# 
using System;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Design;
using DevExpress.ExpressApp.Win;
using DevExpress.ExpressApp.Win.ApplicationBuilder;

namespace MySolution.Win;

public class ApplicationBuilder : IDesignTimeApplicationFactory {
    public static WinApplication BuildApplication(string connectionString) {
        ArgumentNullException.ThrowIfNull(connectionString);
        var builder = WinApplication.CreateBuilder();
        builder.UseApplication<MySolutionWindowsFormsApplication>();
        builder.Modules
            .Add<MySolutionWindowsFormsModule>();
        builder.AddBuildStep(application => {
            application.ConnectionString = connectionString;
        });
        var winApplication = builder.Build();
        return winApplication;
    }
    XafApplication IDesignTimeApplicationFactory.Create()
        => BuildApplication(XafApplication.DesignTimeConnectionString);
}

2.Use the application builder to create your WinForms or ASP.NET Core Blazor application:
使用应用程序构建器创建WinForms或ASP.NETCore Blazor应用程序:

WinForms File: MySolution.Win\Program.cs.

C# 
static class Program {
    [STAThread]
    static void Main() {
        // Comment out or remove the following lines
        // var winApplication = new MySolutionWindowsFormsApplication();
        // winApplication.GetSecurityStrategy().RegisterXPOAdapterProviders();
        string connectionString = null;
        if(ConfigurationManager.ConnectionStrings["ConnectionString"] != null) {
            connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
        }
        ArgumentNullException.ThrowIfNull(connectionString);
        var winApplication = ApplicationBuilder.BuildApplication(connectionString);
        // ...
    }
}

ASP.NET Core Blazor File: MySolution.Blazor.Server\Startup.cs.

C# 
using DevExpress.ExpressApp.ApplicationBuilder;
using DevExpress.ExpressApp.Blazor.ApplicationBuilder;

namespace MySolution.Blazor.Server;

public class Startup {
    public Startup(IConfiguration configuration) {
        // ...
        // Comment out or remove the following line
        // services.AddXaf<MySolutionBlazorApplication>(Configuration);
        services.AddXaf(Configuration, builder => {
            builder.UseApplication<MySolutionBlazorApplication>();
            builder.Modules
                .Add<MySolutionBlazorModule>();
        });
        // ...
    }
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
        // ...
        app.UseEndpoints(endpoints => {
            endpoints.MapXafEndpoints();
            // ...
        });
    }
}

Step 3. Register Extra Modules in the Application Builder(步骤3.在Application Builder中注册额外模块)

1.Register and configure the required extra modules in your application builder. For example, to add Office, Dashboards, and Reports V2 modules to your application in the application builder, apply the following changes:
在您的应用程序构建器中注册和配置所需的额外模块。例如,要在应用程序构建器中将Office、仪表板和报告V2模块添加到您的应用程序,请应用以下更改:

Comment out or remove the registration of modules from the InitializeComponents method:
注释掉或删除InitializeComponents方法中模块的注册:

Files:
ASP.NET Core Blazor - MySolution.Blazor.Server\BlazorApplication.Designer.cs
WinForms - MySolution.Win\WinApplication.Designer.cs

C# 
private void InitializeComponent() {
    // this.module3 = new MySolution.Module.MySolutionModule();
    // this.module4 = new MySolution.Module.Blazor.MySolutionBlazorModule();
    // this.dashboardsModule = new DevExpress.ExpressApp.Dashboards.DashboardsModule();
    // this.dashboardsBlazorModule = new DevExpress.ExpressApp.Dashboards.Blazor.DashboardsBlazorModule();
    // this.officeModule = new DevExpress.ExpressApp.Office.OfficeModule();
    // this.officeBlazorModule = new DevExpress.ExpressApp.Office.Blazor.OfficeBlazorModule();
    // this.reportsModuleV2 = new DevExpress.ExpressApp.ReportsV2.ReportsModuleV2();
    // this.reportsBlazorModuleV2 = new DevExpress.ExpressApp.ReportsV2.Blazor.ReportsBlazorModuleV2();

    // ...
    // this.dashboardsModule.DashboardDataType = typeof(DevExpress.Persistent.BaseImpl.DashboardData);
    // this.reportsModuleV2.EnableInplaceReports = true;
    // this.reportsModuleV2.ReportDataType = typeof(DevExpress.Persistent.BaseImpl.ReportDataV2);
    // this.reportsModuleV2.ReportStoreMode = DevExpress.ExpressApp.ReportsV2.ReportStoreModes.XML;

    // ...
    // this.Modules.Add(this.module3);
    // this.Modules.Add(this.module4);
    // this.Modules.Add(this.dashboardsModule);
    // this.Modules.Add(this.dashboardsBlazorModule);
    // this.Modules.Add(this.officeModule);
    // this.Modules.Add(this.officeBlazorModule);
    // this.Modules.Add(this.reportsModuleV2);
    // this.Modules.Add(this.reportsBlazorModuleV2);

    // ...
}

2.Use the Modules property and the “AddModuleName” methods (for example, AddDashboards or AddOffice) to add the required modules to your application:
使用Modules属性和“AddModuleName”方法(例如,AddDashboard或AddOffice)将所需的模块添加到您的应用程序:

ASP.NET Core Blazor File: MySolution.Blazor.Server\Startup.cs.

C# 
public void ConfigureServices(IServiceCollection services) {
    // ...
    services.AddXaf(Configuration, builder => {
        builder.UseApplication<MySolutionBlazorApplication>();
        builder.Modules
            .Add<MySolutionBlazorModule>();
            .AddOffice()
            .AddReports(options => {
                options.EnableInplaceReports = true;
                options.ReportDataType = typeof(DevExpress.Persistent.BaseImpl.ReportDataV2);
                options.ReportStoreMode = DevExpress.ExpressApp.ReportsV2.ReportStoreModes.XML;
            })
            .AddDashboards(options => options.DashboardDataType = typeof(DevExpress.Persistent.BaseImpl.DashboardData))
            // ...
        // ...
    });
    // Comment out or remove the previously used registration code
    // services.AddXafReporting();
    // services.AddXafDashboards();
    // services.AddXafOffice();
    // ...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    // ...
    // app.UseDevExpressBlazorReporting();
    app.UseEndpoints(endpoints => {
        // endpoints.MapXafDashboards();
        // ...
    });
}

Note that with this technique, you do not need to add module-specific services, middleware, or endpoints explicitly (for example, you can remove such redundant methods as services.AddXafOffice(), app.UseDevExpressBlazorReporting(), and endpoints.MapXafDashboards()).
请注意,使用这种技术,您不需要显式添加特定于模块的服务、中间件或端点(例如,您可以删除诸如services. AddXafOffice()、app.UseDevExpressBlazorReporting()和endpoint.MapXafDashboard()等冗余方法)。

WinForms File: MySolution.Win\Startup.cs.

C# 
using DevExpress.ExpressApp.Win.ApplicationBuilder;
// ...
public class ApplicationBuilder : IDesignTimeApplicationFactory {
    public static WinApplication BuildApplication(string connectionString) {
        // ...
        builder.Modules
            .Add<MySolutionWindowsFormsModule>()
            .AddOffice()
            .AddReports(options => {
                options.EnableInplaceReports = true;
                options.ReportDataType = typeof(DevExpress.Persistent.BaseImpl.ReportDataV2);
                options.ReportStoreMode = DevExpress.ExpressApp.ReportsV2.ReportStoreModes.XML;
            })
            .AddDashboards(options =>
                options.DashboardDataType = typeof(DevExpress.Persistent.BaseImpl.DashboardData)
            );
        // ...
    }
}

Note
If your application has a customized module-specific exported type (a business class that the Application Model must load), specify this type in the Application Builder module options in the following manner:
如果您的应用程序具有自定义的特定于模块的导出类型(应用程序模型必须加载的业务类),请按以下方式在Application Builder模块选项中指定此类型:

C#
using DevExpress.ExpressApp.ApplicationBuilder;
using DevExpress.ExpressApp.Blazor.ApplicationBuilder;
using DevExpress.Persistent.BaseImpl;

namespace YourApplicationName.Blazor.Server;

public class Startup {
   // ...
    public void ConfigureServices(IServiceCollection services) {
        // ...
        services.AddXaf(Configuration, builder => {
            builder.UseApplication<MySolutionBlazorApplication>();
            builder.Modules
                // ...
                builder.Modules
                    .AddReports(options => {
                        options.ReportDataType = typeof(MyReportDataV2);
                    })
                    .AddScheduler(options => {
                        options.ExportedTypes.Clear();
                        options.ExportedTypes.Add(typeof(MyEvent));
                        options.ExportedTypes.Add(typeof(MyResource));
                    })
                    .AddFileAttachments(options => {
                        options.ExportedTypes.Clear();
                        options.ExportedTypes.Add(typeof(MyFileDate));
                        options.ExportedTypes.Add(typeof(MyFileAttachment));
                    })
            // ...
        });
        // ...
    }
}

Step 4. Configure Object Space Providers(步骤4.配置对象空间提供程序)

1.Comment out or remove the CreateDefaultObjectSpaceProvider method from your applications:
从应用程序中注释或删除CreateDefaultObjectSpaceProvider方法:

Files:
ASP.NET Core Blazor: MySolution.Blazor.Server\BlazorApplication.cs
WinForms: MySolution.Win\WinApplication.cs

C# 
public partial class MySolutionBlazorApplication :
    BlazorApplication, ILegacyInitializationXafApplication {
    // ...
    // protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
    // ...
    // }
}

2.According to your application configuration, use the following methods in your application builders to configure Object Space Providers:
根据您的应用程序配置,在您的应用程序构建器中使用以下方法来配置对象空间提供程序:

  • ASP.NET Core Blazor + XPO: AddXpo<TContext>(IObjectSpaceProviderServiceBasedBuilder<TContext>, Action<IServiceProvider, XPObjectSpaceProviderOptions>)
  • ASP.NET Core Blazor + XPO + Security System: AddSecuredXpo<TContext>(IObjectSpaceProviderServiceBasedBuilder<TContext>, Action<IServiceProvider, SecuredXPObjectSpaceProviderOptions>)
  • ASP.NET Core Blazor + EF Core: AddEFCore<TContext>(IObjectSpaceProviderServiceBasedBuilder<TContext>, Action<EFCoreObjectSpaceProviderOptionsBuilder>)
  • ASP.NET Core Blazor + EF Core + Security System: AddSecuredEFCore<TContext>(IObjectSpaceProviderServiceBasedBuilder<TContext>, Action<EFCoreObjectSpaceProviderOptionsBuilder>)
  • WinForms + XPO: AddXpo<TContext>(IObjectSpaceProviderBuilder<TContext>, Action<XafApplication, XPObjectSpaceProviderOptions>)
  • WinForms + XPO + Security System: AddSecuredXpo(IObjectSpaceProviderBuilder<TContext>, Action<XafApplication, SecuredXPObjectSpaceProviderOptions>)
  • WinForms + EF Core: AddEFCore<TContext>(IObjectSpaceProviderBuilder<TContext>, Action<EFCoreObjectSpaceProviderOptionsBuilder>)
  • WinForms + EF Core + Security System AddSecuredEFCore<TContext>(IObjectSpaceProviderBuilder<TContext>, Action<EFCoreObjectSpaceProviderOptionsBuilder>)
  • ASP.NET Core Blazor File: MySolution.Blazor.Server\Startup.cs.
C# (XPO) 
public void ConfigureServices(IServiceCollection services) {
    // ...
    // services.AddSingleton<XpoDataStoreProviderAccessor>();
    services.AddXaf(Configuration, builder => {
        // ...
        builder.ObjectSpaceProviders
            .AddSecuredXpo((serviceProvider, options) => {
                if (Configuration.GetConnectionString("ConnectionString") != null) {
                    options.ConnectionString = Configuration.GetConnectionString("ConnectionString");
                }
#if EASYTEST
                if(Configuration.GetConnectionString("EasyTestConnectionString") != null) {
                    options.ConnectionString = Configuration.GetConnectionString("EasyTestConnectionString");
                }
#endif
                options.ThreadSafe = true;
                options.UseSharedDataStoreProvider = true;
            })
            .AddNonPersistent();
        // ...
    });
}
C# (EF Core) 
public void ConfigureServices(IServiceCollection services) {
    // ...
    // services.AddSingleton<XpoDataStoreProviderAccessor>();
    services.AddXaf(Configuration, builder => {
        // ...
        builder.ObjectSpaceProviders
            .AddSecuredEFCore().WithAuditedDbContext(contexts => {
                contexts.Configure<MainDemoDbContext, AuditingDbContext>(
                    (application, businessObjectDbContextOptions) => {
                        businessObjectDbContextOptions.UseSqlServer(connectionString);
                    },
                    (application, auditHistoryDbContextOptions) => {
                        auditHistoryDbContextOptions.UseSqlServer(connectionString);
                    });
            })
            .AddNonPersistent();
        // ...
    });
}

WinForms File: MySolution.Win\Startup.cs.

C# (XPO) 
public class ApplicationBuilder : IDesignTimeApplicationFactory {
    public static WinApplication BuildApplication(string connectionString) {
        // ...
        builder.ObjectSpaceProviders
            .AddSecuredXpo((application, options) => {
                options.ConnectionString = connectionString;
            })
            .AddNonPersistent();
        // ...
    }
}
C# (EF Core)
public class ApplicationBuilder : IDesignTimeApplicationFactory {
    public static WinApplication BuildApplication(string connectionString) {
        // ...
        builder.ObjectSpaceProviders
            .AddSecuredEFCore().WithDbContext<YourSolutionName.Module.BusinessObjects.YourSolutionNameEFCoreDbContext>((application, options) => {
                options.UseSqlServer(connectionString);
            })
            .AddNonPersistent();
        // ...
    }
}

Step 5. Configure the Security System(步骤5.配置安全系统)

1.In the application InitializeComponent method, comment out or remove SecurityModule registration and configuration of Security Strategy and authentication:
在应用程序InitializeComponent方法中,注释掉或删除SecurityModule注册和配置安全策略和身份验证:

Files:
ASP.NET Core Blazor
: MySolution.Blazor.Server\BlazorApplication.Designer.cs
WinForms: MySolution.Win\WinApplication.Designer.cs

C# 
private void InitializeComponent() {
    // this.securityModule1 = new DevExpress.ExpressApp.Security.SecurityModule();
    // this.securityStrategyComplex1 = new DevExpress.ExpressApp.Security.SecurityStrategyComplex();
    // this.securityStrategyComplex1.SupportNavigationPermissionsForTypes = false;
    // this.authenticationStandard1 = new DevExpress.ExpressApp.Security.AuthenticationStandard();

    // ...
    // this.securityStrategyComplex1.Authentication = this.authenticationStandard1;
    // this.securityStrategyComplex1.RoleType = typeof(DevExpress.Persistent.BaseImpl.PermissionPolicy.PermissionPolicyRole);
    // this.securityStrategyComplex1.UserType = typeof(MySolution.Module.BusinessObjects.ApplicationUser);
    // this.securityModule1.UserType = typeof(MySolution.Module.BusinessObjects.ApplicationUser);
    // this.authenticationStandard1.LogonParametersType = typeof(DevExpress.ExpressApp.Security.AuthenticationStandardLogonParameters);
    // this.authenticationStandard1.UserLoginInfoType = typeof(MySolution.Module.BusinessObjects.ApplicationUserLoginInfo);

    // ...
    // this.Modules.Add(this.securityModule1);
    // this.Security = this.securityStrategyComplex1;

    // ...
}

2.In ASP.NET Core Blazor applications, comment out or remove the existing AddXafSecurity method call. To set up the Security System, use IBlazorSecurityBuilder.
在ASP.NETCore Blazor应用程序中,注释掉或删除现有的AddXafSecurity方法调用。要设置安全系统,请使用IBlazorSecurityBuilder。

The following example demonstrates how to configure the application that uses the Security System in Integrated Mode with password authentication:
以下示例演示如何配置在集成模式下使用安全系统并进行密码身份验证的应用程序:

File: MySolution.Blazor.Server\Startup.cs.

C# 
public void ConfigureServices(IServiceCollection services) {
    // ...
    services.AddXaf(Configuration, builder => {
        // ...
        builder.Security
            .UseIntegratedMode(options => {
                options.RoleType = typeof(PermissionPolicyRole);
                options.UserType = typeof(MySolution.Module.BusinessObjects.ApplicationUser);
                options.UserLoginInfoType = typeof(MySolution.Module.BusinessObjects.ApplicationUserLoginInfo);
                options.UseXpoPermissionsCaching();
            })
            .AddPasswordAuthentication(options => {
                options.IsSupportChangePassword = true;
            })
            .AddExternalAuthentication(options => { });
        // ...
    });
    // services.AddXafSecurity(options => {
    //     options.RoleType = typeof(PermissionPolicyRole);
    //     options.UserType = typeof(MySolution.Module.BusinessObjects.ApplicationUser);
    //     options.UserLoginInfoType = typeof(MySolution.Module.BusinessObjects.ApplicationUserLoginInfo);
    //     options.Events.OnSecurityStrategyCreated = securityStrategy => ((SecurityStrategy)securityStrategy).RegisterXPOAdapterProviders();
    //     options.SupportNavigationPermissionsForTypes = false;
    // })
    // .AddAuthenticationStandard(options => {
    //     options.IsSupportChangePassword = true;
    // })
    // .AddExternalAuthentication<HttpContextPrincipalProvider>();
}

3.In WinForms applications, use IWinSecurityBuilder to set up the Security System.
在WinForms应用程序中,使用IWinSecurityBuilder设置安全系统。

The following example demonstrates how to configure the application that uses the Security System in Integrated Mode with password authentication:
以下示例演示如何配置在集成模式下使用安全系统并进行密码身份验证的应用程序:

File: MySolution.Win/Startup.cs.

C# 
using DevExpress.ExpressApp.ApplicationBuilder;
using DevExpress.ExpressApp.Security;


public class ApplicationBuilder : IDesignTimeApplicationFactory {
    public static WinApplication BuildApplication(string connectionString) {
        // ...
        builder.Security
            .UseIntegratedMode(options => {
                options.UserType = typeof(MySolution.Module.BusinessObjects.ApplicationUser);
                options.RoleType = typeof(DevExpress.Persistent.BaseImpl.PermissionPolicy.PermissionPolicyRole);
                options.UserLoginInfoType = typeof(MySolution.Module.BusinessObjects.ApplicationUserLoginInfo);
                options.UseXpoPermissionsCaching();
            })
            .UsePasswordAuthentication(options => {
                options.LogonParametersType = typeof(DevExpress.ExpressApp.Security.AuthenticationStandardLogonParameters);
            });
        // ...
    }
}

Step 6. Move Common Application Settings(步骤6.移动通用应用程序设置)

1.Comment out or remove the remaining code from the InitializeComponent method:
注释掉或删除OrializeComponent方法中的剩余代码:

Files:
ASP.NET Core Blazor: MySolution.Blazor.Server\BlazorApplication.Designer.cs
WinForms: MySolution.Win\WinApplication.Designer.cs

C# 
private void InitializeComponent() {
    // ...
    // this.ApplicationName = "MySolution";
    // this.CheckCompatibilityType = DevExpress.ExpressApp.CheckCompatibilityType.DatabaseSchema;
    // this.DatabaseVersionMismatch += new System.EventHandler<DevExpress.ExpressApp.DatabaseVersionMismatchEventArgs>(this.MySolutionBlazorApplication_DatabaseVersionMismatch);
    // ...
}

2.Apply the commented settings in the application constructor or application builder’s AddBuildStep(Action) method:
在应用程序构造函数或应用程序构建器的AddBuildStep(Action)方法中应用注释设置:

Files:
ASP.NET Core Blazor
- MySolution.Blazor.Server\BlazorApplication.cs
WinForms - MySolution.Win\WinApplication.cs

C# 
public partial class MySolutionBlazorApplication : BlazorApplication, ILegacyInitializationXafApplication {
    public MySolutionBlazorApplication() {
        CheckCompatibilityType = DevExpress.ExpressApp.CheckCompatibilityType.DatabaseSchema;
        DatabaseVersionMismatch += MySolutionBlazorApplication_DatabaseVersionMismatch;
    }
    // ...
}

ASP.NET Core Blazor File: MySolution.Blazor.Server\Startup.cs.

C# 
public void ConfigureServices(IServiceCollection services) {
    // ...
    services.AddXaf(Configuration, builder => {
        // ...
        builder.AddBuildStep(application => {
            application.ApplicationName = "MySolution";
        });
    });
    // ...
}

WinForms File: MySolution.Win\Startup.cs.

C# 
public class ApplicationBuilder : IDesignTimeApplicationFactory {
    public static WinApplication BuildApplication(string connectionString) {
        // ...
        builder.AddBuildStep(application => {
            application.ApplicationName = "MySolution";
        });
        // ...
    }
}

Step 7. Remove Unnecessary Code and Files(步骤7.删除不必要的代码和文件)

You can remove the ILegacyInitializationXafApplication interface implementation from your applications and WinApplication.Designer.cs/BlazorApplication.Designer.cs files from your solution.
您可以从应用程序中删除ILegacyInitializationXafApplication接口实现,并从解决方案中删除WinApplication. Designer.cs/BlazorApplication.Designer.cs文件。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汤姆•猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值