.NET6中一些常用组件的配置及使用记录,持续更新中。。。


NET6App

介绍

.NET 6的CoreApp框架,用来学习.NET6的一些变动和新特性,使用EFCore,等一系列组件的运用,每个用单独的文档篇章记录,持续更新文档哦,有什么想要了解的组件啊可以留言或私信我。

如果对您有帮助,点击⭐Star⭐关注 ,感谢支持开源!

软件架构

分为模型层,服务层,接口层来做测试使用

0.如何使用IConfiguration、Environment

直接在builder后的主机中使用。

builder.Configuration;
builder.Environment

1.如何使用Swagger

.NET 6 自带模板已经默认添加Swagger,直接使用即可。

builder.Services.AddSwaggerGen();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

2. 如何添加EFCore到.NET 6中

按照EFCore常规使用方法,申明表的Entity及Dbcontext后,在program.cs文件中添加

builder.Services.AddDbContext<Service.DataContext>(opt => {
    opt.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
});

即可在其他地方注入使用 DataContext

使用Sqlite数据库,需要引用 Microsoft.EntityFrameworkCore.Sqlite,
并在添加服务时,改为

opt.UseSqlite(builder.Configuration.GetConnectionString("Default"));

包管理控制台数据库结构生成方法:
使用 add-migration 创建迁移
使用 update-database 更新数据结构

3.如何注入一个服务

builder.Services.AddScoped<UserIdentyService>();

4.如何定义全局的using引用

在根目录下新建一个 cs文件,比如Globalusing.cs,在里面添加你的全局引用,和常规引用不同的是,在using前面添加 global

global using Service;
global using Entity;
global using Entity.Dto;

5.如何使用Autofac

添加 Nuget 引用

Autofac.Extensions.DependencyInjection

program.cs文件添加autofac的使用和注入配置

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
 {
     Assembly assembly = Assembly.Load("Service.dll");
     builder.RegisterAssemblyTypes(assembly)
            //.AsImplementedInterfaces()// 无接口的注入方式
            .InstancePerDependency();
 });

此时即可构造函数注入使用。

6.如何使用Log4Net

添加引用

Microsoft.Extensions.Logging.Log4Net.AspNetCore

新建配置文件 log4net.config;
添加service配置

//注入Log4Net
builder.Services.AddLogging(cfg =>
{
    //默认的配置文件路径是在根目录,且文件名为log4net.config
    //cfg.AddLog4Net();
    //如果文件路径或名称有变化,需要重新设置其路径或名称
    //比如在项目根目录下创建一个名为config的文件夹,将log4net.config文件移入其中,并改名为log4net.config
    //则需要使用下面的代码来进行配置
    cfg.AddLog4Net(new Log4NetProviderOptions()
    {
        Log4NetConfigFileName = "config/log4net.config",
        Watch = true
    });
});

即可在需要的地方定义使用

_logger = LogManager.GetLogger(typeof(UserController));

7.如何使用全局异常过滤器

首先新建 GlobalExceptionFilter 全局异常过滤器,继承于 ExceptionFilter ,用于接收处理抛出的异常

public class GlobalExceptionFilter : IExceptionFilter
{
    readonly IWebHostEnvironment hostEnvironment;
    readonly ILog logger;
    public GlobalExceptionFilter(IWebHostEnvironment _hostEnvironment)
    {
        this.hostEnvironment = _hostEnvironment;
        this.logger = LogManager.GetLogger(typeof(GlobalExceptionFilter));
    }
    public void OnException(ExceptionContext context)
    {
        if (!context.ExceptionHandled)//如果异常没有处理
        {
            var result = new ApiResult
            {
                Code = 500,
                IsSuccess = false,
                Message = "服务器发生未处理的异常"
            };

            if (hostEnvironment.IsDevelopment())
            {
                result.Message += "," + context.Exception.Message;
                result.Data = context.Exception.StackTrace;
            }

            logger.Error(result);

            context.Result = new JsonResult(result);
            context.ExceptionHandled = true;//异常已处理
        }
    }
}

然后在Service中添加全局异常过滤器

builder.Services.AddControllers(option =>
    {
        option.Filters.Add<GlobalExceptionFilter>();
    }
);

添加控制器方法完成测试

[HttpGet("exception")]
public ApiResult ExceptionAction()
{
    throw new NotImplementedException();
}

8.如何使用redis做缓存

使用 StackExchange.Redis 作为缓存组件(其他组件类似的使用方式)。nuget 安装 StackExchange.Redis.Extensions.Core
首先,先建立一个类 RedisClient ,用于管理redis的连接和操作,再建立一个 RedisClientFactory 类,用于创建 redis的连接;

public class RedisClient{...}
public class RedisClientFactory{...}

appsettings.json 中添加redis的配置

"RedisConfig": {
    "Redis_Default": {
      "Connection": "127.0.0.1:6379",
      "InstanceName": "Redis1:"
    },
    "Redis_6": {
      "Connection": "127.0.0.1:6379",
      "DefaultDatabase": 6,
      "InstanceName": "Redis2:"
    }
  }

service中添加 redis客户端的引用

//添加redis的使用
builder.Services.AddSingleton<RedisClient>(_=> RedisClientFactory.GetInstance(builder.Configuration));

一顿操作后,就可以在你想要使用redis的地方引用了

RedisClient redisClient
...
this.redisDb = redisClient.GetDatabase("Redis_Default");
redisDb.StringSet("clientId", "clientId", TimeSpan.FromSeconds(10));

要使用redis做分布式缓存,先引用 Microsoft.Extensions.Caching.StackExchangeRedis

//将Redis分布式缓存服务添加到服务中
builder.Services.AddStackExchangeRedisCache(options =>
    {
        //用于连接Redis的配置  Configuration.GetConnectionString("RedisConnectionString")读取配置信息的串
        options.Configuration = "Redis_6";// Configuration.GetConnectionString("RedisConnectionString");
        //Redis实例名RedisDistributedCache
        options.InstanceName = "RedisDistributedCache";
    });

引用自 "分布式 Redis 缓存"

9. 如何添加使用定时任务组件

此处使用 Hangfire 定时任务组件,轻便,可持久化,还有面板。
引用 Hangfire 后,即可新增定时任务。

//启用Hangfire服务.
builder.Services.AddHangfire(x => x.UseStorage(new MemoryStorage()));
builder.Services.AddHangfireServer();

...

//启用Hangfire面板
app.UseHangfireDashboard();
//开启一个定时任务
RecurringJob.AddOrUpdate("test",() => Console.WriteLine("Recurring!"), Cron.Minutely());

访问 https://localhost:7219/hangfire 即可看到任务面板

10. 如何使用业务锁锁住下单或者支付操作

首先,做这个事需要能先构建出一个锁出来,这个锁有个锁的标识key,可以根据这个key判定key对应的锁是否存在,
这样的话,在某个用户支付或者下单减库存啥的时候,就可以按照这个key先上锁,后面有用户走其他渠道进行同样的操作的时候,就可以根据是否上锁了,来判断操作能否继续。

比如一个支付订单的业务,可以在手机上操作,也可以在电脑上操作,这个时候就可以给支付接口上锁,只要一个支付过程存在着,并且没有超时,那就不能在其他渠道进行操作。
我们上面已经使用了redis,下面就用redis构建个锁来模拟这个操作,具体看代码:

/// <summary>
    /// 测试业务锁
    /// </summary>
    /// <returns></returns>
    [HttpGet("lockhandle")]
    public async Task<ApiResult> LockHandle(int userId)
    {
        var key = "user";
        var token = $"ID:{userId}";
        try
        {
            if (redisDb.LockTake(key, token, TimeSpan.FromSeconds(50)))
            {
                await Task.Delay(30 * 1000);
                return await Task.FromResult(ApiResult.Success($"ID:{userId} 获取到锁了,操作正常,connectId:{Request.HttpContext.Connection.Id}"));
            }
            else
            {
                return await Task.FromResult(ApiResult.Fail($"有正在操作的锁,connectId:{Request.HttpContext.Connection.Id}"));
            }
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            redisDb.LockRelease(key, token);
        }
    }

11. 如何配置跨域

此处主要记录全局跨域,不包括指定api跨域。先增加一个配置 "Cors": "http:127.0.0.1:5001",配置可以跨域的url,也可以使用默认跨域配置。
host配置以下服务,按需使用:

builder.Services.AddCors(delegate (CorsOptions options)
{
    options.AddPolicy("CorsPolicy", delegate (CorsPolicyBuilder corsBuilder)
    {
        //指定url跨域
        corsBuilder.WithOrigins(builder.Configuration.GetValue<string>("Cors").Split(','));
        //默认跨域
        corsBuilder.SetIsOriginAllowed((string _) => true).AllowAnyMethod().AllowAnyHeader()
            .AllowCredentials();
    });
});

12. 如何使用NewtonsoftJson

.NET6 默认的系列化库是内置的 System.Text.Json,使用中如果有诸多不熟悉的地方,那肯定是想换回 NewtonsoftJson,需要nuget 引用 Microsoft.AspNetCore.Mvc.NewtonsoftJson 来配置使用,
常用配置包括日期格式、大小写规则、循环引用配置。。。等,下面是一个配置

builder.Services.AddControllers(option =>
    {
        option.Filters.Add<GlobalExceptionFilter>();
    }
).AddNewtonsoftJson(options =>
{
    options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); //序列化时key为驼峰样式
    options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
    options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
    options.SerializerSettings.ReferenceLoopHandling =  ReferenceLoopHandling.Ignore;//忽略循环引用
});

13. 如何使用SignalR

首先添加一个 ChatHub 作为 交互中心处理器

public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }

在主机中使用服务

builder.Services.AddSignalR();
...
app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<ChatHub>("/chatHub");
});

14. 如何使用Dapper

Dapper是大家常用的一个数据库连接扩展组件,下面介绍下,如何使用常规扩展,来在.net Core中使用Dapper。
首先,建立一个DbComponent ,来获取由 .netCore 提供的 Configuration 配置文件,并用 DbProviderFactories 工厂,创建数据库连接,此类只管创建连接,又其他使用类进行销毁。

/// <summary>
/// 创建连接处理
/// </summary>
public class DbComponent
{
    /// 数据库连接配置
    private static ConnectionStringSettings connectionSetting;

    public static void InitDapper(ConnectionStringSettings connectionStringSettings)
    {
        connectionSetting = connectionStringSettings;
    }

    //通过工厂模式创建Connection连接 此连接已打开
    public static IDbConnection GetConnection()
    {
        get {
            var cnnection = DbProviderFactories.GetFactory(connectionSetting.ProviderName).CreateConnection();
            if (cnnection == null)
                throw new Exception("数据库链接获取失败!");
            cnnection.ConnectionString = connectionSetting.ConnectionString;
            cnnection.Open();
            return cnnection;
        }
    }
}

使用前,需要在program中初始化一下组件

/// <summary>
/// 初始化Dapper组件
/// </summary>
DbProviderFactories.RegisterFactory("Microsoft.Data.Sqlite", Microsoft.Data.Sqlite.SqliteFactory.Instance);
DbComponent.InitDapper(new System.Configuration.ConnectionStringSettings
{
    ConnectionString = builder.Configuration.GetConnectionString("Default"),
    ProviderName = "Microsoft.Data.Sqlite"
});

程序启动后,就可以在需要的地方使用

public class UserIdentyService
{
    public ApiResult DapperList()
    {
        using (var connect = DbComponent.Connection)
        {
            var users= connect.Query<User>("SELECT * FROM Users").ToList();
            return ApiResult.Success(users);
        }
    }    
}

15. 如何添加自定义配置文件

有时候我们不想把配置全部放在 appsettings.json ,我们想自己建立一个文件夹来存储其他配置文件,比如config/...json之类的,咋整呢,
我们新建个文件夹 config,下面建立一个配置文件app.json,里面存几个配置以便验证。

使用前添加如下代码即可

builder.Configuration.AddJsonFile("config/app.json");
Console.WriteLine(builder.Configuration.GetValue<string>("weixin"));

16. 如何简单上传文件

上传文件是每个api框架都会实现的功能,我们先实现一个简单的文件上传。
首先做个配置文件,存储上传的文件存储位置、大小及格式限制等的配置

public class UploadConfig
{
    /// <summary>
    /// 最大值
    /// </summary>
    public int MaxSize { get; set; } = 1024 * 1024 * 1024;
    /// <summary>
    /// 存储路径
    /// </summary>
    public string UploadDir { get; set; } = @"D://Upload";
    /// <summary>
    /// 站点名称
    /// </summary>
    public string WebSite { get; set; }
}

添加测试action,完成文件上传,并返回文件访问路径

/// <summary>
/// 上传文件测试
/// </summary>
/// <param name="files"></param>
/// <returns></returns>
[HttpPost("upload")]
public async Task<ApiResult> Upload([FromForm(Name ="file")] List<IFormFile> files)
{
    var config = configuration.GetSection("UploadConfig").Get<UploadConfig>();
    if (files.Count == 0)
    {
        return ApiResult.Fail("没有需要上传的文件");
    }
    var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.UploadDir);
    
    if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

    //验证大小或者格式之类
    foreach (var file in files)
    {
        var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName;
        var fileSize = file.Length;
        if (fileSize > config.MaxSize)
        {
            return ApiResult.Fail($"{fileName}文件过大");
        }
    }
    //存储文件
    var result = new List<string>();
    foreach (var file in files)
    {
        var fileName = file.FileName;
        using (var stream = System.IO.File.Create(Path.Combine(dir, fileName)))
        {
            await file.CopyToAsync(stream);
        }
        result.Add(string.Join('/',config.WebSite,"upload/view" fileName));
    }
    return ApiResult.Success(result);
}

上述文件访问路径需要配置静态目录来进行访问

//启动www静态目录
app.UseStaticFiles();
//启动上传文件目录
app.UseStaticFiles(new StaticFileOptions { 
    FileProvider =  new PhysicalFileProvider(builder.Configuration.GetValue<string>("UploadConfig:UploadDir")),
    RequestPath = "/upload/view"
});

至此,文件上传及访问已添加完成

17. 如何添加验证码

验证码是常用的一个api功能,需要完成2步,先生成验证码字符串值及存储,后需要输出验证码图片到前端,即验证码功能需要2个api,1.获取验证码,2.验证验证码。
先实现一个类,用于获取随机值及生成图片
为了方便部署到unix系统,使用 ImageSharp 相关类库

public class CaptchaHelper
{
   private const string Letters = "1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z";

   /// <summary>
   /// 生成验证码随机值
   /// </summary>
   /// <param name="codeLength"></param>
   /// <returns></returns>
   public Task<string> GenerateRandomCaptchaAsync(int codeLength = 4)
   {
       var array = Letters.Split(new[] { ',' });
       var random = new Random();
       var temp = -1;
       var captcheCode = string.Empty;
       for (int i = 0; i < codeLength; i++)
       {
           if (temp != -1)
               random = new Random(i * temp * unchecked((int)DateTime.Now.Ticks));

           var index = random.Next(array.Length);
           if (temp != -1 && temp == index)
               return GenerateRandomCaptchaAsync(codeLength);

           temp = index;
           captcheCode += array[index];
       }
       return Task.FromResult(captcheCode);
   }

   /// <summary>
   /// 生成验证码及图片
   /// </summary>
   /// <param name="captchaCode"></param>
   /// <param name="width"></param>
   /// <param name="height"></param>
   /// <returns></returns>
   public Task<(string code, MemoryStream ms)> GenerateCaptchaImageAsync(string captchaCode, int width = 0, int height = 30)
   {
       //验证码颜色集合
       Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
       //验证码字体集合
       string[] fonts = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial" };
       var r = new Random();
       
       if(width == 0) { width = captchaCode.Length * 25; }
       //定义图像的大小,生成图像的实例
       using var image = new Image<Rgba32>(width, height);

       // 字体
       var font = SystemFonts.CreateFont(SystemFonts.Families.First().Name, 25, FontStyle.Bold);

       image.Mutate(ctx =>
       {
           // 白底背景
           ctx.Fill(Color.White);

           // 画验证码
           for (int i = 0; i < captchaCode.Length; i++)
           {
               ctx.DrawText(captchaCode[i].ToString()
                   , font
                   , colors[r.Next(colors.Length)]
                   , new PointF(20 * i + 10, r.Next(2, 12)));
           }

           // 画干扰线
           for (int i = 0; i < 10; i++)
           {
               var pen = new Pen(colors[r.Next(colors.Length)], 1);
               var p1 = new PointF(r.Next(width), r.Next(height));
               var p2 = new PointF(r.Next(width), r.Next(height));

               ctx.DrawLines(pen, p1, p2);
           }

           // 画噪点
           for (int i = 0; i < 80; i++)
           {
               var pen = new Pen(colors[r.Next(colors.Length)], 1);
               var p1 = new PointF(r.Next(width), r.Next(height));
               var p2 = new PointF(p1.X + 1f, p1.Y + 1f);

               ctx.DrawLines(pen, p1, p2);
           }
       });
       using var ms = new MemoryStream();

       // gif 格式
       image.SaveAsGif(ms);
       return Task.FromResult((captchaCode, ms));
   }
}

测试代码

/// <summary>
/// 测试验证码
/// </summary>
/// <returns></returns>
[HttpGet("captcha")]
public async Task<IActionResult> GetCaptcha()
{
   var captchaHelper = new CaptchaHelper();
   var captcha = await captchaHelper.GenerateCaptchaImageAsync();
   this.HttpContext.Session.SetString("captcha", captcha.code);
   return File(captcha.ms.ToArray(), "image/gif");
}

18. 如何发布到windows

发布到windows比较简单,一步步选择发布,只要注意是按照框架依赖还是独立发布就好了,框架依赖的意思是, 你的服务器已经安装好了个.NetCore的运行时,你就可以直接框架依赖来发布就好了,如果你的服务器没有安装运行时, 或者是需要兼容以前的老的运行时不能更新啥的,你就使用独立发布。

19. 如何发布到linux

发布到linux其实也比较简单,首先,需要有待发布的程序文件,和windows一样,做好框架依赖和独立发布的安装包,目标运行时记得选择linux.64 或者linux-arm,编译发布,获得程序文件。
服务器运行时,和windows一样,框架依赖需要安装下.NET Core的运行时,注意匹配版本号,运行时安装好之后,把程序通过xshell 或者 scp压缩包上传到linux服务器你想要的目录下面,完成程序文件部署,在运行时版本没问题的情况下,dotnet xxx.dll 即可启动站点,还有一些其他的配置可能需要注意,比如后台运行,比如开放防火墙端口之类的,照着整就可以了。

引用来源:https://www.cnblogs.com/Start201505/p/15713345.html

版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
上一篇:Blazor Webassembly多标签页实现非iframe的实现
下一篇:WPF开发随笔收录-唯一标识符GUID
评论列表

发表评论

评论内容
昵称:
验证码:
验证码
关联文章

.NET6一些常用组件配置使用记录持续更新。。。
Visual Studio 一些常用设置
使用Hot Chocolate和.NET 6构建GraphQL应用(1)——GraphQL示例项目介绍
在Winform项目和Web API.NetCore项目使用Serilog 来记录日志信息
使用.NET 6开发TodoList应用(6)——使用MediatR实现POST请求
使用.NET 6开发TodoList应用(26)——实现Configuration和Option强类型绑定
使用.NET 6开发TodoList应用(12)——实现ActionFilter
使用.NET 6开发TodoList应用(31)——实现基于Github Actions和ACICI/CD
使用.NET 6开发TodoList应用(27)——实现APISwagger文档化
使用.NET 6开发TodoList应用(24)——实现基于JWTIdentity功能
Quartz在.NET使用
使用.NET 6开发TodoList应用(25)——实现RefreshToken
使用.NET 6开发TodoList应用(22)——实现缓存
使用.NET 6开发TodoList应用(10)——实现DELETE请求以及HTTP请求幂等性
使用.NET 6开发TodoList应用(9)——实现PUT请求
.NET Core MVC中间件使用记录日志
.NET C#教程初级篇 1-1 基本数据类型其存储方式
202607更新记录
使用.NET 6开发TodoList应用(15)——实现查询搜索
使用.NET 6开发TodoList应用(8)——实现全局异常处理

热门标签
.NET Core .NET Reactor ag-grid AI发布 api安全 ASP.NET Core C#DLL加密 C#播放声音 C#代码混淆 C#代码加密 ChromeDriver Codex DateTime DBeaver devexpress devTool DLL混淆 edge.js EF EFCore Electron element-ui el-form el-table excel FastReport FileStream FolderBrowerDialog FolderSelectDialog form提交 git gridcontrol gridview input javascript json字符串 JS转换对象JSON jwt JWT授权 linq log Math MCP mitmproxy MVC MySQL Navicat netstat nginx node_modules NSwag Nuget Nuget镜像 number PowerShell pyinstaller python pythoncom python爬虫 python抓包 pywin32 redis Requests-html RestSharp Selenium sql SQL Server Swagger to-cms Visual Studio VSCode vue VueRouter vue路由 VUE页面通讯 Webpack Windows Windows服务 winform wmi xlrd yaml YESCMS YESWEB开发框架 白象 表单提交 播放声音 打开URL 代码混淆 弹窗提醒 端口占用 对象转换 分布式 公共字典 机器码 进程排查 静态资源 开发指南 路由参数 密钥 配置教程 配置文件 权限 人工智能 任务 任务调度 日期间隔 日志 日志记录 省市区 授权验证 数据库 四舍五入 文案 文件读取 文件夹选择 文件目录选择 问题排查 行政区域数据 页面通讯 中间件 CSharp 事务锁 工单系统 并发控制 重复提交 CMS Markdig Markdown markdown-it marked 技术选型 VS Code 开发工具 源代码管理 版本控制 Docker PostgreSQL 时区 部署排查 CMS架构 EF Core 主题系统 二次开发 插件系统 容器 运维命令 镜像清理 Linux NAS 远程挂载 飞牛 fnOS S/4HANA SAP GUI SAP HANA SAP R/3 SAP入门 SAP版本 ERP SAP SAP MM 库存管理 物料管理 采购管理 入门教程 SAP S/4HANA SPRO 企业结构 采购组织 MM01 物料主数据 物料类型 BP分组 业务伙伴 供应商主数据 ME41 RFQ 库存物料 采购流程 ME51 消耗性物料 科目分配 采购申请 AC03 ML81N 外部服务 服务主数据 Business Partner SAP培训 ME51N MM模块 Lean Services MM-SRV 外部服务采购 PIR 供应来源 采购主数据 采购信息记录 ME31K 框架协议 计划协议 采购合同 ME01 供应来源确定 货源清单 MEQ1 供应源确定 配额安排 配额评分 MD04 MD21 MRP 计划文件 需求计划 批量程序 MD01N MD02 MRP Live MD05 MM 物料计划 优化采购 供应源 采购订单 ME2A 供应商确认 采购监控 Flexible Workflow 凭证释放 采购审批 释放策略 实地盘点 物料凭证 货物移动 MIGO 收货 移动类型 已撤回 供应商退货 货物发出 STO 库存转储 转移过账 生产订单 预留 GR/IR MIRO 供应商发票 物流发票校验 OMR2 税码 FI PP SD 实操教程 MRBR OMR6 发票差异 交货成本 后续借记 MI01 实物盘点 盘点差异 公司代码 工厂 组织结构 OMS2 主数据定制 自动科目确定 BP角色 CVI 伙伴确定 编号范围 凭证类型 字段选择 FBN1 OMBT OMC2 会计凭证 OMJJ BOM 委外加工 项目类别L MRKO 供应商寄售 特殊库存K MRKON PIPE Pipeline 特殊库存P ERS MRIS 发票计划 周期性结算 里程碑付款 变更追踪 版本管理 采购凭证 SFTP WebDAV 网盘 飞牛fnOS AMPL HERS MPN 中文教程 库存确定 可用性检查 缺件检查 Output Management 消息确定 输出确定 分割评估 库存计价 评估类别 评估类型 PB00 RM0000 条件技术 采购定价 MM-FI集成 OBYC 库存估价 文本类型 文本采用 EFB EVO MSV SU3 用户参数 发票校验 合同参照 履约保留款 特别总账 预付款 Fiori Launchpad SAP Fiori 应用导航 用户体验 LSMW LTMC Migration Cockpit 数据迁移 BRFplus OPD Output Control My Inbox 审批流程 灵活工作流 SAP PP 外部加工 SAP QM 检验批 质量信息记录 采购收货 SAP PM 维护BOM 维护订单 SAP SD SAP Service 端到端流程 MM模块培训 FI-MM集成 供应商管理 审批配置 FICO入门 SAP FICO 财务配置 供应商税务 预扣税 House Bank 银行对账 客户清账 应收账款 FI控制 验证与替代 印度 GST 税务配置 F110 FBZP EWM入门 SAP EWM 仓库管理 OX14 成本核算 物料评估 后勤配置 物料组 价值更新 数量更新 PP-PI 流程制造 生产计划 容差配置 SAP事务码 SAP基础 TCODE Basis 事务代码 MMNR 编号区间 采购实操 组织架构 OMSF SAP实操 FI配置 端口修改 密码设置 数据库配置 远程访问 ABAP基础 SAP ABAP 内表 变量定义 常量 数据类型 ABAP SAP开发 变量 基础语法 系统变量 结构体 字符串处理 循环语句 控制语句 DDIC SE11 数据字典 透明表 ABAP开发 ALE EDI IDoc 增强技术 ABAP内表 HASHED TABLE SORTED TABLE STANDARD TABLE 基本概念 性能优化 PARAMETERS SELECT-OPTIONS SELECTION-SCREEN 报表程序 选择屏幕 F4帮助 Report事件 输入校验 ABAP SQL ABAP语法 Open SQL SELECT 数据库访问 IKuai IP修改 PVE 网络配置 虚拟化
联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码