主頁 > .NET開發 > 基于JieBaNet+Lucene.Net實作全文搜索

基于JieBaNet+Lucene.Net實作全文搜索

2020-09-24 08:26:00 .NET開發

 

實作效果:

  上一篇文章有附全文搜索結果的設計圖,下面截一張開發完成上線后的實圖:

  基本風格是模仿的百度搜索結果,綠色的分頁略顯小清新,

  目前已采集并創建索引的文章約3W多篇,索引檔案不算太大,查詢速度非常棒,

  

刀不磨要生銹,人不學要落后,每天都要學一些新東西, 

 

基本技術介紹:

  還記得上一次做全文搜索是在2013年,主要核心設計與代碼均是當時的架構師寫的,自己只能算是全程參與,

  當時使用的是經典搭配:盤古分詞+Lucene.net,

  前幾篇文章有說到,盤古分詞已經很多年不更新了,我在SupportYun系統一直參考的JieBaNet來做分詞技術,

  那么是否也有成型的JieBaNet+Lucene.Net的全文搜索方案呢?

  經過多番尋找,在GitHub上面找到一個簡易的例子:https://github.com/anderscui/jiebaForLuceneNet

  博主下面要講的實作方案就是從這個demo得到的啟發,大家有興趣可以去看看這個demo,

  博主使用的具體版本:Lucene.net 3.0.3.0 ,JieBaNet 0.38.3.0(做過簡易的調整與擴展,前面文章有講到)

  首先我們對Lucene.Net的分詞器Tokenizer、分析器Analyzer做一個基于JieBaNet的擴展,

  1.基于LuceneNet擴展的JieBa分析器JiebaForLuceneAnalyzer  

復制代碼 復制代碼
 1     /// <summary>
 2     /// 基于LuceneNet擴展的JieBa分析器
 3     /// </summary>
 4     public class JiebaForLuceneAnalyzer : Analyzer
 5     {
 6         protected static readonly ISet<string> DefaultStopWords = StopAnalyzer.ENGLISH_STOP_WORDS_SET;
 7 
 8         private static ISet<string> StopWords;
 9 
10         static JiebaForLuceneAnalyzer()
11         {
12             StopWords = new HashSet<string>();
13             var stopWordsFile = Path.GetFullPath(JiebaNet.Analyser.ConfigManager.StopWordsFile);
14             if (File.Exists(stopWordsFile))
15             {
16                 var lines = File.ReadAllLines(stopWordsFile);
17                 foreach (var line in lines)
18                 {
19                     StopWords.Add(line.Trim());
20                 }
21             }
22             else
23             {
24                 StopWords = DefaultStopWords;
25             }
26         }
27 
28         public override TokenStream TokenStream(string fieldName, TextReader reader)
29         {
30             var seg = new JiebaSegmenter();
31             TokenStream result = new JiebaForLuceneTokenizer(seg, reader);
32             result = new LowerCaseFilter(result);
33             result = new StopFilter(true, result, StopWords);
34             return result;
35         }
36     }
復制代碼 復制代碼

  2.基于LuceneNet擴展的JieBa分詞器:JiebaForLuceneTokenizer

復制代碼 復制代碼
 1     /// <summary>
 2     /// 基于Lucene的JieBa分詞擴展
 3     /// </summary>
 4     public class JiebaForLuceneTokenizer:Tokenizer
 5     {
 6         private readonly JiebaSegmenter segmenter;
 7         private readonly ITermAttribute termAtt;
 8         private readonly IOffsetAttribute offsetAtt;
 9         private readonly ITypeAttribute typeAtt;
10 
11         private readonly List<Token> tokens;
12         private int position = -1;
13 
14         public JiebaForLuceneTokenizer(JiebaSegmenter seg, TextReader input):this(seg, input.ReadToEnd()) { }
15 
16         public JiebaForLuceneTokenizer(JiebaSegmenter seg, string input)
17         {
18             segmenter = seg;
19             termAtt = AddAttribute<ITermAttribute>();
20             offsetAtt = AddAttribute<IOffsetAttribute>();
21             typeAtt = AddAttribute<ITypeAttribute>();
22 
23             var text = input;
24             tokens = segmenter.Tokenize(text, TokenizerMode.Search).ToList();
25         }
26 
27         public override bool IncrementToken()
28         {
29             ClearAttributes();
30             position++;
31             if (position < tokens.Count)
32             {
33                 var token = tokens[position];
34                 termAtt.SetTermBuffer(token.Word);
35                 offsetAtt.SetOffset(token.StartIndex, token.EndIndex);
36                 typeAtt.Type = "Jieba";
37                 return true;
38             }
39 
40             End();
41             return false;
42         }
43 
44         public IEnumerable<Token> Tokenize(string text, TokenizerMode mode = TokenizerMode.Search)
45         {
46             return segmenter.Tokenize(text, mode);
47         }
48     }
復制代碼 復制代碼

理想如果不向現實做一點點屈服,那么理想也將歸于塵土, 

 

實作方案設計:

  我們做全文搜索的設計時一定會考慮的一個問題就是:我們系統是分很多模塊的,不同模塊的欄位差異很大,怎么才能實作同一個索引,既可以單個模塊搜索又可以全站搜索,甚至按一些欄位做條件來搜索呢?

  這些也是SupportYun系統需要考慮的問題,因為目前的資料就天然的拆分成了活動、文章兩個類別,欄位也大有不同,博主想實作的是一個可以全站搜索(結果包括活動、文章),也可以在文章欄目/活動欄目分別搜索,并且可以按幾個指定欄位來做搜索條件,

  要做一個這樣的全文搜索功能,我們需要從程式設計上來下功夫,下面就介紹一下博主的設計方案:

  一、索引創建

    

    1.我們設計一個IndexManager來處理最基本的索引創建、更新、洗掉操作,

 View Code

    2.創建、更新使用到的標準資料類:IndexContent,

    我們設計TableName(對應DB表名)、RowId(對應DB主鍵)、CollectTime(對應DB資料創建時間)、ModuleType(所屬系統模塊)、Title(檢索標題)、IndexTextContent(檢索文本)等六個基礎欄位,所有模塊需要創建索引必須構建該6個欄位(大家可據具體情況擴展),

    然后設計10個預留欄位Tag1-Tag10,用以兼容各大模塊其他不同欄位,

    預留欄位的存盤、索引方式可獨立配置,

 View Code

    其中BaseIndexContent含有六個基礎欄位,

    3.創建一個子模塊索引構建器的介面:IIndexBuilder,

    各子模塊通過繼承實作IIndexBuilder,來實作索引的操作,

 View Code

    4.下面我們以活動模塊為例,來實作索引創建,

    a)首先創建一個基于活動模塊的資料類:ActivityIndexContent,可以將我們需要索引或存盤的欄位都設計在內,

 View Code

    b)我們再創建ActivityIndexBuilder并繼承IIndexBuilder,實作其創建、更新、洗掉方法,

 View Code

    代碼就不解釋了,很簡單,主要就是呼叫IndexManager來執行操作,

    我們只需要在需要創建活動資料索引的業務點,構建ActivityIndexBuilder物件,并構建ActivityIndexContent集合作為引數,呼叫BuildIndex方法即可,

 

  二、全文搜索

    全文搜索我們采用同樣的設計方式,

    1.設計一個抽象的搜索類:BaseIndexSearch,所有搜索模塊(包括全站)均需繼承它來實作搜索效果,

復制代碼 復制代碼
  1     public abstract class BaseIndexSearch<TIndexSearchResultItem>
  2         where TIndexSearchResultItem : IndexSearchResultItem
  3     {
  4         /// <summary>
  5         /// 索引存盤目錄
  6         /// </summary>
  7         private static readonly string IndexStorePath = ConfigurationManager.AppSettings["IndexStorePath"];
  8         private readonly string[] fieldsToSearch;
  9         protected static readonly SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<em>", "</em>");
 10         private static IndexSearcher indexSearcher = null;
 11 
 12         /// <summary>
 13         /// 索引內容命中片段大小
 14         /// </summary>
 15         public int FragmentSize { get; set; }
 16 
 17         /// <summary>
 18         /// 構造方法
 19         /// </summary>
 20         /// <param name="fieldsToSearch">搜索文本欄位</param>
 21         protected BaseIndexSearch(string[] fieldsToSearch)
 22         {
 23             FragmentSize = 100;
 24             this.fieldsToSearch = fieldsToSearch;
 25         }
 26 
 27         /// <summary>
 28         /// 創建搜索結果實體
 29         /// </summary>
 30         /// <returns></returns>
 31         protected abstract TIndexSearchResultItem CreateIndexSearchResultItem();
 32 
 33         /// <summary>
 34         /// 修改搜索結果(主要修改tag欄位對應的屬性)
 35         /// </summary>
 36         /// <param name="indexSearchResultItem">搜索結果項實體</param>
 37         /// <param name="content">用戶搜索內容</param>
 38         /// <param name="docIndex">索引庫位置</param>
 39         /// <param name="doc">當前位置內容</param>
 40         /// <returns>搜索結果</returns>
 41         protected abstract void ModifyIndexSearchResultItem(ref TIndexSearchResultItem indexSearchResultItem, string content, int docIndex, Document doc);
 42 
 43         /// <summary>
 44         /// 修改篩選器(各模塊)
 45         /// </summary>
 46         /// <param name="filter"></param>
 47         protected abstract void ModifySearchFilter(ref Dictionary<string, string> filter);
 48 
 49         /// <summary>
 50         /// 全庫搜索
 51         /// </summary>
 52         /// <param name="content">搜索文本內容</param>
 53         /// <param name="filter">查詢內容限制條件,默認為null,不限制條件.</param>
 54         /// <param name="fieldSorts">對欄位進行排序</param>
 55         /// <param name="pageIndex">查詢結果當前頁,默認為1</param>
 56         /// <param name="pageSize">查詢結果每頁結果數,默認為20</param>
 57         public PagedIndexSearchResult<TIndexSearchResultItem> Search(string content
 58             , Dictionary<string, string> filter = null, List<FieldSort> fieldSorts = null
 59             , int pageIndex = 1, int pageSize = 20)
 60         {
 61             try
 62             {
 63                 if (!string.IsNullOrEmpty(content))
 64                 {
 65                     content = ReplaceIndexSensitiveWords(content);
 66                     content = GetKeywordsSplitBySpace(content,
 67                         new JiebaForLuceneTokenizer(new JiebaSegmenter(), content));
 68                 }
 69                 if (string.IsNullOrEmpty(content) || pageIndex < 1)
 70                 {
 71                     throw new Exception("輸入引數不符合要求(用戶輸入為空,頁碼小于等于1)");
 72                 }
 73 
 74                 var stopWatch = new Stopwatch();
 75                 stopWatch.Start();
 76 
 77                 Analyzer analyzer = new JiebaForLuceneAnalyzer();
 78                 // 索引條件創建
 79                 var query = MakeSearchQuery(content, analyzer);
 80                 // 篩選條件構建
 81                 filter = filter == null ? new Dictionary<string, string>() : new Dictionary<string, string>(filter);
 82                 ModifySearchFilter(ref filter);
 83                 Filter luceneFilter = MakeSearchFilter(filter);
 84 
 85                 #region------------------------------執行查詢---------------------------------------
 86 
 87                 TopDocs topDocs;
 88                 if (indexSearcher == null)
 89                 {
 90                     var dir = new DirectoryInfo(IndexStorePath);
 91                     FSDirectory entityDirectory = FSDirectory.Open(dir);
 92                     IndexReader reader = IndexReader.Open(entityDirectory, true);
 93                     indexSearcher = new IndexSearcher(reader);
 94                 }
 95                 else
 96                 {
 97                     IndexReader indexReader = indexSearcher.IndexReader;
 98                     if (!indexReader.IsCurrent())
 99                     {
100                         indexSearcher.Dispose();
101                         indexSearcher = new IndexSearcher(indexReader.Reopen());
102                     }
103                 }
104                 // 收集器容量為所有
105                 int totalCollectCount = pageIndex*pageSize;
106                 Sort sort = GetSortByFieldSorts(fieldSorts);
107                 topDocs = indexSearcher.Search(query, luceneFilter, totalCollectCount, sort ?? Sort.RELEVANCE);
108 
109                 #endregion
110 
111                 #region-----------------------回傳結果生成-------------------------------
112 
113                 ScoreDoc[] hits = topDocs.ScoreDocs;
114                 var start = (pageIndex - 1)*pageSize + 1;
115                 var end = Math.Min(totalCollectCount, hits.Count());
116 
117                 var result = new PagedIndexSearchResult<TIndexSearchResultItem>
118                 {
119                     PageIndex = pageIndex,
120                     PageSize = pageSize,
121                     TotalRecords = topDocs.TotalHits
122                 };
123 
124                 for (var i = start; i <= end; i++)
125                 {
126                     var scoreDoc = hits[i - 1];
127                     var doc = indexSearcher.Doc(scoreDoc.Doc);
128 
129                     var indexSearchResultItem = CreateIndexSearchResultItem();
130                     indexSearchResultItem.DocIndex = scoreDoc.Doc;
131                     indexSearchResultItem.ModuleType = doc.Get("ModuleType");
132                     indexSearchResultItem.TableName = doc.Get("TableName");
133                     indexSearchResultItem.RowId = Guid.Parse(doc.Get("RowId"));
134                     if (!string.IsNullOrEmpty(doc.Get("CollectTime")))
135                     {
136                         indexSearchResultItem.CollectTime = DateTime.Parse(doc.Get("CollectTime"));
137                     }
138                     var title = GetHighlighter(formatter, FragmentSize).GetBestFragment(content, doc.Get("Title"));
139                     indexSearchResultItem.Title = string.IsNullOrEmpty(title) ? doc.Get("Title") : title;
140                     var text = GetHighlighter(formatter, FragmentSize)
141                         .GetBestFragment(content, doc.Get("IndexTextContent"));
142                     indexSearchResultItem.Content = string.IsNullOrEmpty(text)
143                         ? (doc.Get("IndexTextContent").Length > 100
144                             ? doc.Get("IndexTextContent").Substring(0, 100)
145                             : doc.Get("IndexTextContent"))
146                         : text;
147                     ModifyIndexSearchResultItem(ref indexSearchResultItem, content, scoreDoc.Doc, doc);
148                     result.Add(indexSearchResultItem);
149                 }
150                 stopWatch.Stop();
151                 result.Elapsed = stopWatch.ElapsedMilliseconds*1.0/1000;
152 
153                 return result;
154 
155                 #endregion
156             }
157             catch (Exception exception)
158             {
159                 LogUtils.ErrorLog(exception);
160                 return null;
161             }
162         }
163 
164         private Sort GetSortByFieldSorts(List<FieldSort> fieldSorts)
165         {
166             if (fieldSorts == null)
167             {
168                 return null;
169             }
170             return new Sort(fieldSorts.Select(fieldSort => new SortField(fieldSort.FieldName, SortField.FLOAT, !fieldSort.Ascend)).ToArray());
171         }
172 
173         private static Filter MakeSearchFilter(Dictionary<string, string> filter)
174         {
175             Filter luceneFilter = null;
176             if (filter != null && filter.Keys.Any())
177             {
178                 var booleanQuery = new BooleanQuery();
179                 foreach (KeyValuePair<string, string> keyValuePair in filter)
180                 {
181                     var termQuery = new TermQuery(new Term(keyValuePair.Key, keyValuePair.Value));
182                     booleanQuery.Add(termQuery, Occur.MUST);
183                 }
184                 luceneFilter = new QueryWrapperFilter(booleanQuery);
185             }
186             return luceneFilter;
187         }
188 
189         private Query MakeSearchQuery(string content, Analyzer analyzer)
190         {
191             var query = new BooleanQuery();
192             // 總查詢引數
193             // 屬性查詢
194             if (!string.IsNullOrEmpty(content))
195             {
196                 QueryParser parser = new MultiFieldQueryParser(Version.LUCENE_30, fieldsToSearch, analyzer);
197                 Query queryObj;
198                 try
199                 {
200                     queryObj = parser.Parse(content);
201                 }
202                 catch (ParseException parseException)
203                 {
204                     throw new Exception("在FileLibraryIndexSearch中構造Query時出錯,", parseException);
205                 }
206                 query.Add(queryObj, Occur.MUST);
207             }
208             return query;
209         }
210 
211         private string GetKeywordsSplitBySpace(string keywords, JiebaForLuceneTokenizer jiebaForLuceneTokenizer)
212         {
213             var result = new StringBuilder();
214 
215             var words = jiebaForLuceneTokenizer.Tokenize(keywords);
216 
217             foreach (var word in words)
218             {
219                 if (string.IsNullOrWhiteSpace(word.Word))
220                 {
221                     continue;
222                 }
223 
224                 result.AppendFormat("{0} ", word.Word);
225             }
226 
227             return result.ToString().Trim();
228         }
229 
230         private string ReplaceIndexSensitiveWords(string str)
231         {
232             str = str.Replace("+", "");
233             str = str.Replace("+", "");
234             str = str.Replace("-", "");
235             str = str.Replace("-", "");
236             str = str.Replace("!", "");
237             str = str.Replace("!", "");
238             str = str.Replace("(", "");
239             str = str.Replace(")", "");
240             str = str.Replace("(", "");
241             str = str.Replace(")", "");
242             str = str.Replace(":", "");
243             str = str.Replace(":", "");
244             str = str.Replace("^", "");
245             str = str.Replace("[", "");
246             str = str.Replace("]", "");
247             str = str.Replace("【", "");
248             str = str.Replace("】", "");
249             str = str.Replace("{", "");
250             str = str.Replace("}", "");
251             str = str.Replace("{", "");
252             str = str.Replace("}", "");
253             str = str.Replace("~", "");
254             str = str.Replace("~", "");
255             str = str.Replace("*", "");
256             str = str.Replace("*", "");
257             str = str.Replace("?", "");
258             str = str.Replace("?", "");
259             return str;
260         }
261 
262         protected Highlighter GetHighlighter(Formatter formatter, int fragmentSize)
263         {
264             var highlighter = new Highlighter(formatter, new Segment()) { FragmentSize = fragmentSize };
265             return highlighter;
266         }
267     }
復制代碼 復制代碼

    幾個protected abstract方法,是需要繼承的子類來實作的,

    其中為了實作搜索結果對命中關鍵詞進行高亮顯示,特參考了盤古分詞的Highlighter,原則是此處應該是參照盤古分詞的原始碼,自己使用JieBaNet來做實作的,由于工期較緊,直接參考了盤古,

    2.我們設計一個IndexSearchResultItem,表示搜索結果的基類,

 View Code

    3.我們來看看具體的實作,先來看全站搜索的SearchService

復制代碼 復制代碼
 1     public class IndexSearch : BaseIndexSearch<IndexSearchResultItem>
 2     {
 3         public IndexSearch()
 4             : base(new[] { "IndexTextContent", "Title" })
 5         {
 6         }
 7 
 8         protected override IndexSearchResultItem CreateIndexSearchResultItem()
 9         {
10             return new IndexSearchResultItem();
11         }
12 
13         protected override void ModifyIndexSearchResultItem(ref IndexSearchResultItem indexSearchResultItem, string content,
14             int docIndex, Document doc)
15         {
16             //不做修改
17         }
18 
19         protected override void ModifySearchFilter(ref Dictionary<string, string> filter)
20         {
21             //不做篩選條件修改
22         }
23     }
復制代碼 復制代碼

    是不是非常簡單,由于我們此處搜索的是全站,結果展示直接用基類,取出基本欄位即可,

    4.再列舉一個活動的搜索實作,

    a)我們首先創建一個活動搜索結果類ActivityIndexSearchResultItem,繼承自結果基類IndexSearchResultItem

 View Code

    b)然后創建活動模塊的搜索服務:ActivityIndexSearch,同樣需要繼承BaseIndexSearch,這時候ActivityIndexSearch只需要相對全站搜索修改幾個引數即可,

復制代碼 復制代碼
 1     public class ActivityIndexSearch: BaseIndexSearch<ActivityIndexSearchResultItem>
 2     {
 3         public ActivityIndexSearch()
 4             : base(new[] { "IndexTextContent", "Title" })
 5         {
 6         }
 7 
 8         protected override ActivityIndexSearchResultItem CreateIndexSearchResultItem()
 9         {
10             return new ActivityIndexSearchResultItem();
11         }
12 
13         protected override void ModifyIndexSearchResultItem(ref ActivityIndexSearchResultItem indexSearchResultItem, string content,
14             int docIndex, Document doc)
15         {
16             indexSearchResultItem.ActivityTypes = doc.Get("Tag1");
17             indexSearchResultItem.Url = doc.Get("Tag2");
18             indexSearchResultItem.SourceName = doc.Get("Tag3");
19             indexSearchResultItem.SourceOfficialHotline = doc.Get("Tag4");
20             indexSearchResultItem.SourceUrl = doc.Get("Tag5");
21             indexSearchResultItem.CityId=new Guid(doc.Get("Tag6"));
22             indexSearchResultItem.Address = doc.Get("Tag7");
23             indexSearchResultItem.ActivityDate = doc.Get("Tag8");
24         }
25 
26         protected override void ModifySearchFilter(ref Dictionary<string, string> filter)
27         {
28             filter.Add("ModuleType", "活動");
29         }
30     }
復制代碼 復制代碼

    篩選條件加上模塊=活動,回傳結果資料類指定,活動特有欄位回傳賦值,

    業務呼叫就非常簡單了,

    全站全文搜索:我們直接new IndexSearch(),然后呼叫其Search()方法

    活動全文搜索:我們直接new ActivityIndexSearch(),然后呼叫其Search()方法

    Search()方法幾個引數:

    ///<param name="content">搜索文本內容</param>
    /// <param name="filter">查詢內容限制條件,默認為null,不限制條件.</param>
    /// <param name="fieldSorts">對欄位進行排序</param>
    /// <param name="pageIndex">查詢結果當前頁,默認為1</param>
    /// <param name="pageSize">查詢結果每頁結果數,默認為20</param>

 如果我們用軟能力而不是用技術能力來區分程式員的好壞 – 是不是有那么點反常和變態,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/117680.html

標籤:C#

上一篇:使用FastReport報表工具生成報表PDF檔案

下一篇:C#取視頻某一幀圖片

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more