初始化项目
This commit is contained in:
parent
39ffb5c190
commit
3b0d56a0ce
230
.editorconfig
Normal file
230
.editorconfig
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
root = true
|
||||
|
||||
[*.cs]
|
||||
charset = utf-8 #设置文件字符集为utf-8,在 Linux 系统中,通常推荐使用 UTF-8 而不是 UTF-8 with BOM。添加 BOM 可能会干扰那些不期望在文件开头出现非 ASCII 字节的软件对 UTF-8 的使用。
|
||||
|
||||
#### Core EditorConfig 选项 ####
|
||||
|
||||
# 缩进和间距
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# 新行首选项
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET 编码约定 ####
|
||||
|
||||
# 组织 Using
|
||||
dotnet_separate_import_directive_groups = true
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. 和 Me. 首选项
|
||||
dotnet_style_qualification_for_event = false
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# 语言关键字与 bcl 类型首选项
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
dotnet_style_predefined_type_for_member_access = true
|
||||
|
||||
# 括号首选项
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# 修饰符首选项
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||
|
||||
# 表达式级首选项
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_collection_initializer = true
|
||||
dotnet_style_explicit_tuple_names = true
|
||||
dotnet_style_namespace_match_folder = true
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_object_initializer = true
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true:suggestion
|
||||
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_return = true
|
||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||
dotnet_style_prefer_inferred_tuple_names = true
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
|
||||
# 字段首选项
|
||||
dotnet_style_readonly_field = false
|
||||
|
||||
# 参数首选项
|
||||
dotnet_code_quality_unused_parameters = all:silent
|
||||
|
||||
# 禁止显示首选项
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
# 新行首选项
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = true
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||
|
||||
#### c# 编码约定 ####
|
||||
|
||||
# var 首选项
|
||||
csharp_style_var_elsewhere = false
|
||||
csharp_style_var_for_built_in_types = false
|
||||
csharp_style_var_when_type_is_apparent = false
|
||||
|
||||
# Expression-bodied 成员
|
||||
csharp_style_expression_bodied_accessors = true
|
||||
csharp_style_expression_bodied_constructors = false
|
||||
csharp_style_expression_bodied_indexers = true
|
||||
csharp_style_expression_bodied_lambdas = true
|
||||
csharp_style_expression_bodied_local_functions = false
|
||||
csharp_style_expression_bodied_methods = false
|
||||
csharp_style_expression_bodied_operators = false
|
||||
csharp_style_expression_bodied_properties = true
|
||||
|
||||
# 模式匹配首选项
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_extended_property_pattern = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true
|
||||
csharp_style_prefer_switch_expression = false:silent
|
||||
|
||||
# Null 检查首选项
|
||||
csharp_style_conditional_delegate_call = true
|
||||
|
||||
# 修饰符首选项
|
||||
csharp_prefer_static_local_function = false
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
|
||||
csharp_style_prefer_readonly_struct = false
|
||||
csharp_style_prefer_readonly_struct_member = true
|
||||
|
||||
# 代码块首选项
|
||||
csharp_prefer_braces = true:suggestion
|
||||
csharp_prefer_simple_using_statement = true
|
||||
csharp_style_namespace_declarations = block_scoped
|
||||
csharp_style_prefer_method_group_conversion = true
|
||||
csharp_style_prefer_primary_constructors = true
|
||||
csharp_style_prefer_top_level_statements = true
|
||||
|
||||
# 表达式级首选项
|
||||
csharp_prefer_simple_default_expression = true
|
||||
csharp_style_deconstructed_variable_declaration = true
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = false:silent
|
||||
csharp_style_inlined_variable_declaration = true
|
||||
csharp_style_prefer_index_operator = false:silent
|
||||
csharp_style_prefer_local_over_anonymous_function = true
|
||||
csharp_style_prefer_null_check_over_type_check = true
|
||||
csharp_style_prefer_range_operator = false:silent
|
||||
csharp_style_prefer_tuple_swap = false:silent
|
||||
csharp_style_prefer_utf8_string_literals = true
|
||||
csharp_style_throw_expression = true
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:silent
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||
|
||||
# "using" 指令首选项
|
||||
csharp_using_directive_placement = outside_namespace:suggestion
|
||||
|
||||
# 新行首选项
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||
|
||||
#### C# 格式规则 ####
|
||||
|
||||
# 新行首选项
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# 缩进首选项
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# 空格键首选项
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# 包装首选项
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### 命名样式 ####
|
||||
|
||||
# 命名规则
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# 符号规范
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# 命名样式
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
||||
365
.gitignore
vendored
Normal file
365
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
output/
|
||||
logs/
|
||||
30
src/CloudGaming/.dockerignore
Normal file
30
src/CloudGaming/.dockerignore
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
230
src/CloudGaming/.editorconfig
Normal file
230
src/CloudGaming/.editorconfig
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
root = true
|
||||
|
||||
[*.cs]
|
||||
charset = utf-8 #设置文件字符集为utf-8,在 Linux 系统中,通常推荐使用 UTF-8 而不是 UTF-8 with BOM。添加 BOM 可能会干扰那些不期望在文件开头出现非 ASCII 字节的软件对 UTF-8 的使用。
|
||||
|
||||
#### Core EditorConfig 选项 ####
|
||||
|
||||
# 缩进和间距
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# 新行首选项
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET 编码约定 ####
|
||||
|
||||
# 组织 Using
|
||||
dotnet_separate_import_directive_groups = true
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. 和 Me. 首选项
|
||||
dotnet_style_qualification_for_event = false
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# 语言关键字与 bcl 类型首选项
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
dotnet_style_predefined_type_for_member_access = true
|
||||
|
||||
# 括号首选项
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# 修饰符首选项
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||
|
||||
# 表达式级首选项
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_collection_initializer = true
|
||||
dotnet_style_explicit_tuple_names = true
|
||||
dotnet_style_namespace_match_folder = true
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_object_initializer = true
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true:suggestion
|
||||
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_return = true
|
||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||
dotnet_style_prefer_inferred_tuple_names = true
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
|
||||
# 字段首选项
|
||||
dotnet_style_readonly_field = false
|
||||
|
||||
# 参数首选项
|
||||
dotnet_code_quality_unused_parameters = all:silent
|
||||
|
||||
# 禁止显示首选项
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
# 新行首选项
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = true
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||
|
||||
#### c# 编码约定 ####
|
||||
|
||||
# var 首选项
|
||||
csharp_style_var_elsewhere = false
|
||||
csharp_style_var_for_built_in_types = false
|
||||
csharp_style_var_when_type_is_apparent = false
|
||||
|
||||
# Expression-bodied 成员
|
||||
csharp_style_expression_bodied_accessors = true
|
||||
csharp_style_expression_bodied_constructors = false
|
||||
csharp_style_expression_bodied_indexers = true
|
||||
csharp_style_expression_bodied_lambdas = true
|
||||
csharp_style_expression_bodied_local_functions = false
|
||||
csharp_style_expression_bodied_methods = false
|
||||
csharp_style_expression_bodied_operators = false
|
||||
csharp_style_expression_bodied_properties = true
|
||||
|
||||
# 模式匹配首选项
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_extended_property_pattern = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true
|
||||
csharp_style_prefer_switch_expression = false:silent
|
||||
|
||||
# Null 检查首选项
|
||||
csharp_style_conditional_delegate_call = true
|
||||
|
||||
# 修饰符首选项
|
||||
csharp_prefer_static_local_function = false
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
|
||||
csharp_style_prefer_readonly_struct = false
|
||||
csharp_style_prefer_readonly_struct_member = true
|
||||
|
||||
# 代码块首选项
|
||||
csharp_prefer_braces = true:suggestion
|
||||
csharp_prefer_simple_using_statement = true
|
||||
csharp_style_namespace_declarations = block_scoped
|
||||
csharp_style_prefer_method_group_conversion = true
|
||||
csharp_style_prefer_primary_constructors = true
|
||||
csharp_style_prefer_top_level_statements = true
|
||||
|
||||
# 表达式级首选项
|
||||
csharp_prefer_simple_default_expression = true
|
||||
csharp_style_deconstructed_variable_declaration = true
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = false:silent
|
||||
csharp_style_inlined_variable_declaration = true
|
||||
csharp_style_prefer_index_operator = false:silent
|
||||
csharp_style_prefer_local_over_anonymous_function = true
|
||||
csharp_style_prefer_null_check_over_type_check = true
|
||||
csharp_style_prefer_range_operator = false:silent
|
||||
csharp_style_prefer_tuple_swap = false:silent
|
||||
csharp_style_prefer_utf8_string_literals = true
|
||||
csharp_style_throw_expression = true
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:silent
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||
|
||||
# "using" 指令首选项
|
||||
csharp_using_directive_placement = outside_namespace:suggestion
|
||||
|
||||
# 新行首选项
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||
|
||||
#### C# 格式规则 ####
|
||||
|
||||
# 新行首选项
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# 缩进首选项
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# 空格键首选项
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# 包装首选项
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### 命名样式 ####
|
||||
|
||||
# 命名规则
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# 符号规范
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# 命名样式
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
27
src/CloudGaming/1-utile/HuanMeng.DotNetCore/Base/BLLBase.cs
Normal file
27
src/CloudGaming/1-utile/HuanMeng.DotNetCore/Base/BLLBase.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
namespace HuanMeng.DotNetCore.Base
|
||||
{
|
||||
public abstract class BLLBase<TDao> where TDao : DaoBase
|
||||
{
|
||||
/// <summary>
|
||||
/// _serviceProvider,提供基本依赖注入支持
|
||||
/// </summary>
|
||||
protected readonly IServiceProvider _serviceProvider;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// _dao,提供数据访问支持
|
||||
/// </summary>
|
||||
public abstract TDao Dao { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider"></param>
|
||||
/// <param name="dao"></param>
|
||||
public BLLBase(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
using XLib.DotNetCore.Base;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Base
|
||||
{
|
||||
/// <summary>
|
||||
/// 接口和服务调用基础响应类
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
[DataContract]
|
||||
[Serializable]
|
||||
public class BaseResponse<T> : IResponse
|
||||
{
|
||||
|
||||
///// <summary>
|
||||
///// Http状态码
|
||||
///// </summary>
|
||||
//[DataMember]
|
||||
//public HttpStatusCode StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 功能执行返回代码
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public int Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public T? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public BaseResponse()
|
||||
{
|
||||
//StatusCode = HttpStatusCode.OK;
|
||||
Code = 0;
|
||||
Message = "";
|
||||
}
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public BaseResponse(int code, string message)
|
||||
{
|
||||
Code = code;
|
||||
Message = message;
|
||||
Data = default(T);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public BaseResponse(int code, string message, T? data)
|
||||
{
|
||||
Code = code;
|
||||
Message = message;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public BaseResponse(ResonseCode code, string message, T? data)
|
||||
{
|
||||
Code = (int)code;
|
||||
Message = message;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToString
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Code:{Code};Message:{Message}; Data:{Data}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
32
src/CloudGaming/1-utile/HuanMeng.DotNetCore/Base/DaoBase.cs
Normal file
32
src/CloudGaming/1-utile/HuanMeng.DotNetCore/Base/DaoBase.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
namespace HuanMeng.DotNetCore.Base
|
||||
{
|
||||
public class DaoBase
|
||||
{
|
||||
/// <summary>
|
||||
/// _serviceProvider,提供基本依赖注入支持
|
||||
/// </summary>
|
||||
protected readonly IServiceProvider _serviceProvider;
|
||||
|
||||
///// <summary>
|
||||
///// 构造函数
|
||||
///// </summary>
|
||||
//public DaoBase()
|
||||
//{
|
||||
// // 创建一个空的ServiceCollection
|
||||
// var webApplication = WebApplication.Current;
|
||||
// var services = new ServiceCollection();
|
||||
|
||||
// // 创建ServiceProvider实例
|
||||
// _serviceProvider = services.BuildServiceProvider();
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider"></param>
|
||||
public DaoBase(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Base
|
||||
{ /// <summary>
|
||||
/// 基本数据库操作,需要安装
|
||||
/// Microsoft.EntityFrameworkCore
|
||||
/// Microsoft.EntityFrameworkCore.Relational
|
||||
/// </summary>
|
||||
/// <typeparam name="TDbContext"></typeparam>
|
||||
public class EfCoreDaoBase<TDbContext> where TDbContext : DbContext
|
||||
//, new()
|
||||
{
|
||||
private TDbContext _context;
|
||||
public TDbContext context
|
||||
{
|
||||
get
|
||||
{
|
||||
//return _context ?? (_context = new TDbContext());
|
||||
return _context;
|
||||
}
|
||||
//set { _context = value; }
|
||||
}
|
||||
|
||||
//public EfCoreDaoBase() { }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public EfCoreDaoBase(TDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
/// <summary>
|
||||
/// 是否手动提交
|
||||
/// </summary>
|
||||
public bool IsManualSubmit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SqlQueryRaw
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public T SqlQuery<T>(string sql, params object[] parameters) where T : class
|
||||
{
|
||||
var quiry = context.Database.SqlQueryRaw<T>(sql, parameters);
|
||||
return quiry.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SqlQueryList
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public List<T> SqlQueryList<T>(string sql, params object[] parameters)
|
||||
{
|
||||
var quiry = context.Database.SqlQueryRaw<T>(sql, parameters);
|
||||
return quiry.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ExecuteSql
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public int ExecuteSql(string sql, params object[] parameters)
|
||||
{
|
||||
int r = context.Database.ExecuteSqlRaw(sql, parameters);
|
||||
context.SaveChanges();
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加实体
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public void Add<T>(T entity) where T : class
|
||||
{
|
||||
context.Set<T>().Add(entity);
|
||||
if (!IsManualSubmit)
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量添加实体
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="entity"></param>
|
||||
public void AddRange<T>(List<T> entity) where T : class
|
||||
{
|
||||
context.Set<T>().AddRange(entity);
|
||||
if (!IsManualSubmit)
|
||||
context.SaveChanges();
|
||||
}
|
||||
/// <summary>
|
||||
/// 删除某个实体
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public void Delete<T>(T entity) where T : class
|
||||
{
|
||||
context.Entry<T>(entity).State = EntityState.Deleted; //整条更新
|
||||
//context.Set<T>().Remove(entity);
|
||||
if (!IsManualSubmit)
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新实体
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public void Update<T>(T entity) where T : class
|
||||
{
|
||||
//一般不需要整条更新,按需更新字段即可
|
||||
if (context.Entry<T>(entity).State == EntityState.Detached)
|
||||
{
|
||||
context.Set<T>().Attach(entity);
|
||||
context.Entry<T>(entity).State = EntityState.Modified; //整条更新
|
||||
}
|
||||
if (!IsManualSubmit)
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除上下文跟踪(清除缓存) by wyg
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="entity"></param>
|
||||
public void removeTracking<T>(T entity) where T : class
|
||||
{
|
||||
if (context.Entry<T>(entity).State != EntityState.Detached)
|
||||
context.Entry<T>(entity).State = EntityState.Detached;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取实体,从缓存中,根据键值获取
|
||||
/// </summary>
|
||||
/// <param name="keyName"></param>
|
||||
/// <param name="keyValue"></param>
|
||||
/// <returns></returns>
|
||||
public T GetModel<T>(params object[] keyValues) where T : class
|
||||
{
|
||||
return context.Set<T>().Find(keyValues);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取实体,根据条件获取,从数据库获取
|
||||
/// </summary>
|
||||
/// <param name="where">筛选条件</param>
|
||||
/// <param name="isNoTracking">默认false会进行缓存并跟踪(可按需update字段),true不需要缓存和跟踪(一般只读查询使用)</param>
|
||||
/// <returns></returns>
|
||||
public T GetModel<T>(System.Linq.Expressions.Expression<Func<T, bool>> where, bool isNoTracking = false) where T : class
|
||||
{
|
||||
if (!isNoTracking)
|
||||
return context.Set<T>().FirstOrDefault(where); //可按需update字段
|
||||
return context.Set<T>().AsNoTracking<T>().FirstOrDefault(where); //一般只读查询使用
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取列表数据,返回IQueryable
|
||||
/// </summary>
|
||||
/// <param name="where">筛选条件</param>
|
||||
/// <param name="isNoTracking">默认false会进行缓存并跟踪(可按需update字段),true不需要缓存和跟踪(一般只读查询使用)</param>
|
||||
/// <returns></returns>
|
||||
public IQueryable<T> GetList<T>(System.Linq.Expressions.Expression<Func<T, bool>> where, bool isNoTracking = false) where T : class
|
||||
{
|
||||
if (!isNoTracking)
|
||||
return context.Set<T>().Where(where).AsQueryable();
|
||||
return context.Set<T>().Where(where).AsNoTracking<T>().AsQueryable();
|
||||
}
|
||||
|
||||
public int GetCount<T>(System.Linq.Expressions.Expression<Func<T, bool>> where) where T : class
|
||||
{
|
||||
return context.Set<T>().AsNoTracking<T>().Count(where);
|
||||
}
|
||||
public bool Exists<T>(System.Linq.Expressions.Expression<Func<T, bool>> where) where T : class
|
||||
{
|
||||
//return context.Set<T>().AsNoTracking<T>().Any(where);
|
||||
return context.Set<T>().AsNoTracking<T>().Count(where) > 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
namespace XLib.DotNetCore.Base
|
||||
{
|
||||
/// <summary>
|
||||
/// 接口和服务调用通用响应接口
|
||||
/// </summary>
|
||||
public interface IResponse
|
||||
{
|
||||
///// <summary>
|
||||
///// Http状态码
|
||||
///// </summary>
|
||||
//HttpStatusCode StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 功能执行返回代码
|
||||
/// </summary>
|
||||
int Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息
|
||||
/// </summary>
|
||||
string Message { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
namespace HuanMeng.DotNetCore.Base
|
||||
{
|
||||
/// <summary>
|
||||
/// 响应编码参考,实际的项目使用可以自行定义
|
||||
/// 基本规则:
|
||||
/// 成功:大于等于0
|
||||
/// 失败:小于0
|
||||
/// </summary>
|
||||
public enum ResonseCode
|
||||
{
|
||||
/// <summary>
|
||||
/// Sign签名错误
|
||||
/// </summary>
|
||||
SignError = -999,
|
||||
/// <summary>
|
||||
/// jwt用户签名错误
|
||||
/// </summary>
|
||||
TwtError = -998,
|
||||
|
||||
/// <summary>
|
||||
/// 用户验证失败
|
||||
/// </summary>
|
||||
Unauthorized = 401,
|
||||
/// <summary>
|
||||
/// 重复请求
|
||||
/// </summary>
|
||||
ManyRequests = 429,
|
||||
|
||||
/// <summary>
|
||||
/// 正在处理中
|
||||
/// </summary>
|
||||
Processing = 102,
|
||||
/// <summary>
|
||||
/// 通用错误
|
||||
/// </summary>
|
||||
Error = -1,
|
||||
|
||||
/// <summary>
|
||||
/// 参数错误
|
||||
/// </summary>
|
||||
ParamError = -2,
|
||||
|
||||
/// <summary>
|
||||
/// 没找到数据记录
|
||||
/// </summary>
|
||||
NotFoundRecord = -3,
|
||||
|
||||
/// <summary>
|
||||
/// 成功
|
||||
/// </summary>
|
||||
Success = 0,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using Microsoft.Extensions.Caching.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace XLib.DotNetCore.CacheHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 内存缓存帮助类
|
||||
/// </summary>
|
||||
public class MemoryCacheHelper
|
||||
{
|
||||
public static MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheName"></param>
|
||||
/// <returns></returns>
|
||||
public static T? GetCache<T>(string cacheName) where T : class, new()
|
||||
{
|
||||
|
||||
return cache.TryGetValue(cacheName, out var value) ? value as T : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置缓存
|
||||
/// </summary>
|
||||
/// <param name="cacheName"></param>
|
||||
/// <param name="val"></param>
|
||||
/// <param name="cacheTime">单位秒,默认1小时</param>
|
||||
public static void SetCache(string cacheName, object val, int cacheTime = 21000)
|
||||
{
|
||||
//数据量渐渐大了,每次因为很多都是同时缓存 所以在这里分流一下
|
||||
if (cacheTime == 21000)
|
||||
cacheTime = new Random().Next(21000, 43200);
|
||||
cache.Set(cacheName, val, TimeSpan.FromSeconds(cacheTime));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除缓存
|
||||
/// </summary>
|
||||
/// <param name="cacheName"></param>
|
||||
public static void DelCache(string? cacheName = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(cacheName))
|
||||
cache.Remove(cacheName);
|
||||
else
|
||||
cache.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
||||
namespace HuanMeng.DotNetCore.CustomExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加跨域
|
||||
/// </summary>
|
||||
public static class CorsExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加跨域
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="policyName"></param>
|
||||
public static void AddCustomCors(this IServiceCollection services, string policyName)
|
||||
{
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(policyName,
|
||||
builder =>
|
||||
{
|
||||
builder
|
||||
.AllowAnyOrigin()
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlipayEasySDK.Kernel" Version="1.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Json
|
||||
{
|
||||
public class CustomDateTimeConverter : JsonConverter<DateTime>
|
||||
{
|
||||
private readonly string _format;
|
||||
|
||||
public CustomDateTimeConverter(string format)
|
||||
{
|
||||
_format = format;
|
||||
}
|
||||
|
||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return DateTime.Parse(reader.GetString());
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString(_format));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.JwtInfrastructure.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// jwt帮助类
|
||||
/// </summary>
|
||||
public interface IJwtAuthManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户刷新令牌只读词典
|
||||
/// </summary>
|
||||
IImmutableDictionary<string, JwtRefreshToken> UsersRefreshTokensReadOnlyDictionary { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成令牌
|
||||
/// </summary>
|
||||
/// <param name="username">用户名</param>
|
||||
/// <param name="claims">用户的有关信息</param>
|
||||
/// <param name="now"></param>
|
||||
/// <returns></returns>
|
||||
JwtAuthResult GenerateTokens(string username, Claim[] claims, DateTime now);
|
||||
/// <summary>
|
||||
/// 刷新令牌
|
||||
/// </summary>
|
||||
/// <param name="refreshToken"></param>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="now"></param>
|
||||
/// <returns></returns>
|
||||
JwtAuthResult Refresh(string refreshToken, string accessToken, DateTime now);
|
||||
/// <summary>
|
||||
/// 删除过期的刷新令牌
|
||||
/// </summary>
|
||||
/// <param name="now"></param>
|
||||
void RemoveExpiredRefreshTokens(DateTime now);
|
||||
/// <summary>
|
||||
/// 按用户名删除刷新令牌
|
||||
/// </summary>
|
||||
/// <param name="userName"></param>
|
||||
void RemoveRefreshTokenByUserName(string userName);
|
||||
/// <summary>
|
||||
/// 解码JwtToken
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
(ClaimsPrincipal, JwtSecurityToken?) DecodeJwtToken(string token);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
using HuanMeng.DotNetCore.JwtInfrastructure.Interface;
|
||||
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace HuanMeng.DotNetCore.JwtInfrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// jwt帮助类
|
||||
/// </summary>
|
||||
/// <param name="jwtTokenConfig"></param>
|
||||
public class JwtAuthManager(JwtTokenConfig jwtTokenConfig) : IJwtAuthManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 保存刷新token
|
||||
/// </summary>
|
||||
public IImmutableDictionary<string, JwtRefreshToken> UsersRefreshTokensReadOnlyDictionary => _usersRefreshTokens.ToImmutableDictionary();
|
||||
/// <summary>
|
||||
/// 后面可以存储在数据库或分布式缓存中
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, JwtRefreshToken> _usersRefreshTokens = new();
|
||||
/// <summary>
|
||||
/// 获取加密字段
|
||||
/// </summary>
|
||||
private readonly byte[] _secret = Encoding.UTF8.GetBytes(jwtTokenConfig.Secret);
|
||||
|
||||
/// <summary>
|
||||
/// 删除过期token
|
||||
/// </summary>
|
||||
/// <param name="now"></param>
|
||||
public void RemoveExpiredRefreshTokens(DateTime now)
|
||||
{
|
||||
var expiredTokens = _usersRefreshTokens.Where(x => x.Value.ExpireAt < now).ToList();
|
||||
foreach (var expiredToken in expiredTokens)
|
||||
{
|
||||
_usersRefreshTokens.TryRemove(expiredToken.Key, out _);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据用户名删除token
|
||||
/// </summary>
|
||||
/// <param name="userName"></param>
|
||||
public void RemoveRefreshTokenByUserName(string userName)
|
||||
{
|
||||
var refreshTokens = _usersRefreshTokens.Where(x => x.Value.UserName == userName).ToList();
|
||||
foreach (var refreshToken in refreshTokens)
|
||||
{
|
||||
_usersRefreshTokens.TryRemove(refreshToken.Key, out _);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建token
|
||||
/// </summary>
|
||||
/// <param name="username">用户名</param>
|
||||
/// <param name="claims">用户项</param>
|
||||
/// <param name="now">过期时间</param>
|
||||
/// <returns></returns>
|
||||
public JwtAuthResult GenerateTokens(string username, Claim[] claims, DateTime now)
|
||||
{
|
||||
var shouldAddAudienceClaim = string.IsNullOrWhiteSpace(claims.FirstOrDefault(x => x.Type == JwtRegisteredClaimNames.Aud)?.Value);
|
||||
//创建token
|
||||
var jwtToken = new JwtSecurityToken(
|
||||
jwtTokenConfig.Issuer,
|
||||
shouldAddAudienceClaim ? jwtTokenConfig.Audience : string.Empty,
|
||||
claims,
|
||||
expires: now.AddMinutes(jwtTokenConfig.AccessTokenExpiration),
|
||||
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(_secret), SecurityAlgorithms.HmacSha256Signature));
|
||||
var accessToken = new JwtSecurityTokenHandler().WriteToken(jwtToken);
|
||||
|
||||
//创建刷新token
|
||||
var refreshToken = new JwtRefreshToken
|
||||
{
|
||||
UserName = username,
|
||||
TokenString = GenerateRefreshTokenString(),
|
||||
ExpireAt = now.AddMinutes(jwtTokenConfig.RefreshTokenExpiration)
|
||||
};
|
||||
_usersRefreshTokens.AddOrUpdate(refreshToken.TokenString, refreshToken, (_, _) => refreshToken);
|
||||
|
||||
return new JwtAuthResult
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新token
|
||||
/// </summary>
|
||||
/// <param name="refreshToken"></param>
|
||||
/// <param name="accessToken"></param>
|
||||
/// <param name="now"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SecurityTokenException"></exception>
|
||||
public JwtAuthResult Refresh(string refreshToken, string accessToken, DateTime now)
|
||||
{
|
||||
var (principal, jwtToken) = DecodeJwtToken(accessToken);
|
||||
if (jwtToken == null || !jwtToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256Signature))
|
||||
{
|
||||
throw new SecurityTokenException("无效的token");
|
||||
}
|
||||
|
||||
var userName = principal.Identity?.Name;
|
||||
if (!_usersRefreshTokens.TryGetValue(refreshToken, out var existingRefreshToken))
|
||||
{
|
||||
throw new SecurityTokenException("token已失效");
|
||||
}
|
||||
if (existingRefreshToken.UserName != userName || existingRefreshToken.ExpireAt < now)
|
||||
{
|
||||
throw new SecurityTokenException("token不匹配");
|
||||
}
|
||||
//创建新的token
|
||||
return GenerateTokens(userName, principal.Claims.ToArray(), now);
|
||||
}
|
||||
/// <summary>
|
||||
/// 解析token
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SecurityTokenException"></exception>
|
||||
public (ClaimsPrincipal, JwtSecurityToken?) DecodeJwtToken(string token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
throw new SecurityTokenException("token不能为空");
|
||||
}
|
||||
var principal = new JwtSecurityTokenHandler()
|
||||
.ValidateToken(token,
|
||||
new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = jwtTokenConfig.Issuer,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(_secret),
|
||||
ValidAudience = jwtTokenConfig.Audience,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.FromMinutes(5)
|
||||
},
|
||||
out var validatedToken);
|
||||
return (principal, validatedToken as JwtSecurityToken);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取刷新的token
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static string GenerateRefreshTokenString()
|
||||
{
|
||||
var randomNumber = new byte[32];
|
||||
using var randomNumberGenerator = RandomNumberGenerator.Create();
|
||||
randomNumberGenerator.GetBytes(randomNumber);
|
||||
return Convert.ToBase64String(randomNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.JwtInfrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// 令牌
|
||||
/// </summary>
|
||||
public class JwtAuthResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前token
|
||||
/// </summary>
|
||||
[JsonPropertyName("accessToken")]
|
||||
public string AccessToken { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 刷新token
|
||||
/// </summary>
|
||||
[JsonPropertyName("refreshToken")]
|
||||
public JwtRefreshToken RefreshToken { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.JwtInfrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// 刷新token
|
||||
/// </summary>
|
||||
public class JwtRefreshToken
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
[JsonPropertyName("username")]
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// token
|
||||
/// </summary>
|
||||
[JsonPropertyName("tokenString")]
|
||||
public string TokenString { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 过期时间
|
||||
/// </summary>
|
||||
[JsonPropertyName("expireAt")]
|
||||
public DateTime ExpireAt { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.JwtInfrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// JwtToken 配置文件
|
||||
/// </summary>
|
||||
public class JwtTokenConfig
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 加密值
|
||||
/// </summary>
|
||||
[JsonPropertyName("secret")]
|
||||
public string Secret { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 颁发者
|
||||
/// </summary>
|
||||
[JsonPropertyName("issuer")]
|
||||
public string Issuer { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 受众
|
||||
/// </summary>
|
||||
[JsonPropertyName("audience")]
|
||||
public string Audience { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 令牌过期时间
|
||||
/// </summary>
|
||||
[JsonPropertyName("accessTokenExpiration")]
|
||||
public int AccessTokenExpiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 刷新令牌过期时间(一般会比令牌过期时间长)
|
||||
/// </summary>
|
||||
[JsonPropertyName("refreshTokenExpiration")]
|
||||
public int RefreshTokenExpiration { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
using HuanMeng.DotNetCore.Base;
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
namespace HuanMeng.DotNetCore.MiddlewareExtend
|
||||
{
|
||||
/// <summary>
|
||||
/// 异常中间件
|
||||
/// </summary>
|
||||
public class ExceptionMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<ExceptionMiddleware> _logger;
|
||||
|
||||
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
catch (ArgumentNullException ex)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
|
||||
|
||||
context.Response.StatusCode = StatusCodes.Status200OK;
|
||||
BaseResponse<object> baseResponse = new BaseResponse<object>(ResonseCode.ParamError, ex.ParamName ?? "参数错误", null)
|
||||
{
|
||||
|
||||
};
|
||||
context.Response.ContentType = "application/json; charset=utf-8";
|
||||
// 将异常信息写入 HTTP 响应
|
||||
await context.Response.WriteAsync(JsonConvert.SerializeObject(baseResponse, settings));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
_logger.LogError(context.Request.Path.ToString(), ex, "异常记录");
|
||||
// 设置 HTTP 响应状态码为 500
|
||||
context.Response.ContentType = "application/json; charset=utf-8";
|
||||
context.Response.StatusCode = StatusCodes.Status200OK;
|
||||
BaseResponse<object> baseResponse = new BaseResponse<object>(ResonseCode.Error, ex.Message, null);
|
||||
// 将异常信息写入 HTTP 响应
|
||||
await context.Response.WriteAsync(JsonConvert.SerializeObject(baseResponse, settings));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MiddlewareExtend
|
||||
{
|
||||
/// <summary>
|
||||
/// 方法执行时间
|
||||
/// </summary>
|
||||
public class ExecutionTimeMiddleware(RequestDelegate _next)
|
||||
{
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
// 开始计时
|
||||
var sw = Stopwatch.StartNew();
|
||||
//在将响应标头发送到之前添加要调用的委托客户此处注册的回调按相反顺序运行。
|
||||
context.Response.OnStarting(() =>
|
||||
{
|
||||
sw.Stop();
|
||||
context.Response.Headers.TryAdd("X-Request-Duration", $"{sw.Elapsed.TotalMilliseconds} ms");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
await _next(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MiddlewareExtend
|
||||
{
|
||||
/// <summary>
|
||||
/// 中间库扩展
|
||||
/// </summary>
|
||||
public static class MiddlewareExtends
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载全部中间件
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseMiddlewareAll(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.UseExceptionMiddleware()
|
||||
.UseExecutionTimeMiddleware()
|
||||
.UseSignMiddleware()
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异常中间件
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseExecutionTimeMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ExecutionTimeMiddleware>();
|
||||
}
|
||||
/// <summary>
|
||||
/// 执行时间中间件
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseExceptionMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ExceptionMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加密验证
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <returns></returns>
|
||||
public static IApplicationBuilder UseSignMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<SignMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using HuanMeng.DotNetCore.Base;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MiddlewareExtend
|
||||
{
|
||||
/// <summary>
|
||||
/// 参数请求加密验证
|
||||
/// </summary>
|
||||
public class SignMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private const string FixedString = "cccc"; // 固定字符串
|
||||
public SignMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
// 读取请求体
|
||||
context.Request.EnableBuffering(); // 启用请求流的多次读取功能
|
||||
var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
|
||||
context.Request.Body.Position = 0; // 重置请求体的位置
|
||||
|
||||
if (string.IsNullOrEmpty(requestBody))
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
// 解析请求体为 JSON 对象
|
||||
var requestJson = JObject.Parse(requestBody);
|
||||
// 获取请求中的 sign 值
|
||||
var requestSign = requestJson["sign"]?.ToString();
|
||||
if (string.IsNullOrEmpty(requestSign))
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
// 获取所有的键值对,并排序
|
||||
var sortedKeys = requestJson.Properties()
|
||||
.Where(p => p.Name != "sign")
|
||||
.OrderBy(p => p.Name)
|
||||
.Select(p => p.Value.ToString())
|
||||
.ToList();
|
||||
|
||||
// 拼接所有的值,并加上固定字符串
|
||||
var concatenatedValues = string.Join("", sortedKeys) + FixedString;
|
||||
|
||||
// 计算 MD5 哈希值
|
||||
var md5Hash = ComputeMD5Hash(concatenatedValues);
|
||||
|
||||
|
||||
|
||||
// 验证 MD5 哈希值与请求中的 sign 是否匹配
|
||||
if (md5Hash != requestSign)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
// 返回 500 错误
|
||||
context.Response.StatusCode = 500;
|
||||
BaseResponse<object> baseResponse = new BaseResponse<object>(ResonseCode.SignError, "sign加密验证失败", null)
|
||||
{
|
||||
|
||||
};
|
||||
context.Response.ContentType = "application/json; charset=utf-8";
|
||||
// 将异常信息写入 HTTP 响应
|
||||
await context.Response.WriteAsync(JsonConvert.SerializeObject(baseResponse));
|
||||
//await context.Response.WriteAsync("");
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用下一个中间件
|
||||
await _next(context);
|
||||
}
|
||||
/// <summary>
|
||||
/// Md5加密
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
private string ComputeMD5Hash(string input)
|
||||
{
|
||||
using (var md5 = MD5.Create())
|
||||
{
|
||||
var inputBytes = Encoding.UTF8.GetBytes(input);
|
||||
var hashBytes = md5.ComputeHash(inputBytes);
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MultiTenant.Contract
|
||||
{
|
||||
/// <summary>
|
||||
/// 基本多租户接口
|
||||
/// </summary>
|
||||
public interface IMultiTenant
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户ID
|
||||
/// </summary>
|
||||
Guid TenantId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MultiTenant.Contract
|
||||
{
|
||||
/// <summary>
|
||||
/// 多租户DbContent接口
|
||||
/// </summary>
|
||||
public interface IMultiTenantDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户信息
|
||||
/// </summary>
|
||||
ITenantInfo TenantInfo { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MultiTenant.Contract
|
||||
{
|
||||
/// <summary>
|
||||
/// 多租户实体类接口
|
||||
/// </summary>
|
||||
public interface IMultiTenantEntity : IMultiTenant
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MultiTenant.Contract
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 租户信息接口
|
||||
/// </summary>
|
||||
public interface ITenantInfo : IMultiTenant
|
||||
{
|
||||
|
||||
string? Identifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a display friendly name for the tenant.
|
||||
/// </summary>
|
||||
string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据库连接字符串
|
||||
/// </summary>
|
||||
string? ConnectionString { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
using HuanMeng.DotNetCore.MultiTenant.Contract;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MultiTenant
|
||||
{
|
||||
/// <summary>
|
||||
/// 基本多租户DbContext
|
||||
/// </summary>
|
||||
public class MultiTenantDbContext : DbContext, IMultiTenantDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户信息
|
||||
/// </summary>
|
||||
public ITenantInfo? TenantInfo { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="tenantInfo"></param>
|
||||
public MultiTenantDbContext(ITenantInfo? tenantInfo)
|
||||
{
|
||||
this.TenantInfo = tenantInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="tenantInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
public MultiTenantDbContext(ITenantInfo? tenantInfo, DbContextOptions options)
|
||||
: base(options)
|
||||
{
|
||||
if (tenantInfo == null)
|
||||
{
|
||||
tenantInfo = new TenantInfo()
|
||||
{
|
||||
TenantId = Guid.NewGuid(),
|
||||
Identifier = "default",
|
||||
Name = "default"
|
||||
};
|
||||
}
|
||||
this.TenantInfo = tenantInfo;
|
||||
}
|
||||
|
||||
public void SetTenantInfo(ITenantInfo tenantInfo)
|
||||
{
|
||||
this.TenantInfo = tenantInfo;
|
||||
}
|
||||
|
||||
public override int SaveChanges()
|
||||
{
|
||||
if (TenantInfo?.TenantId != null)
|
||||
{
|
||||
var entries = ChangeTracker.Entries()
|
||||
.Where(e => e.Entity is IMultiTenantEntity &&
|
||||
(e.State == EntityState.Added || e.State == EntityState.Modified))
|
||||
.Select(e => e.Entity as IMultiTenantEntity)
|
||||
.ToList();
|
||||
|
||||
foreach (var entity in entries)
|
||||
{
|
||||
if (entity?.TenantId == null || entity?.TenantId == Guid.Empty)
|
||||
{
|
||||
entity.TenantId = TenantInfo.TenantId;
|
||||
}
|
||||
}
|
||||
}
|
||||
return base.SaveChanges();
|
||||
}
|
||||
|
||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (TenantInfo?.TenantId != null)
|
||||
{
|
||||
var entries = ChangeTracker.Entries()
|
||||
.Where(e => e.Entity is IMultiTenantEntity &&
|
||||
(e.State == EntityState.Added || e.State == EntityState.Modified))
|
||||
.Select(e => e.Entity as IMultiTenantEntity)
|
||||
.ToList();
|
||||
|
||||
foreach (var entity in entries)
|
||||
{
|
||||
if (entity?.TenantId == null || entity?.TenantId == Guid.Empty)
|
||||
{
|
||||
entity.TenantId = TenantInfo.TenantId;
|
||||
}
|
||||
}
|
||||
}
|
||||
return await base.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using HuanMeng.DotNetCore.MultiTenant.Contract;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MultiTenant
|
||||
{
|
||||
/// <summary>
|
||||
/// 基本多租户实体类
|
||||
/// </summary>
|
||||
public class MultiTenantEntity : IMultiTenantEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户ID
|
||||
/// </summary>
|
||||
public virtual Guid TenantId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
using HuanMeng.DotNetCore.MultiTenant.Contract;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.MultiTenant
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户信息
|
||||
/// </summary>
|
||||
public class TenantInfo : ITenantInfo
|
||||
{
|
||||
|
||||
public TenantInfo()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 租户ID
|
||||
/// </summary>
|
||||
public Guid TenantId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户字符串标志
|
||||
/// </summary>
|
||||
public string? Identifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户名称(描述说明)
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户数据库连接字符串
|
||||
/// </summary>
|
||||
public string? ConnectionString { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
|
||||
namespace HuanMeng.DotNetCore.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务处理器
|
||||
/// </summary>
|
||||
public abstract class BaseProcessor : ITaskProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// 终止内部处理线程的最长等待时间(毫秒)
|
||||
/// </summary>
|
||||
protected const int WaitTimeMax_StopProc = 20000;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public BaseProcessor()
|
||||
{
|
||||
//加载配置
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置
|
||||
/// </summary>
|
||||
protected virtual void LoadSettings()
|
||||
{
|
||||
//初始化
|
||||
// LogHelper.Info("BaseProcessor.LoadSettings");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行任务
|
||||
/// </summary>
|
||||
public virtual void Run()
|
||||
{
|
||||
//LogHelper.Info("BaseProcessor.Run");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止执行任务
|
||||
/// </summary>
|
||||
public virtual void Stop()
|
||||
{
|
||||
// LogHelper.Info("BaseProcessor.Stop");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
namespace HuanMeng.DotNetCore.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务处理器接口
|
||||
/// </summary>
|
||||
public interface ITaskProcessor : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行任务
|
||||
/// </summary>
|
||||
void Run();
|
||||
|
||||
/// <summary>
|
||||
/// 停止运行
|
||||
/// </summary>
|
||||
void Stop();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
namespace HuanMeng.DotNetCore.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// 包含处理线程的处理器基类
|
||||
/// </summary>
|
||||
public abstract class ThreadProcessor : BaseProcessor
|
||||
{
|
||||
protected Thread? _thread;
|
||||
protected bool _isRunning;
|
||||
protected string? _threadName;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public ThreadProcessor() { }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="threadName">线程名称</param>
|
||||
public ThreadProcessor(string threadName)
|
||||
{
|
||||
_threadName = threadName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行处理
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">如果处理器已经在运行,抛出异常</exception>
|
||||
public override void Run()
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
throw new InvalidOperationException("重复执行线程");
|
||||
}
|
||||
|
||||
_isRunning = true;
|
||||
|
||||
_thread = new Thread(Proc_Do)
|
||||
{
|
||||
Name = _threadName
|
||||
};
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 线程处理函数,需要在子类中实现
|
||||
/// </summary>
|
||||
protected abstract void Proc_Do();
|
||||
|
||||
/// <summary>
|
||||
/// 停止处理线程
|
||||
/// </summary>
|
||||
public override void Stop()
|
||||
{
|
||||
if (!_isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunning = false;
|
||||
|
||||
if (_thread != null && _thread.IsAlive)
|
||||
{
|
||||
_thread.Join(WaitTimeMax_StopProc); // 等待线程结束
|
||||
_thread = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
Stop();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.TextCensor
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证字符串是否违规
|
||||
/// </summary>
|
||||
//[Obsolete]
|
||||
//public static class CheckTextVerification
|
||||
//{
|
||||
// static HashSet<string> ShieldStr = new HashSet<string>();
|
||||
|
||||
// static List<string> ShieldListStr = new List<string>();
|
||||
// static string[] ShieldString = new string[0];
|
||||
|
||||
|
||||
|
||||
// static CheckTextVerification()
|
||||
// {
|
||||
// var ckPath = Path.GetFullPath("I:\\test\\PrayForBlessings\\PrayForBlessings\\ciku/");
|
||||
// var filePath = Directory.EnumerateFiles(ckPath);
|
||||
// foreach (var item in filePath)
|
||||
// {
|
||||
// CheckTextVerification.AddShieldString(item);
|
||||
// }
|
||||
// //
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// ///
|
||||
// /// </summary>
|
||||
// /// <param name="path"></param>
|
||||
// /// <exception cref="Exception"></exception>
|
||||
// public static void AddShieldString(string path)
|
||||
// {
|
||||
|
||||
// if (!File.Exists(path))
|
||||
// {
|
||||
// throw new Exception("文件不存在");
|
||||
// }
|
||||
// HashSet<string> strings = new HashSet<string>();
|
||||
|
||||
// using (StreamReader reader = new StreamReader(path, UnicodeEncoding.UTF8))
|
||||
// {
|
||||
|
||||
// while (reader.Peek() > 0)
|
||||
// {
|
||||
// var tempStr = (reader.ReadLine() ?? "");
|
||||
// //string result = Regex.Replace(tempStr, pattern, "");
|
||||
// if (!string.IsNullOrEmpty(tempStr))
|
||||
// {
|
||||
// var tempStringToLower = tempStr.ToLower().Split(new char[] { '、', ',' });
|
||||
// string[] filteredArrayToLower = tempStringToLower.Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||
// strings.UnionWith(filteredArrayToLower);
|
||||
// }
|
||||
// }
|
||||
// ShieldStr.UnionWith(strings);
|
||||
// ShieldListStr.AddRange(ShieldStr);
|
||||
// ShieldString = ShieldStr.ToArray();
|
||||
// Console.WriteLine(string.Format($"[{Path.GetFileName(path)}]加载完毕,总共加载屏蔽字行数:{ShieldStr.Count}"));
|
||||
// }
|
||||
// }
|
||||
// /// <summary>
|
||||
// ///
|
||||
// /// </summary>
|
||||
// /// <param name="sourceTxt"></param>
|
||||
// /// <returns></returns>
|
||||
// public static bool VerifyTxt(string sourceTxt)
|
||||
// {
|
||||
// if (!string.IsNullOrEmpty(sourceTxt))
|
||||
// {
|
||||
// //先去掉空格,特殊字符
|
||||
// string cleanedText = CleanedTextString(sourceTxt);
|
||||
// foreach (var item in ShieldStr)
|
||||
// {
|
||||
// if (cleanedText.Contains(item))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
// /// <summary>
|
||||
// ///
|
||||
// /// </summary>
|
||||
// /// <param name="sourceTxt"></param>
|
||||
// /// <returns></returns>
|
||||
// public static bool VerifyTxtList(string sourceTxt)
|
||||
// {
|
||||
// if (!string.IsNullOrEmpty(sourceTxt))
|
||||
// {
|
||||
// //先去掉空格,特殊字符
|
||||
// string cleanedText = CleanedTextString(sourceTxt);
|
||||
// foreach (var item in ShieldListStr)
|
||||
// {
|
||||
// if (cleanedText.Contains(item))
|
||||
// {
|
||||
// return false;//2.56
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
// private static string replaceString = " ,.。,@*12345690_-";
|
||||
|
||||
// /// <summary>
|
||||
// ///
|
||||
// /// </summary>
|
||||
// /// <param name="sourceTxt"></param>
|
||||
// /// <returns></returns>
|
||||
// private static string CleanedTextString(string sourceTxt)
|
||||
// {
|
||||
// string cleanedText = sourceTxt.ToLower()
|
||||
// .Replace(" ", "")
|
||||
// .Replace(",", "")
|
||||
// .Replace(".", "")
|
||||
// .Replace("。", "")
|
||||
// .Replace(",", "")
|
||||
// .Replace("@", "")
|
||||
// .Replace("-", "")
|
||||
// .Replace("*", "")
|
||||
// .Replace("1", "")
|
||||
// .Replace("2", "")
|
||||
// .Replace("3", "")
|
||||
// .Replace("4", "")
|
||||
// .Replace("5", "")
|
||||
// .Replace("6", "")
|
||||
// .Replace("9", "")
|
||||
// .Replace("0", "")
|
||||
// .Replace("_", "");
|
||||
// return cleanedText;
|
||||
// }
|
||||
|
||||
// public static bool VerifyTxtString(string sourceTxt)
|
||||
// {
|
||||
// if (!string.IsNullOrEmpty(sourceTxt))
|
||||
// {
|
||||
// //先去掉空格,特殊字符
|
||||
// string cleanedText = CleanedTextString(sourceTxt);
|
||||
|
||||
// foreach (var item in ShieldString)
|
||||
// {
|
||||
// if (cleanedText.Contains(item))
|
||||
// {
|
||||
// return false;//2.56
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.TextCensor
|
||||
{
|
||||
/// <summary>
|
||||
/// 文本审核接口
|
||||
/// </summary>
|
||||
public interface ITextCensor
|
||||
{
|
||||
/// <summary>
|
||||
/// 文本审核
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
bool TextCensor(string text);
|
||||
|
||||
/// <summary>
|
||||
/// 文本审核
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> TextCensorAsync(string text);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.TextCensor.SensitiveWord
|
||||
{
|
||||
public class SensitiveWordFilter : ITextCensor
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义Trie树节点
|
||||
/// </summary>
|
||||
private class TrieNode
|
||||
{
|
||||
/// <summary>
|
||||
/// 标记是否是一个敏感词的结尾
|
||||
/// </summary>
|
||||
public bool IsEnd { get; set; }
|
||||
/// <summary>
|
||||
/// 存储子节点
|
||||
/// </summary>
|
||||
public Dictionary<char, TrieNode> Children { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
//public FrozenDictionary<char, TrieNode> fChildren { get; set; }
|
||||
public TrieNode()
|
||||
{
|
||||
IsEnd = false;
|
||||
Children = new Dictionary<char, TrieNode>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根节点
|
||||
/// </summary>
|
||||
private TrieNode Root { get; set; }
|
||||
public SensitiveWordFilter()
|
||||
{
|
||||
Root = new TrieNode();
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加敏感词到Trie树中
|
||||
/// </summary>
|
||||
/// <param name="word"></param>
|
||||
public void AddSensitiveWord(string word)
|
||||
{
|
||||
TrieNode currentNode = Root;
|
||||
word = CleanText(word);
|
||||
foreach (char c in word.ToLower())
|
||||
{
|
||||
// 如果当前字符不存在于子节点中,则添加
|
||||
if (!currentNode.Children.ContainsKey(c))
|
||||
{
|
||||
currentNode.Children[c] = new TrieNode();
|
||||
}
|
||||
currentNode = currentNode.Children[c];
|
||||
}
|
||||
currentNode.IsEnd = true; // 标记当前节点为敏感词结尾
|
||||
}
|
||||
/// <summary>
|
||||
/// 定义要移除的字符集合,包括空格
|
||||
/// </summary>
|
||||
private static string replaceString = @"[ \.,。,@\-*12345690_]";
|
||||
/// <summary>
|
||||
/// 清理文字
|
||||
/// </summary>
|
||||
/// <param name="sourceTxt"></param>
|
||||
/// <returns></returns>
|
||||
public string CleanText(string sourceTxt)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sourceTxt))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// 使用正则表达式替换所有匹配的字符
|
||||
//string cleanedText = Regex.Replace(sourceTxt.ToLower(), replaceString, string.Empty);
|
||||
string cleanedText = sourceTxt
|
||||
.Replace(',', ' ')
|
||||
.Replace('.', ' ')
|
||||
.Replace('。', ' ')
|
||||
.Replace(',', ' ')
|
||||
.Replace('@', ' ')
|
||||
.Replace('-', ' ')
|
||||
.Replace('*', ' ')
|
||||
.Replace("1", string.Empty)
|
||||
.Replace("2", string.Empty)
|
||||
.Replace("3", string.Empty)
|
||||
.Replace("4", string.Empty)
|
||||
.Replace("5", string.Empty)
|
||||
.Replace("6", string.Empty)
|
||||
.Replace("9", string.Empty)
|
||||
.Replace("0", string.Empty)
|
||||
.Replace("_", string.Empty)
|
||||
.Replace(" ", string.Empty).ToLower();
|
||||
return cleanedText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断文本中是否包含敏感词
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public bool ContainsSensitiveWord(string text)
|
||||
{
|
||||
//过滤字符串
|
||||
text = CleanText(text);
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
TrieNode currentNode = Root;
|
||||
int j = i;
|
||||
// 从当前位置开始匹配敏感词
|
||||
while (j < text.Length && currentNode.Children.ContainsKey(text[j]))
|
||||
{
|
||||
currentNode = currentNode.Children[text[j]];
|
||||
// 如果当前节点是敏感词结尾,返回true
|
||||
if (currentNode.IsEnd)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TextCensor(string text)
|
||||
{
|
||||
return ContainsSensitiveWord(text);
|
||||
}
|
||||
|
||||
public Task<bool> TextCensorAsync(string text)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
return ContainsSensitiveWord(text);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.TextCensor.SensitiveWord
|
||||
{
|
||||
public class SensitiveWordFilterFrozen : ITextCensor
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义Trie树节点
|
||||
/// </summary>
|
||||
private class TrieNode
|
||||
{
|
||||
/// <summary>
|
||||
/// 标记是否是一个敏感词的结尾
|
||||
/// </summary>
|
||||
public bool IsEnd { get; set; }
|
||||
/// <summary>
|
||||
/// 存储子节点
|
||||
/// </summary>
|
||||
public FrozenDictionary<char, TrieNode> Children { get; private set; }
|
||||
|
||||
public TrieNode()
|
||||
{
|
||||
IsEnd = false;
|
||||
Children = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将子节点字典冻结为 FrozenDictionary
|
||||
/// </summary>
|
||||
public void FreezeChildren(Dictionary<char, TrieNode> children)
|
||||
{
|
||||
Children = children.ToFrozenDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根节点
|
||||
/// </summary>
|
||||
private TrieNode Root { get; set; }
|
||||
|
||||
public SensitiveWordFilterFrozen()
|
||||
{
|
||||
Root = new TrieNode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加敏感词到Trie树中
|
||||
/// </summary>
|
||||
/// <param name="word"></param>
|
||||
public void AddSensitiveWord(string word)
|
||||
{
|
||||
TrieNode currentNode = Root;
|
||||
word = CleanText(word);
|
||||
foreach (char c in word.ToLower())
|
||||
{
|
||||
// 如果当前字符不存在于子节点中,则添加
|
||||
if (currentNode.Children == null || !currentNode.Children.ContainsKey(c))
|
||||
{
|
||||
var children = currentNode.Children?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) ?? new Dictionary<char, TrieNode>();
|
||||
children[c] = new TrieNode();
|
||||
currentNode.FreezeChildren(children);
|
||||
}
|
||||
currentNode = currentNode.Children[c];
|
||||
}
|
||||
currentNode.IsEnd = true; // 标记当前节点为敏感词结尾
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理文字
|
||||
/// </summary>
|
||||
/// <param name="sourceTxt"></param>
|
||||
/// <returns></returns>
|
||||
public string CleanText(string sourceTxt)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sourceTxt))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string cleanedText = sourceTxt
|
||||
.Replace(',', ' ')
|
||||
.Replace('.', ' ')
|
||||
.Replace('。', ' ')
|
||||
.Replace(',', ' ')
|
||||
.Replace('@', ' ')
|
||||
.Replace('-', ' ')
|
||||
.Replace('*', ' ')
|
||||
.Replace("1", string.Empty)
|
||||
.Replace("2", string.Empty)
|
||||
.Replace("3", string.Empty)
|
||||
.Replace("4", string.Empty)
|
||||
.Replace("5", string.Empty)
|
||||
.Replace("6", string.Empty)
|
||||
.Replace("9", string.Empty)
|
||||
.Replace("0", string.Empty)
|
||||
.Replace("_", string.Empty)
|
||||
.Replace(" ", string.Empty).ToLower();
|
||||
return cleanedText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断文本中是否包含敏感词
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public bool ContainsSensitiveWord(string text)
|
||||
{
|
||||
//过滤字符串
|
||||
text = CleanText(text);
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
TrieNode currentNode = Root;
|
||||
int j = i;
|
||||
// 从当前位置开始匹配敏感词
|
||||
while (j < text.Length && currentNode.Children != null && currentNode.Children.ContainsKey(text[j]))
|
||||
{
|
||||
currentNode = currentNode.Children[text[j]];
|
||||
// 如果当前节点是敏感词结尾,返回true
|
||||
if (currentNode.IsEnd)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TextCensor(string text)
|
||||
{
|
||||
return ContainsSensitiveWord(text);
|
||||
}
|
||||
|
||||
public Task<bool> TextCensorAsync(string text)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
return ContainsSensitiveWord(text);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
using HuanMeng.DotNetCore.TextCensor.SensitiveWord;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace HuanMeng.DotNetCore.TextCensor
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class TextCensorExtend
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 获取文本检测
|
||||
/// </summary>
|
||||
/// <param name="_dirPath"></param>
|
||||
/// <returns></returns>
|
||||
public static ITextCensor GetITextCensor(string _dirPath)
|
||||
{
|
||||
|
||||
return GetSensitiveWordFilterFrozen(_dirPath)
|
||||
//GetSensitiveWordFilter(_dirPath)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="_dirPath"></param>
|
||||
/// <returns></returns>
|
||||
public static SensitiveWordFilter GetSensitiveWordFilter(string _dirPath)
|
||||
{
|
||||
SensitiveWordFilter sensitiveWordFilter = new SensitiveWordFilter();
|
||||
var ckPath = Path.GetFullPath(_dirPath);
|
||||
var filePath = Directory.EnumerateFiles(ckPath);
|
||||
foreach (var item in filePath)
|
||||
{
|
||||
AddShieldString(item, sensitiveWordFilter);
|
||||
}
|
||||
return sensitiveWordFilter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="_dirPath"></param>
|
||||
/// <returns></returns>
|
||||
public static SensitiveWordFilterFrozen GetSensitiveWordFilterFrozen(string _dirPath)
|
||||
{
|
||||
SensitiveWordFilterFrozen sensitiveWordFilter = new SensitiveWordFilterFrozen();
|
||||
var ckPath = Path.GetFullPath(_dirPath);
|
||||
var filePath = Directory.EnumerateFiles(ckPath);
|
||||
foreach (var item in filePath)
|
||||
{
|
||||
AddShieldString(item, sensitiveWordFilter);
|
||||
}
|
||||
return sensitiveWordFilter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static void AddShieldString(string path, SensitiveWordFilterFrozen sensitiveWordFilter)
|
||||
{
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new Exception("文件不存在");
|
||||
}
|
||||
using (StreamReader reader = new StreamReader(path, UnicodeEncoding.UTF8))
|
||||
{
|
||||
|
||||
while (reader.Peek() > 0)
|
||||
{
|
||||
|
||||
var tempStr = (reader.ReadLine() ?? "");
|
||||
if (!string.IsNullOrEmpty(tempStr))
|
||||
{
|
||||
sensitiveWordFilter.AddSensitiveWord(tempStr);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static void AddShieldString(string path, SensitiveWordFilter sensitiveWordFilter)
|
||||
{
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new Exception("文件不存在");
|
||||
}
|
||||
using (StreamReader reader = new StreamReader(path, UnicodeEncoding.UTF8))
|
||||
{
|
||||
|
||||
while (reader.Peek() > 0)
|
||||
{
|
||||
|
||||
var tempStr = (reader.ReadLine() ?? "");
|
||||
if (!string.IsNullOrEmpty(tempStr))
|
||||
{
|
||||
sensitiveWordFilter.AddSensitiveWord(tempStr);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Utility.AssemblyHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 保存各种程序集信息属性的类。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AssemblyInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置程序集的版本。
|
||||
/// </summary>
|
||||
public string Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置程序集的文件版本。
|
||||
/// </summary>
|
||||
public string FileVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置程序集版本。
|
||||
/// </summary>
|
||||
public string AssemblyVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置程序集的信息性版本。
|
||||
/// </summary>
|
||||
public string InformationalVersion { get; set; }
|
||||
|
||||
///// <summary>
|
||||
///// 获取或设置与程序集关联的公司名称。
|
||||
///// </summary>
|
||||
//public string Company { get; set; }
|
||||
|
||||
///// <summary>
|
||||
///// 获取或设置与程序集关联的产品名称。
|
||||
///// </summary>
|
||||
//public string Product { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置与程序集关联的版权信息。
|
||||
/// </summary>
|
||||
public string Copyright { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置程序集的描述信息。
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Utility.AssemblyHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于检索程序集信息的辅助类。
|
||||
/// </summary>
|
||||
public static class AssemblyInfoHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 从正在执行的程序集检索各种属性,并返回一个 AssemblyInfo 对象。
|
||||
/// </summary>
|
||||
/// <returns>包含程序集属性的 AssemblyInfo 对象。</returns>
|
||||
public static AssemblyInfo GetAssemblyInfo()
|
||||
{
|
||||
// 获取正在执行的程序集
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// 创建并填充 AssemblyInfo 对象的相关属性
|
||||
var assemblyInfo = new AssemblyInfo
|
||||
{
|
||||
Version = assembly.GetName().Version.ToString(),
|
||||
FileVersion = assembly.GetCustomAttributes<AssemblyFileVersionAttribute>().FirstOrDefault()?.Version ?? "",
|
||||
AssemblyVersion = assembly.GetCustomAttributes<AssemblyVersionAttribute>().FirstOrDefault()?.Version ?? "",
|
||||
InformationalVersion = assembly.GetCustomAttributes<AssemblyInformationalVersionAttribute>().FirstOrDefault()?.InformationalVersion ?? "",
|
||||
//Company = assembly.GetCustomAttributes<AssemblyCompanyAttribute>().FirstOrDefault()?.Company ?? "",
|
||||
//Product = assembly.GetCustomAttributes<AssemblyProductAttribute>().FirstOrDefault()?.Product ?? "",
|
||||
Copyright = assembly.GetCustomAttributes<AssemblyCopyrightAttribute>().FirstOrDefault()?.Copyright ?? "",
|
||||
Description = assembly.GetCustomAttributes<AssemblyDescriptionAttribute>().FirstOrDefault()?.Description ?? ""
|
||||
};
|
||||
|
||||
return assemblyInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// 时间扩展
|
||||
/// </summary>
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取时间戳,秒
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
public static long ToUnixTimestamp(this DateTime dateTime)
|
||||
{
|
||||
return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取是时间戳,毫秒
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
public static long ToUnixTimestampMilliseconds(this DateTime dateTime)
|
||||
{
|
||||
return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Utility
|
||||
{
|
||||
public static class HttpContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取IP地址
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetClientIpAddress(this HttpContext context)
|
||||
{
|
||||
// 尝试从X-Forwarded-For头部中获取IP地址
|
||||
var forwardedFor = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(forwardedFor))
|
||||
{
|
||||
// 处理可能的多个IP地址,通常第一个是客户端的真实IP
|
||||
var ipAddresses = forwardedFor.Split(',');
|
||||
if (ipAddresses.Length > 0)
|
||||
{
|
||||
return ipAddresses[0].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果X-Forwarded-For头部不存在,使用RemoteIpAddress
|
||||
return context.Connection.RemoteIpAddress?.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Utility
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class MD5Encryption
|
||||
{
|
||||
/// <summary>
|
||||
/// md5加密
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static string ComputeMD5Hash(string input)
|
||||
{
|
||||
// 创建一个 MD5 哈希算法的实例
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
// 将输入字符串转换为字节数组
|
||||
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
|
||||
|
||||
// 计算 MD5 哈希值
|
||||
byte[] hashBytes = md5.ComputeHash(inputBytes);
|
||||
|
||||
// 将字节数组转换为十六进制字符串
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < hashBytes.Length; i++)
|
||||
{
|
||||
sb.Append(hashBytes[i].ToString("x2"));
|
||||
}
|
||||
|
||||
// 返回哈希值字符串
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace HuanMeng.DotNetCore.Utility;
|
||||
/// <summary>
|
||||
/// 手机号
|
||||
/// </summary>
|
||||
public class PhoneNumberValidator
|
||||
{
|
||||
// 正则表达式用于匹配手机号码。可以根据需要调整以适应不同的国家或地区。
|
||||
private static readonly Regex phoneNumberRegex = new Regex(@"^(1[3-9]\d{9})$");
|
||||
|
||||
public static bool IsPhoneNumber(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return phoneNumberRegex.IsMatch(input);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
using StackExchange.Redis;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HuanMeng.DotNetCore.WeChat
|
||||
{
|
||||
/// <summary>
|
||||
/// 小程序帮助类
|
||||
/// </summary>
|
||||
/// <param name="appId"></param>
|
||||
/// <param name="secret"></param>
|
||||
/// <param name="database"></param>
|
||||
/// <param name="_httpClientFactory"></param>
|
||||
public class MiniProgram(string appId, string secret, IDatabase database, IHttpClientFactory? _httpClientFactory)
|
||||
{
|
||||
public string AppId { get; set; } = appId;
|
||||
string key = $"WeChat:{appId}";
|
||||
// Method to get user's OpenID
|
||||
public async Task<(string openId, string unionid, string session_key)> GetOpenid(string code)
|
||||
{
|
||||
string url = $"https://api.weixin.qq.com/sns/jscode2session?appid={appId}&secret={secret}&js_code={code}&grant_type=authorization_code";
|
||||
var resUserInfo = await GetCurlData(url);
|
||||
|
||||
if (resUserInfo.ContainsKey("errcode"))
|
||||
{
|
||||
return (null, null, null);
|
||||
}
|
||||
|
||||
string openid = resUserInfo.GetValueOrDefault("openid")?.ToString() ?? "";
|
||||
string unionid = resUserInfo.GetValueOrDefault("unionid")?.ToString() ?? "";
|
||||
string session_key = resUserInfo.GetValueOrDefault("session_key")?.ToString() ?? "";
|
||||
|
||||
return new(openid, unionid, session_key);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Get user info based on access token and openid
|
||||
public async Task<dynamic> GetUserInfoAsync(string openid)
|
||||
{
|
||||
var accessToken = await GetAccessToken();
|
||||
string url = $"https://api.weixin.qq.com/sns/userinfo?access_token={accessToken}&openid={openid}&lang=zh_CN";
|
||||
var response = await GetCurlData(url);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<string> GetAccessToken()
|
||||
{
|
||||
var accessTokenInfo = GetConfig();
|
||||
|
||||
if (accessTokenInfo != null)
|
||||
{
|
||||
return accessTokenInfo?.Access_token;
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={secret}";
|
||||
var resAccessToken = await GetCurlData(url);
|
||||
if (resAccessToken.ContainsKey("errcode"))
|
||||
{
|
||||
throw new Exception("获取微信token失败");
|
||||
}
|
||||
|
||||
string accessToken = resAccessToken["access_token"].ToString();
|
||||
int expiresIn = Convert.ToInt32(resAccessToken["expires_in"]);
|
||||
long accessTokenTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + expiresIn;
|
||||
|
||||
var data = new AccessToken(accessToken, accessTokenTime);
|
||||
SetConfig(data);
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method for GET requests
|
||||
private async Task<Dictionary<string, object>> GetCurlData(string url)
|
||||
{
|
||||
using HttpClient client = _httpClientFactory.CreateClient();
|
||||
var response = await client.GetStringAsync(url);
|
||||
return JsonConvert.DeserializeObject<Dictionary<string, object>>(response);
|
||||
}
|
||||
|
||||
// Helper method for POST requests
|
||||
private async Task<Dictionary<string, object>> PostCurlData(string url, object data)
|
||||
{
|
||||
using HttpClient client = _httpClientFactory.CreateClient();
|
||||
var json = JsonConvert.SerializeObject(data);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
var response = await client.PostAsync(url, content);
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<Dictionary<string, object>>(result);
|
||||
}
|
||||
|
||||
// Helper method for HTTP POST data
|
||||
private async Task<string> HttpPostData(string url, object data)
|
||||
{
|
||||
using HttpClient client = _httpClientFactory.CreateClient();
|
||||
var json = JsonConvert.SerializeObject(data);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
var response = await client.PostAsync(url, content);
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
private AccessToken? GetConfig()
|
||||
{
|
||||
|
||||
var json = database.StringGet(key);
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
{
|
||||
return JsonConvert.DeserializeObject<AccessToken>(json);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetConfig(AccessToken access)
|
||||
{
|
||||
var outTime = new TimeSpan(0, 0, 7000);
|
||||
database.StringSet(key, JsonConvert.SerializeObject(access), outTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class AccessToken
|
||||
{
|
||||
public string? Access_token { get; }
|
||||
public long Access_token_time { get; }
|
||||
|
||||
public AccessToken(string? access_token, long access_token_time)
|
||||
{
|
||||
Access_token = access_token;
|
||||
Access_token_time = access_token_time;
|
||||
}
|
||||
public AccessToken()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HuanMeng.DotNetCore.WeChat
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class WXBizDataCrypt
|
||||
{
|
||||
private string appId;
|
||||
private string sessionKey;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="appId">小程序的 appId</param>
|
||||
/// <param name="sessionKey">用户在小程序登录后获取的会话密钥</param>
|
||||
public WXBizDataCrypt(string appId, string sessionKey)
|
||||
{
|
||||
this.appId = appId;
|
||||
this.sessionKey = sessionKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检验数据的真实性,并且获取解密后的明文
|
||||
/// </summary>
|
||||
/// <param name="encryptedData">加密的用户数据</param>
|
||||
/// <param name="iv">与用户数据一同返回的初始向量</param>
|
||||
/// <param name="data">解密后的原文</param>
|
||||
/// <returns>成功0,失败返回对应的错误码</returns>
|
||||
public int DecryptData(string encryptedData, string iv, out string data)
|
||||
{
|
||||
data = null;
|
||||
|
||||
// 检查 sessionKey 长度
|
||||
if (sessionKey.Length != 24)
|
||||
{
|
||||
return ErrorCode.IllegalAesKey;
|
||||
}
|
||||
|
||||
// 检查 iv 长度
|
||||
if (iv.Length != 24)
|
||||
{
|
||||
return ErrorCode.IllegalIv;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
byte[] aesKey = Convert.FromBase64String(sessionKey);
|
||||
byte[] aesIV = Convert.FromBase64String(iv);
|
||||
byte[] aesCipher = Convert.FromBase64String(encryptedData);
|
||||
|
||||
using (Aes aesAlg = Aes.Create())
|
||||
{
|
||||
aesAlg.Key = aesKey;
|
||||
aesAlg.IV = aesIV;
|
||||
aesAlg.Mode = CipherMode.CBC;
|
||||
aesAlg.Padding = PaddingMode.PKCS7;
|
||||
|
||||
using (ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV))
|
||||
{
|
||||
using (MemoryStream msDecrypt = new MemoryStream(aesCipher))
|
||||
{
|
||||
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
|
||||
{
|
||||
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
|
||||
{
|
||||
// 解密后的明文
|
||||
data = srDecrypt.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JObject dataObj = JObject.Parse(data);
|
||||
|
||||
// 检查 appId
|
||||
if (dataObj["watermark"]["appid"].ToString() != this.appId)
|
||||
{
|
||||
return ErrorCode.IllegalBuffer;
|
||||
}
|
||||
|
||||
return ErrorCode.OK;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return ErrorCode.IllegalBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ErrorCode
|
||||
{
|
||||
public const int OK = 0;
|
||||
public const int IllegalAesKey = -41001;
|
||||
public const int IllegalIv = -41002;
|
||||
public const int IllegalBuffer = -41003;
|
||||
}
|
||||
|
||||
public class WXBizDataCryptModel
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string EncryptedData;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Iv;
|
||||
|
||||
|
||||
public int UserId { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
20
src/CloudGaming/CloudGaming.Model/CloudGaming.Model.csproj
Normal file
20
src/CloudGaming/CloudGaming.Model/CloudGaming.Model.csproj
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="DbSqlServer\Db_User\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,355 @@
|
|||
<#@ template hostSpecific="true" #>
|
||||
<#@ assembly name="Microsoft.EntityFrameworkCore" #>
|
||||
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
|
||||
<#@ assembly name="Microsoft.EntityFrameworkCore.Relational" #>
|
||||
<#@ assembly name="Microsoft.Extensions.DependencyInjection.Abstractions" #>
|
||||
<#@ parameter name="Model" type="Microsoft.EntityFrameworkCore.Metadata.IModel" #>
|
||||
<#@ parameter name="Options" type="Microsoft.EntityFrameworkCore.Scaffolding.ModelCodeGenerationOptions" #>
|
||||
<#@ parameter name="NamespaceHint" type="System.String" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
|
||||
<#@ import namespace="Microsoft.EntityFrameworkCore.Design" #>
|
||||
<#@ import namespace="Microsoft.EntityFrameworkCore.Infrastructure" #>
|
||||
<#@ import namespace="Microsoft.EntityFrameworkCore.Scaffolding" #>
|
||||
<#@ import namespace="Microsoft.Extensions.DependencyInjection" #>
|
||||
<#
|
||||
if (!ProductInfo.GetVersion().StartsWith("8.0"))
|
||||
{
|
||||
Warning("Your templates were created using an older version of Entity Framework. Additional features and bug fixes may be available. See https://aka.ms/efcore-docs-updating-templates for more information.");
|
||||
}
|
||||
|
||||
var services = (IServiceProvider)Host;
|
||||
var providerCode = services.GetRequiredService<IProviderConfigurationCodeGenerator>();
|
||||
var annotationCodeGenerator = services.GetRequiredService<IAnnotationCodeGenerator>();
|
||||
var code = services.GetRequiredService<ICSharpHelper>();
|
||||
|
||||
var usings = new List<string>
|
||||
{
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"Microsoft.EntityFrameworkCore"
|
||||
};
|
||||
|
||||
if (NamespaceHint != Options.ModelNamespace
|
||||
&& !string.IsNullOrEmpty(Options.ModelNamespace))
|
||||
{
|
||||
usings.Add(Options.ModelNamespace);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(NamespaceHint))
|
||||
{
|
||||
#>
|
||||
namespace <#= NamespaceHint #>;
|
||||
|
||||
<#
|
||||
}
|
||||
#>
|
||||
public partial class <#= Options.ContextName #> : DbContext
|
||||
{
|
||||
<#
|
||||
if (!Options.SuppressOnConfiguring)
|
||||
{
|
||||
#>
|
||||
public <#= Options.ContextName #>()
|
||||
{
|
||||
}
|
||||
|
||||
<#
|
||||
}
|
||||
#>
|
||||
public <#= Options.ContextName #>(DbContextOptions<<#= Options.ContextName #>> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
<#
|
||||
foreach (var entityType in Model.GetEntityTypes().Where(e => !e.IsSimpleManyToManyJoinEntityType()))
|
||||
{
|
||||
#>
|
||||
public virtual DbSet<<#= entityType.Name #>> <#= entityType.GetDbSetName() #> { get; set; }
|
||||
|
||||
<#
|
||||
}
|
||||
|
||||
if (!Options.SuppressOnConfiguring)
|
||||
{
|
||||
#>
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
<#
|
||||
if (!Options.SuppressConnectionStringWarning)
|
||||
{
|
||||
#>
|
||||
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
|
||||
<#
|
||||
}
|
||||
|
||||
var useProviderCall = providerCode.GenerateUseProvider(Options.ConnectionString);
|
||||
usings.AddRange(useProviderCall.GetRequiredUsings());
|
||||
#>
|
||||
=> optionsBuilder<#= code.Fragment(useProviderCall, indent: 3) #>;
|
||||
|
||||
<#
|
||||
}
|
||||
|
||||
#>
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
<#
|
||||
var anyConfiguration = false;
|
||||
|
||||
var modelFluentApiCalls = Model.GetFluentApiCalls(annotationCodeGenerator);
|
||||
if (modelFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(modelFluentApiCalls.GetRequiredUsings());
|
||||
#>
|
||||
modelBuilder<#= code.Fragment(modelFluentApiCalls, indent: 3) #>;
|
||||
<#
|
||||
anyConfiguration = true;
|
||||
}
|
||||
|
||||
StringBuilder mainEnvironment;
|
||||
foreach (var entityType in Model.GetEntityTypes().Where(e => !e.IsSimpleManyToManyJoinEntityType()))
|
||||
{
|
||||
// Save all previously generated code, and start generating into a new temporary environment
|
||||
mainEnvironment = GenerationEnvironment;
|
||||
GenerationEnvironment = new StringBuilder();
|
||||
|
||||
if (anyConfiguration)
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
|
||||
var anyEntityTypeConfiguration = false;
|
||||
#>
|
||||
modelBuilder.Entity<<#= entityType.Name #>>(entity =>
|
||||
{
|
||||
<#
|
||||
var key = entityType.FindPrimaryKey();
|
||||
if (key != null)
|
||||
{
|
||||
var keyFluentApiCalls = key.GetFluentApiCalls(annotationCodeGenerator);
|
||||
if (keyFluentApiCalls != null
|
||||
|| (!key.IsHandledByConvention() && !Options.UseDataAnnotations))
|
||||
{
|
||||
if (keyFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(keyFluentApiCalls.GetRequiredUsings());
|
||||
}
|
||||
#>
|
||||
entity.HasKey(<#= code.Lambda(key.Properties, "e") #>)<#= code.Fragment(keyFluentApiCalls, indent: 4) #>;
|
||||
<#
|
||||
anyEntityTypeConfiguration = true;
|
||||
}
|
||||
}
|
||||
|
||||
var entityTypeFluentApiCalls = entityType.GetFluentApiCalls(annotationCodeGenerator)
|
||||
?.FilterChain(c => !(Options.UseDataAnnotations && c.IsHandledByDataAnnotations));
|
||||
if (entityTypeFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(entityTypeFluentApiCalls.GetRequiredUsings());
|
||||
|
||||
if (anyEntityTypeConfiguration)
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
#>
|
||||
entity<#= code.Fragment(entityTypeFluentApiCalls, indent: 4) #>;
|
||||
<#
|
||||
anyEntityTypeConfiguration = true;
|
||||
}
|
||||
|
||||
foreach (var index in entityType.GetIndexes()
|
||||
.Where(i => !(Options.UseDataAnnotations && i.IsHandledByDataAnnotations(annotationCodeGenerator))))
|
||||
{
|
||||
if (anyEntityTypeConfiguration)
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
|
||||
var indexFluentApiCalls = index.GetFluentApiCalls(annotationCodeGenerator);
|
||||
if (indexFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(indexFluentApiCalls.GetRequiredUsings());
|
||||
}
|
||||
#>
|
||||
entity.HasIndex(<#= code.Lambda(index.Properties, "e") #>, <#= code.Literal(index.GetDatabaseName()) #>)<#= code.Fragment(indexFluentApiCalls, indent: 4) #>;
|
||||
<#
|
||||
anyEntityTypeConfiguration = true;
|
||||
}
|
||||
|
||||
var firstProperty = true;
|
||||
foreach (var property in entityType.GetProperties())
|
||||
{
|
||||
var propertyFluentApiCalls = property.GetFluentApiCalls(annotationCodeGenerator)
|
||||
?.FilterChain(c => !(Options.UseDataAnnotations && c.IsHandledByDataAnnotations)
|
||||
&& !(c.Method == "IsRequired" && Options.UseNullableReferenceTypes && !property.ClrType.IsValueType));
|
||||
if (propertyFluentApiCalls == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
usings.AddRange(propertyFluentApiCalls.GetRequiredUsings());
|
||||
|
||||
if (anyEntityTypeConfiguration && firstProperty)
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
#>
|
||||
entity.Property(e => e.<#= property.Name #>)<#= code.Fragment(propertyFluentApiCalls, indent: 4) #>;
|
||||
<#
|
||||
anyEntityTypeConfiguration = true;
|
||||
firstProperty = false;
|
||||
}
|
||||
|
||||
foreach (var foreignKey in entityType.GetForeignKeys())
|
||||
{
|
||||
var foreignKeyFluentApiCalls = foreignKey.GetFluentApiCalls(annotationCodeGenerator)
|
||||
?.FilterChain(c => !(Options.UseDataAnnotations && c.IsHandledByDataAnnotations));
|
||||
if (foreignKeyFluentApiCalls == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
usings.AddRange(foreignKeyFluentApiCalls.GetRequiredUsings());
|
||||
|
||||
if (anyEntityTypeConfiguration)
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
#>
|
||||
entity.HasOne(d => d.<#= foreignKey.DependentToPrincipal.Name #>).<#= foreignKey.IsUnique ? "WithOne" : "WithMany" #>(<#= foreignKey.PrincipalToDependent != null ? $"p => p.{foreignKey.PrincipalToDependent.Name}" : "" #>)<#= code.Fragment(foreignKeyFluentApiCalls, indent: 4) #>;
|
||||
<#
|
||||
anyEntityTypeConfiguration = true;
|
||||
}
|
||||
|
||||
foreach (var skipNavigation in entityType.GetSkipNavigations().Where(n => n.IsLeftNavigation()))
|
||||
{
|
||||
if (anyEntityTypeConfiguration)
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
|
||||
var left = skipNavigation.ForeignKey;
|
||||
var leftFluentApiCalls = left.GetFluentApiCalls(annotationCodeGenerator, useStrings: true);
|
||||
var right = skipNavigation.Inverse.ForeignKey;
|
||||
var rightFluentApiCalls = right.GetFluentApiCalls(annotationCodeGenerator, useStrings: true);
|
||||
var joinEntityType = skipNavigation.JoinEntityType;
|
||||
|
||||
if (leftFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(leftFluentApiCalls.GetRequiredUsings());
|
||||
}
|
||||
|
||||
if (rightFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(rightFluentApiCalls.GetRequiredUsings());
|
||||
}
|
||||
#>
|
||||
entity.HasMany(d => d.<#= skipNavigation.Name #>).WithMany(p => p.<#= skipNavigation.Inverse.Name #>)
|
||||
.UsingEntity<Dictionary<string, object>>(
|
||||
<#= code.Literal(joinEntityType.Name) #>,
|
||||
r => r.HasOne<<#= right.PrincipalEntityType.Name #>>().WithMany()<#= code.Fragment(rightFluentApiCalls, indent: 6) #>,
|
||||
l => l.HasOne<<#= left.PrincipalEntityType.Name #>>().WithMany()<#= code.Fragment(leftFluentApiCalls, indent: 6) #>,
|
||||
j =>
|
||||
{
|
||||
<#
|
||||
var joinKey = joinEntityType.FindPrimaryKey();
|
||||
var joinKeyFluentApiCalls = joinKey.GetFluentApiCalls(annotationCodeGenerator);
|
||||
|
||||
if (joinKeyFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(joinKeyFluentApiCalls.GetRequiredUsings());
|
||||
}
|
||||
#>
|
||||
j.HasKey(<#= code.Arguments(joinKey.Properties.Select(e => e.Name)) #>)<#= code.Fragment(joinKeyFluentApiCalls, indent: 7) #>;
|
||||
<#
|
||||
var joinEntityTypeFluentApiCalls = joinEntityType.GetFluentApiCalls(annotationCodeGenerator);
|
||||
if (joinEntityTypeFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(joinEntityTypeFluentApiCalls.GetRequiredUsings());
|
||||
#>
|
||||
j<#= code.Fragment(joinEntityTypeFluentApiCalls, indent: 7) #>;
|
||||
<#
|
||||
}
|
||||
|
||||
foreach (var index in joinEntityType.GetIndexes())
|
||||
{
|
||||
var indexFluentApiCalls = index.GetFluentApiCalls(annotationCodeGenerator);
|
||||
if (indexFluentApiCalls != null)
|
||||
{
|
||||
usings.AddRange(indexFluentApiCalls.GetRequiredUsings());
|
||||
}
|
||||
#>
|
||||
j.HasIndex(<#= code.Literal(index.Properties.Select(e => e.Name).ToArray()) #>, <#= code.Literal(index.GetDatabaseName()) #>)<#= code.Fragment(indexFluentApiCalls, indent: 7) #>;
|
||||
<#
|
||||
}
|
||||
|
||||
foreach (var property in joinEntityType.GetProperties())
|
||||
{
|
||||
var propertyFluentApiCalls = property.GetFluentApiCalls(annotationCodeGenerator);
|
||||
if (propertyFluentApiCalls == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
usings.AddRange(propertyFluentApiCalls.GetRequiredUsings());
|
||||
#>
|
||||
j.IndexerProperty<<#= code.Reference(property.ClrType) #>>(<#= code.Literal(property.Name) #>)<#= code.Fragment(propertyFluentApiCalls, indent: 7) #>;
|
||||
<#
|
||||
}
|
||||
#>
|
||||
});
|
||||
<#
|
||||
anyEntityTypeConfiguration = true;
|
||||
}
|
||||
#>
|
||||
});
|
||||
<#
|
||||
// If any signicant code was generated, append it to the main environment
|
||||
if (anyEntityTypeConfiguration)
|
||||
{
|
||||
mainEnvironment.Append(GenerationEnvironment);
|
||||
anyConfiguration = true;
|
||||
}
|
||||
|
||||
// Resume generating code into the main environment
|
||||
GenerationEnvironment = mainEnvironment;
|
||||
}
|
||||
|
||||
foreach (var sequence in Model.GetSequences())
|
||||
{
|
||||
var needsType = sequence.Type != typeof(long);
|
||||
var needsSchema = !string.IsNullOrEmpty(sequence.Schema) && sequence.Schema != sequence.Model.GetDefaultSchema();
|
||||
var sequenceFluentApiCalls = sequence.GetFluentApiCalls(annotationCodeGenerator);
|
||||
#>
|
||||
modelBuilder.HasSequence<#= needsType ? $"<{code.Reference(sequence.Type)}>" : "" #>(<#= code.Literal(sequence.Name) #><#= needsSchema ? $", {code.Literal(sequence.Schema)}" : "" #>)<#= code.Fragment(sequenceFluentApiCalls, indent: 3) #>;
|
||||
<#
|
||||
}
|
||||
|
||||
if (anyConfiguration)
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
#>
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
}
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||
}
|
||||
<#
|
||||
mainEnvironment = GenerationEnvironment;
|
||||
GenerationEnvironment = new StringBuilder();
|
||||
|
||||
foreach (var ns in usings.Distinct().OrderBy(x => x, new NamespaceComparer()))
|
||||
{
|
||||
#>
|
||||
using <#= ns #>;
|
||||
<#
|
||||
}
|
||||
|
||||
WriteLine("");
|
||||
|
||||
GenerationEnvironment.Append(mainEnvironment);
|
||||
#>
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
<#@ template hostSpecific="true" #>
|
||||
<#@ assembly name="Microsoft.EntityFrameworkCore" #>
|
||||
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
|
||||
<#@ assembly name="Microsoft.EntityFrameworkCore.Relational" #>
|
||||
<#@ assembly name="Microsoft.Extensions.DependencyInjection.Abstractions" #>
|
||||
<#@ parameter name="EntityType" type="Microsoft.EntityFrameworkCore.Metadata.IEntityType" #>
|
||||
<#@ parameter name="Options" type="Microsoft.EntityFrameworkCore.Scaffolding.ModelCodeGenerationOptions" #>
|
||||
<#@ parameter name="NamespaceHint" type="System.String" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ import namespace="System.ComponentModel.DataAnnotations" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
|
||||
<#@ import namespace="Microsoft.EntityFrameworkCore.Design" #>
|
||||
<#@ import namespace="Microsoft.Extensions.DependencyInjection" #>
|
||||
<#
|
||||
if (EntityType.IsSimpleManyToManyJoinEntityType())
|
||||
{
|
||||
// Don't scaffold these
|
||||
return "";
|
||||
}
|
||||
|
||||
var services = (IServiceProvider)Host;
|
||||
var annotationCodeGenerator = services.GetRequiredService<IAnnotationCodeGenerator>();
|
||||
var code = services.GetRequiredService<ICSharpHelper>();
|
||||
|
||||
var usings = new List<string>
|
||||
{
|
||||
"System",
|
||||
"System.Collections.Generic"
|
||||
};
|
||||
|
||||
if (Options.UseDataAnnotations)
|
||||
{
|
||||
usings.Add("System.ComponentModel.DataAnnotations");
|
||||
usings.Add("System.ComponentModel.DataAnnotations.Schema");
|
||||
usings.Add("Microsoft.EntityFrameworkCore");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(NamespaceHint))
|
||||
{
|
||||
#>
|
||||
namespace <#= NamespaceHint #>;
|
||||
|
||||
<#
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(EntityType.GetComment()))
|
||||
{
|
||||
#>
|
||||
/// <summary>
|
||||
/// <#= code.XmlComment(EntityType.GetComment()) #>
|
||||
/// </summary>
|
||||
<#
|
||||
}
|
||||
|
||||
if (Options.UseDataAnnotations)
|
||||
{
|
||||
foreach (var dataAnnotation in EntityType.GetDataAnnotations(annotationCodeGenerator))
|
||||
{
|
||||
#>
|
||||
<#= code.Fragment(dataAnnotation) #>
|
||||
<#
|
||||
}
|
||||
}
|
||||
#>
|
||||
public partial class <#= EntityType.Name #>
|
||||
{
|
||||
<#
|
||||
var firstProperty = true;
|
||||
foreach (var property in EntityType.GetProperties().OrderBy(p => p.GetColumnOrder() ?? -1))
|
||||
{
|
||||
if (!firstProperty)
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(property.GetComment()))
|
||||
{
|
||||
#>
|
||||
/// <summary>
|
||||
/// <#= code.XmlComment(property.GetComment(), indent: 1) #>
|
||||
/// </summary>
|
||||
<#
|
||||
}
|
||||
|
||||
if (Options.UseDataAnnotations)
|
||||
{
|
||||
var dataAnnotations = property.GetDataAnnotations(annotationCodeGenerator)
|
||||
.Where(a => !(a.Type == typeof(RequiredAttribute) && Options.UseNullableReferenceTypes && !property.ClrType.IsValueType));
|
||||
foreach (var dataAnnotation in dataAnnotations)
|
||||
{
|
||||
#>
|
||||
<#= code.Fragment(dataAnnotation) #>
|
||||
<#
|
||||
}
|
||||
}
|
||||
|
||||
usings.AddRange(code.GetRequiredUsings(property.ClrType));
|
||||
|
||||
var needsNullable = Options.UseNullableReferenceTypes && property.IsNullable && !property.ClrType.IsValueType;
|
||||
var needsInitializer = Options.UseNullableReferenceTypes && !property.IsNullable && !property.ClrType.IsValueType;
|
||||
#>
|
||||
public <#= code.Reference(property.ClrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
|
||||
<#
|
||||
firstProperty = false;
|
||||
}
|
||||
|
||||
foreach (var navigation in EntityType.GetNavigations())
|
||||
{
|
||||
WriteLine("");
|
||||
|
||||
if (Options.UseDataAnnotations)
|
||||
{
|
||||
foreach (var dataAnnotation in navigation.GetDataAnnotations(annotationCodeGenerator))
|
||||
{
|
||||
#>
|
||||
<#= code.Fragment(dataAnnotation) #>
|
||||
<#
|
||||
}
|
||||
}
|
||||
|
||||
var targetType = navigation.TargetEntityType.Name;
|
||||
if (navigation.IsCollection)
|
||||
{
|
||||
#>
|
||||
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; set; } = new List<<#= targetType #>>();
|
||||
<#
|
||||
}
|
||||
else
|
||||
{
|
||||
var needsNullable = Options.UseNullableReferenceTypes && !(navigation.ForeignKey.IsRequired && navigation.IsOnDependent);
|
||||
var needsInitializer = Options.UseNullableReferenceTypes && navigation.ForeignKey.IsRequired && navigation.IsOnDependent;
|
||||
#>
|
||||
public virtual <#= targetType #><#= needsNullable ? "?" : "" #> <#= navigation.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
|
||||
<#
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var skipNavigation in EntityType.GetSkipNavigations())
|
||||
{
|
||||
WriteLine("");
|
||||
|
||||
if (Options.UseDataAnnotations)
|
||||
{
|
||||
foreach (var dataAnnotation in skipNavigation.GetDataAnnotations(annotationCodeGenerator))
|
||||
{
|
||||
#>
|
||||
<#= code.Fragment(dataAnnotation) #>
|
||||
<#
|
||||
}
|
||||
}
|
||||
#>
|
||||
public virtual ICollection<<#= skipNavigation.TargetEntityType.Name #>> <#= skipNavigation.Name #> { get; set; } = new List<<#= skipNavigation.TargetEntityType.Name #>>();
|
||||
<#
|
||||
}
|
||||
#>
|
||||
}
|
||||
<#
|
||||
var previousOutput = GenerationEnvironment;
|
||||
GenerationEnvironment = new StringBuilder();
|
||||
|
||||
foreach (var ns in usings.Distinct().OrderBy(x => x, new NamespaceComparer()))
|
||||
{
|
||||
#>
|
||||
using <#= ns #>;
|
||||
<#
|
||||
}
|
||||
|
||||
WriteLine("");
|
||||
|
||||
GenerationEnvironment.Append(previousOutput);
|
||||
#>
|
||||
59
src/CloudGaming/CloudGaming.sln
Normal file
59
src/CloudGaming/CloudGaming.sln
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# 17
|
||||
VisualStudioVersion = 17.10.35027.167
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudGaming", "CloudGaming\CloudGaming.csproj", "{0465FB68-91F3-408E-BD0E-B4D99D91F857}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1-core", "1-core", "{FCA3CA4B-1993-429A-B2E9-2B05DB44F10E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3-api", "3-api", "{51CB40D2-99F5-43E8-95B4-3A75C91736A6}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5-console", "5-console", "{9F7EF36C-17BB-4F93-927E-F462FE3C9337}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "9-test", "9-test", "{0AD219C3-22E9-493C-835D-00694604BD68}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0-model", "0-model", "{A3F00FB0-49D6-48B1-99D9-4619634DF8D9}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F3436760-3B85-4022-AB1C-1DB47976A540}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2-utlie", "2-utlie", "{46D0820D-4EC3-4C22-986D-E505CDEC52D2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HuanMeng.DotNetCore", "1-utile\HuanMeng.DotNetCore\HuanMeng.DotNetCore.csproj", "{0AEFD698-79ED-4A23-AB45-3BAE39B22E10}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudGaming.Model", "CloudGaming.Model\CloudGaming.Model.csproj", "{B0208C8A-63DE-4295-B740-B7A0D556E1E0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0465FB68-91F3-408E-BD0E-B4D99D91F857}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0465FB68-91F3-408E-BD0E-B4D99D91F857}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0465FB68-91F3-408E-BD0E-B4D99D91F857}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0465FB68-91F3-408E-BD0E-B4D99D91F857}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0AEFD698-79ED-4A23-AB45-3BAE39B22E10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0AEFD698-79ED-4A23-AB45-3BAE39B22E10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0AEFD698-79ED-4A23-AB45-3BAE39B22E10}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0AEFD698-79ED-4A23-AB45-3BAE39B22E10}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B0208C8A-63DE-4295-B740-B7A0D556E1E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B0208C8A-63DE-4295-B740-B7A0D556E1E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B0208C8A-63DE-4295-B740-B7A0D556E1E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B0208C8A-63DE-4295-B740-B7A0D556E1E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{0465FB68-91F3-408E-BD0E-B4D99D91F857} = {51CB40D2-99F5-43E8-95B4-3A75C91736A6}
|
||||
{0AEFD698-79ED-4A23-AB45-3BAE39B22E10} = {46D0820D-4EC3-4C22-986D-E505CDEC52D2}
|
||||
{B0208C8A-63DE-4295-B740-B7A0D556E1E0} = {A3F00FB0-49D6-48B1-99D9-4619634DF8D9}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1D299D92-FA27-47A0-8D78-43D1FAFE7628}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
17
src/CloudGaming/CloudGaming/CloudGaming.csproj
Normal file
17
src/CloudGaming/CloudGaming/CloudGaming.csproj
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>60edfd76-b8fa-477a-b105-6e6a7da42295</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.DependencyValidation.Analyzers" Version="0.11.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
6
src/CloudGaming/CloudGaming/CloudGaming.http
Normal file
6
src/CloudGaming/CloudGaming/CloudGaming.http
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@CloudGaming_HostAddress = http://localhost:5171
|
||||
|
||||
GET {{CloudGaming_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CloudGaming.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/CloudGaming/CloudGaming/Dockerfile
Normal file
25
src/CloudGaming/CloudGaming/Dockerfile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
USER app
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
EXPOSE 8081
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["CloudGaming/CloudGaming.csproj", "CloudGaming/"]
|
||||
RUN dotnet restore "./CloudGaming/CloudGaming.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/CloudGaming"
|
||||
RUN dotnet build "./CloudGaming.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./CloudGaming.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "CloudGaming.dll"]
|
||||
25
src/CloudGaming/CloudGaming/Program.cs
Normal file
25
src/CloudGaming/CloudGaming/Program.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
52
src/CloudGaming/CloudGaming/Properties/launchSettings.json
Normal file
52
src/CloudGaming/CloudGaming/Properties/launchSettings.json
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5171"
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "https://localhost:7081;http://localhost:5171"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_HTTPS_PORTS": "8081",
|
||||
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||
},
|
||||
"publishAllPorts": true,
|
||||
"useSSL": true
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:31403",
|
||||
"sslPort": 44348
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/CloudGaming/CloudGaming/WeatherForecast.cs
Normal file
13
src/CloudGaming/CloudGaming/WeatherForecast.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
namespace CloudGaming
|
||||
{
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
public string? Summary { get; set; }
|
||||
}
|
||||
}
|
||||
8
src/CloudGaming/CloudGaming/appsettings.Development.json
Normal file
8
src/CloudGaming/CloudGaming/appsettings.Development.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/CloudGaming/CloudGaming/appsettings.json
Normal file
9
src/CloudGaming/CloudGaming/appsettings.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user