UWInfo Blog
發表新文章
[Join] | [忘記密碼] | [Login]
搜尋

搜尋意見
文章分類-#Author#
[所有文章分類]
所有文章分類
  • ASP.NET (48)
  • ASP.NET2.0 (15)
  • ASP.NET4.0 (34)
  • JavaScript (49)
  • jQuery (26)
  • FireFox (4)
  • UW系統設定 (3)
  • SQL (40)
  • SQL 2008 (25)
  • mirror (4)
  • SVN (4)
  • IE (9)
  • IIS (21)
  • IIS6 (1)
  • 閒聊 (7)
  • W3C (6)
  • 作業系統 (9)
  • C# (24)
  • CSS (12)
  • FileServer (1)
  • HTML 5 (11)
  • CKEditor (3)
  • UW.dll (13)
  • Visual Studio (16)
  • Browser (8)
  • SEO (1)
  • Google Apps (3)
  • 網站輔助系統 (4)
  • DNS (5)
  • SMTP (4)
  • 網管 (11)
  • 社群API (3)
  • SSL (4)
  • App_Inventor (1)
  • URLRewrite (2)
  • 開發工具 (6)
  • JSON (1)
  • Excel2007 (1)
  • 試題 (3)
  • LINQ (1)
  • bootstrap (0)
  • Vue (3)
  • IIS7 (3)
  • foodpanda (2)
  • 編碼 (2)
  • 資安 (3)
  • Sourcetree (1)
  • MAUI (1)
  • CMD (1)
  • my sql (1)
  • API串接 (1)
  • EF MODEL (1)
最新回應
  • Newtonsoft.Json.JsonConvert.DeserializeObject 失敗的情況
    test...more
  • dotnet ef dbcontext scaffold
    ...more
  • [ASP.NET] 利用 aspnet_regiis 加密 web.config
    ...more
  • IIS ARR (reverse proxy) 服務安裝
    ...more
  • [錯誤訊息] 請加入 ScriptResourceMapping 命名的 jquery (區分大小寫)
    ...more
  • 用 Javascript 跨網頁讀取 cookie (Cookie cross page, path of cookie)
    ...more
  • 線上客服 - MSN
    本人信箱被盜用以致資料外洩,是否可以請貴平台予以協助刪除該信箱之使用謝謝囉...more
  • 插入文字到游標或選取處
    aaaaa...more
  • IIS 配合 AD (Active Directory) 認証, 使用 .Net 6.0
    太感謝你了~~~你救了我被windows 認證卡了好幾天QQ...more
  • PostgreSQL 的 monitor trigger
    FOR EACH ROW 可能要改為 FOR EACH STATEMENT ...more
標籤
  • 88 order b
  • 530
  • C
  • imvnOSCt
  • who
  • gui order
  • -9692
  • shop ORDER
  • 版本
  • aspx
  • find212112
  • useragent
  • LINE Pay P
  • ad
  • Su
  • sp_
  • asp.net
  • 80
  • en
  • blog
  • Remote
  • ef
  • 錯誤碼為 -2146
  • win ORDER
  • InvalidOpe
  • 834
  • -2391 UNIO
  • validation
  • 50,
  • array
  • web
  • 244
  • 9056
  • cookie
  • unicode
  • idictionar
  • 60,
  • DxHizbmA
  • HTTP 錯誤 40
  • .
  • 908
  • ab79gvdd
  • 144
  • .net
  • a
  • 252
  • sing,
  • admin
  • Connection
  • cros
頁數 1 / 3 下一頁
搜尋 core 結果:
專案使用 MS SQL EF6.0 升到 EF8.0 要注意的事情
這幾天因為要把農會收穀系統(使用 mssql) 新增複製到另一個農會專案
想說順便把專案升級到 core 8.0,EF也升到 8.0
結果發生一些狀況,這邊紀錄一下

1.  mssql 連線字串要有 TrustServerCertificate=True 這段文字
2. 6.0 與 8.0 有點天差地遠,轉過來時要把 Models 整個重新 build (scaffold 指令) 
3. 因為資料庫也改名,所以整個專案 DBCntext 的名稱要整個調一遍
4. 然後因為資料庫是從舊專案複製過來,但因為 EF8.0關係,資料庫也要做升級
     COMPATIBILITY_LEVEL 等級要大於等於 130 以上
     所以要下這個 SQL 指令
ALTER DATABASE YourDBName SET COMPATIBILITY_LEVEL = 130;

   至於為何要把 COMPATIBILITY_LEVEL 改 130 以上,主要是因為 會用到 MS SQL 的 OPENJSON 功能

btw, 發現 EF 轉 SQL 指令,會跟原本想像的不一樣,原來他是把 陣列用 JSON 方式帶進 SQL
_dbContext.V_Storage.AsNoTracking().Where(x => successIds.Contains(x.Id) && x.Is_Deleted == "N").ToList();

結果轉出 
[Parameters=[@__successIds_0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']

SELECT [v].* (省略列舉欄位)
FROM [V_Storage] AS [v]
WHERE [v].[Id] IN (
    SELECT [s].[value]
    FROM OPENJSON(@__successIds_0) WITH ([value] int '$') AS [s]
) AND [v].[Is_Deleted] = 'N'


​​​​​​而不是 IN ('A','B','C' ..))
More...
darren, 2025/12/12 下午 06:24:16
多專案的 swagger 設定 (swashbuckle.aspnetcore)
多專案(multiple projects)時,會產生多個 xml document, 叫用 IncludeXmlComments 時記得要把每個專案的 xml document 都載入,才能顯示不同專案中的註解和說明 (comment)。

範例如下:

builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });

    //多個 Project, 每一個 xmldocument 都要載入
    List<string> xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml", SearchOption.TopDirectoryOnly).ToList();
    foreach (string fileName in xmlFiles)
    {
        string xmlFilePath = Path.Combine(AppContext.BaseDirectory, fileName);
        if (File.Exists(xmlFilePath))
            options.IncludeXmlComments(xmlFilePath, includeControllerXmlComments: true);
    }

    //防止 SchemaId 的錯誤
    options.CustomSchemaIds(type => type.ToString());
});


這些都可以從註解中讀取:


 
 

 
More...
Bike, 2024/8/8 上午 10:21:46
Restful 的 API 範例
Restful 的 API 範例,比較特別的是取得單一筆資料時,不是用一般常見的 {id} 而是用 get?id=xxx 的方式,以避免 XXS 的功擊。(不要把原網頁中的參數拼入 API 網址,要改用 Query String 的方式傳給 API)

using Ds;
using Ds.Gv;
using iText.Kernel.Geom;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NPOI.SS.Formula.Functions;
using NPOI.SS.Util;
using Su;
using System.Linq.Expressions;

namespace CallCampaign.Api
{
    /// <summary>
    /// 行銷活動
    /// </summary>
    [Route("api/call-campaign")]
    [ApiController]
    [SetAuthorizationFilter(Sh.AuthCode.不設限)]
    public class ReserveCampaignController : Controller
    {
        /// <summary>
        /// 取得行銷活動列表
        /// </summary>
        /// <param name="reserveCampaignName"></param>
        /// <param name="currentPage"></param>
        /// <param name="pageSize"></param>
        /// <param name="orderByName"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        [HttpGet("")]
        public async Task<object> ListAsync([FromQuery] string reserveCampaignName = "", [FromQuery] int? currentPage = 1, [FromQuery] int? pageSize = 20, [FromQuery] string orderByName = "OrderNo", [FromQuery] string sort = "asc")
        {
            if (pageSize > 500)
            {
                pageSize = 500;
            }

            if (!(sort == "asc" || sort == "desc"))
            {
                throw new CustomException(System.Net.HttpStatusCode.BadRequest, "sort只能是asc或desc");
            }

            var temp = new V_ReserveCampaign().GetType().GetProperty(orderByName);
            if (temp == null)
            {
                throw new CustomException(System.Net.HttpStatusCode.BadRequest, "不存在欄位");
            }

            Expression<Func<V_ReserveCampaign, bool>> q = p => p.Is_Deleted == "N"
                    && (string.IsNullOrEmpty(reserveCampaignName) || (p.ReserveCampaignName != null && p.ReserveCampaignName.Contains(reserveCampaignName)))
                    ;

            if (orderByName.ToLower().Trim() != "id")
            {
                orderByName += " " + sort + ", id desc";
            }
            else
            {
                orderByName += " " + sort;
            }

            var ct = NewContext.GvContext;
            var list = await ct.GetPageListAsync(q, columns: "Id, ReserveCampaignName, OrderNo, StartAt, EndAt, ModifierName, ModifyDate, CreatorName, CreateDate", page: currentPage ?? 1, pageSize: pageSize ?? 20, orderByName);
            //var list = await ct.GetPageListAsync(q, page: currentPage ?? 1, pageSize: pageSize ?? 20, orderByName + " " + sort);
            return list;
        }

        /// <summary>
        /// 取得行銷活動
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        [HttpGet("get")]
        public async Task<dynamic> GetAsync([FromQuery] int Id)
        {
            var res = await Ds.NewContext.GvContext.ReserveCampaigns.Where(r => r.Id == Id)
                .FirstOrDefaultAsync();

            if (res == null)
            {
                throw new CustomException(System.Net.HttpStatusCode.BadRequest, "查無資料 " + Id.ToString());
            }
            return res;
        }
                
        /// <summary>
        /// 建立行銷活動
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpPost("")]
        public async Task<object> CreateAsync(Dtos.CreateReserveCampaign dto)
        {
            var ct = NewContext.GvContext;
            var res = await Models.ReserveCampaignHelper.CreateReserveCampaignAsync(ct, dto);
            return res;
        }

        /// <summary>
        /// 編輯行銷活動
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpPatch("")]
        public async Task<object> UpdateAsync(Dtos.UpdateReserveCampaign dto)
        {
            var ct = NewContext.GvContext;
            var res = await Models.ReserveCampaignHelper.UpdateReserveCampaignAsync(ct, dto);
            return res;
        }

        /// <summary>
        /// 刪除行銷活動
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpDelete("")]
        public async Task<object> DeleteAsync([FromQuery] int id)
        {
            var res = await Ds.NewContext.GvContext.MarkDeleteAsync<Ds.Gv.ReserveCampaign>(id, Sh.ModifyInfo);
            return res;
        }
    }
}



再增加一個同步範例(只例出 action)


        /// <summary>
        /// 取得列表
        /// </summary>
        /// <param name="name"></param>
        /// <param name="currentPage"></param>
        /// <param name="pageSize"></param>
        /// <param name="orderByName"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        [HttpGet("")]
        public object List([FromQuery] string name = "", [FromQuery] int? currentPage = 1, [FromQuery] int? pageSize = 20, [FromQuery] string orderByName = "OrderNo", [FromQuery] string sort = "asc")
        {
            return "";
        }

        /// <summary>
        /// 取得明細資料
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        [HttpGet("get")]
        public object Get([FromQuery] int id)
        {
            return "";
        }

        /// <summary>
        /// 建立
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpPost("")]
        public object Create(Dtos.PhysicalCheckUpType dto)
        {
            return "";
        }

        /// <summary>
        /// 編輯
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpPatch("")]
        public object Update(Dtos.PhysicalCheckUpType dto)
        {
            return "";
        }

        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpDelete("")]
        public object Delete([FromQuery] int id)
        {
            return 1;
        }
More...
Bike, 2023/12/13 上午 08:54:28
.net Core環境變數加密方式
簡易執行方式
1. 確定Program.cs有加上Su.Encryption.AesEncryptor.InitAesPaddingEncryptor(secret , iv , encKey , dataRoot)
2. 確定appsettings.json有設定變數,DataRoot、EnvironmentEncKey
3. 先執行一次,會報錯誤,在dataRoot位置會產生一組$"{encKey}.ps1,裡面會放設定環境變數的指令
4. 設定完,記得把 $"{encKey}.ps1檔案刪除
===以上,設定完環境變數===
1. 確定Program.cs有加上Dictionary<string, string> encSetting = Su.Encryption.GetDecryptedSetting
2. 確定appsettings.json有設定變數,DataRoot、EnvironmentEncKey , ShowEncSetting 設定為 false
3. 在 $"{dataRoot}\Config\XXXX_dec.json"設定DBC連線,要注意連線字串要加上  "TrustServerCertificate=true;",因為.net Core一定要藥用SSL連線
4. 設定完之後,在執行一次,會自動產生XXXX.json
5. 刪除XXXX_dec.json
===以上,產生完加密DBC連線
備註,要取回解密連線字串,appsettings.json有設定變數 , ShowEncSetting 設定為 true

概念解說
原本  >> config大多是明文,駭客可以藉由偷到config來知道變數設定,例如  dbc連線資訊、某個APP Key
之後  >> 將重要資訊分成兩段,
appsettings.json  >>  放可公開資訊的設定,例如 DataRoot、EnvironmentEncKey

在環境變數加上一個 由本機產生的隨機變數
讓本機的所有


原理說明
1. .Net Core 啟動時最先執行檔案 Program.cs,在一開頭先檢查本機是否有設定環境變數
緯中用的function Su.Encryption.AesEncryptor.InitAesPaddingEncryptor(secret , iv , encKey , dataRoot)
進階加密標準(英語:Advanced Encryption Standard,縮寫:AES)

沒有的話,會在 dataRoot 這邊產生一個 encKey.ps1的檔案,裡面放有環境變數設定的指令
(encSecretAndIv = Su.Encryption.AesEncryptor.Encrypt(envSecret, envIv, Su.TextFns.GetRandomString(48));
var command = $"[Environment]::SetEnvironmentVariable('{variableName}', '{encSecretAndIv}', 'Machine')";)
(隨機產生48碼亂數,(前32碼為本機專用Secret、後16碼為iv),進行aes加密)




 
More...
Doug, 2023/6/17 下午 12:04:28
Maui With SQLite
試了這兩個 Sample:

https://learn.microsoft.com/en-us/training/modules/store-local-data/4-exercise-store-data-locally-with-sqlite

和 

https://blazorhelpwebsite.com/ViewBlogPost/61

結果專案在 Windows 環境下都可以正常執行,但切換到 Android Emulator 就無法存檔。

在查了很多資料後,發現需要裝額外的 Package,需安裝的 Package 如下:

    <ItemGroup>
     <PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
     <PackageReference Include="SQLiteNetExtensions.Async" Version="2.1.0" />
     <PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.4" />
     <PackageReference Include="SQLitePCLRaw.core" Version="2.1.4" />
     <PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" Version="2.1.4" />
     <PackageReference Include="SQLitePCLRaw.provider.dynamic_cdecl" Version="2.1.4" />
     <PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
    </ItemGroup>



其它的發現為:
1. 雙點專案,就可以開啟 csproj 檔案。下方的 ItemGroup 就是已安裝的 package

2. 可以把 chrome 連接到 Android Emulator 的方法,在網址列輸入以下指令:
chrome://inspect/#devices
More...
Bike, 2023/2/12 下午 05:09:11
VS2022 上面安裝 NSwag (.Net 6.0)
改用 NSwag 時直接 Copy 舊專案,發現找不到 AddOpenApiDocument 和 UseOpenApi 以及 UseSwaggerUi3 如下圖:

 
 

找了一些文件,要求安裝套件,都沒有用,後來發現在 Dependencies 的 Packages 直接新增 NSwag.AspNetCore 即可 





 
不要用 NuGet Package Manager 哦...
More...
Bike, 2022/9/3 下午 05:31:14
The JSON value could not be converted to System.Nullable`1[System.DateTime]
.net 6 在 model 中上傳日期字串時,如果遇到 "The JSON value could not be converted to System.Nullable`1[System.DateTime]"  這個錯誤,解決方法如下:

1. 安裝套件: Microsoft.AspNetCore.Mvc.NewtonsoftJson

2. 在 program.cs 中,原來來的

builder.Services.AddControllers()

改為 (其 options 的部份非必要)

builder.Services.AddControllers()
    .AddNewtonsoftJson(options =>
     {
         options.SerializerSettings.ContractResolver = new Su.CamelCaseContractResolver();
     });
More...
Bike, 2022/8/21 下午 10:12:43
Swagger 介面上傳檔案 (NSwag.AspNetCore IFormFile)
直接對 Action 參數傳入 IFormFile 物件會變成 Json 物件,要包在物件中才會正常 出現一個 file upload 如下圖












 
More...
Bike, 2022/6/27 下午 04:02:19
使用Lucene.Net達成全文檢索!基礎解說(二)
上一集當中我們完成了Lucene基本操作中的Create與Read,這一集會將CRUD中的Update與Delete的操作方法告訴你,並且本集會著重於講解關於"Norms"與權重(Boost)在Lucene中的運作概念。

首先我們建立一個.Net 6的主控台應用程式
   

建立好後於右側專案右鍵選擇"管理Nuget套件",並選擇"瀏覽">於搜索列中搜尋"Lucene">安裝3.0.3最新穩定版 與 "System.Configuration.ConfigurationManager"

 
 安裝好後就可以於專案內使用Lucene套件囉!
再來依照上一篇的教學建立一套簡單的Lucene查詢

using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;

var _dir = new DirectoryInfo("LuceneDocument");
if (!File.Exists(_dir.FullName))
{
    System.IO.Directory.CreateDirectory(_dir.FullName);
}
var analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_CURRENT);
CreateIndex(GetProductsInformation(), _dir, analyzer);

while (true)
{
    Console.Write("請輸入欲查詢字串 :");
    var searchValue = Console.ReadLine();
    Search(searchValue, _dir, analyzer);
}

void CreateIndex(List<Product> information, DirectoryInfo dir, StandardAnalyzer analyzer)
{
    using (var directory = FSDirectory.Open(dir))
    {
        using (var indexWriter = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED))
        {
            foreach (var index in information)
            {
                var document = new Document();
                document.Add(new Field("Id", index.Id.ToString(), Field.Store.YES, Field.Index.NO));
                document.Add(new Field("Name", index.Name, Field.Store.YES, Field.Index.ANALYZED));
                document.Add(new Field("Description", index.Description, Field.Store.YES, Field.Index.ANALYZED));
                indexWriter.AddDocument(document);
            }
            indexWriter.Optimize();
            indexWriter.Commit();
        }
    }
}
void Search(string searchValue, DirectoryInfo dir, StandardAnalyzer analyzer)
{
    using (var directory = FSDirectory.Open(_dir))
    {
        var parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_CURRENT, "Description", analyzer).Parse(searchValue);
        using (var indexSearcher = new IndexSearcher(directory))
        {
            var queryLimit = 20;
            var hits = indexSearcher.Search(parser, queryLimit);
            if (!hits.ScoreDocs.Any())
            {
                Console.WriteLine("查無相關結果。");
                return;
            }
            Document doc;
            foreach (var hit in hits.ScoreDocs)
            {
                doc = indexSearcher.Doc(hit.Doc);
                Console.WriteLine("Score :" + hit.Score + ", Id :" + doc.Get("Id") + ", Name :" + doc.Get("Name") + ", Description :" + doc.Get("Description"));
            }
        }
    }
}

List<Product> GetProductsInformation()
{
    return new List<Product> {
            new Product{ Id = 1, Name = "蘋果", Description = "一天一蘋果,醫生遠離我。"},
            new Product{ Id = 2, Name = "橘子", Description = "醫生給娜美最珍貴的寶藏。"},
            new Product{ Id = 3, Name = "梨子", Description = "我是梨子,比蘋果好吃多囉!"},
            new Product{ Id = 4, Name = "葡萄", Description = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮"},
            new Product{ Id = 5, Name = "榴槤", Description = "水果界的珍寶!好吃一直吃。"}
        };
}
class Product
{
    public long Id { get; set; }
    public string Name { get; set; } = null!;
    public string Description { get; set; } = null!;
}


好囉! 接下來我們要如何更新索引呢?
更新其實就是將存在的索引刪除並重新建立Document,不存在的則直接新增。
首先準備一組資料準備更新

List<Product> GetUpdateProductsInformation()
{
    return new List<Product>
    {
        new Product{ Id = 6, Name = "香蕉", Description = "運動完後吃根香蕉補充養分。"},
        new Product{ Id = 2, Name = "橘子", Description = "橘子跟柳丁你分得出來嗎?"}
    };
}


*欲更新的Document必須與創建所引時使用的Document欄位相同*

void Update(string key, List<Product> information, DirectoryInfo dir, StandardAnalyzer analyzer)
{
    using( var directory = FSDirectory.Open(dir))
    {
        using(var indexWriter = new IndexWriter(directory, analyzer, false, IndexWriter.MaxFieldLength.LIMITED))
        {
            foreach (var index in information)
            {
                var document = new Document();
                document.Add(new Field("Id", index.Id.ToString(), Field.Store.YES, Field.Index.NO));
                document.Add(new Field("Name", index.Name, Field.Store.YES, Field.Index.ANALYZED));
                document.Add(new Field("Description", index.Description, Field.Store.YES, Field.Index.ANALYZED));
                indexWriter.UpdateDocument(new Term("Name", key) ,document);
            }
        }
    }
}


來測試看看
   
可以看見 Name = 橘子 的索引已經改為我們新準備的資料囉。
再來是刪除!
與更新非常相似,只需要使用deleteDocument()就可以了。

void Delete(string key, DirectoryInfo dir, StandardAnalyzer analyzer)
{
    using (var directory = FSDirectory.Open(dir))
    {
        using (var indexWriter = new IndexWriter(directory, analyzer, false, IndexWriter.MaxFieldLength.LIMITED))
        {
            indexWriter.DeleteDocuments(new Term("Name", key));
            indexWriter.Optimize();
            indexWriter.Commit();
        }
    }
}


再來看看輸出結果
 可以發現 Score :0.7554128, Id :2, Name :橘子, Description :醫生給娜美最珍貴的寶藏。這筆索引已經被移除囉!
  
可以發現筆者於更新或刪除時都是輸入單一字來做異動,除了表達可以對索引做複合更動外,
是因為更新與刪除索引同樣會使用到分詞器(analyzer),
*所輸入的索引值非ID等數值時必須要配合分詞器的分詞能力*才能取得所想異動的索引喔!


Boost是什麼呢?
Boost 分為 :
1. Index Time Boost : 在建立索引時就計算好的值。例如上一篇中提到的(NORMS)
2. Query Time Boost : 查詢時賦與搜尋條件不同的值以影響結果。
我們先來測試Index Time Boost的部分
void CreateIndexWithBoost(List<Product> information, DirectoryInfo dir, StandardAnalyzer analyzer)
{
    using (var directory = FSDirectory.Open(dir))
    {
        using (var indexWriter = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED))
        {
            foreach (var index in information)
            {
                var document = new Document();
                document.Add(new Field("Id", index.Id.ToString(), Field.Store.YES, Field.Index.NO));
                document.Add(new Field("Name", index.Name, Field.Store.YES, Field.Index.ANALYZED));
                document.Add(new Field("Description", index.Description, Field.Store.YES, Field.Index.ANALYZED));
                document.GetField("Name").Boost = 1.5F;
                document.GetField("Description").Boost = 0.5F;

                indexWriter.AddDocument(document);
            }
            indexWriter.Optimize();
            indexWriter.Commit();
        }
    }
}


並記得重新CreateIndex才能刷新欄位的權重值喔。


很明顯的搜尋出來的Score分數變動了! 但是有沒有發現明明Name欄位的Boost改成了1.5,蘋果的數值卻仍然只有一半呢?
這是因為我們的Search中所參照的欄位為Description,所以在計算Score的時候其實是完全沒有參與的喔!
另外要記得,使用Index Time Boost的時候,欲給予銓重分配的欄位Field.Index不能使用NO_NORMS,不然這個欄位並不會紀錄權重的資料。

再來我們試試看Query Time Boost
void SearchWithBoost(string searchValue, DirectoryInfo dir, StandardAnalyzer analyzer)
{
    using (var directory = FSDirectory.Open(_dir))
    {
        using (var indexSearcher = new IndexSearcher(directory))
        {
            var query = new QueryParser(Lucene.Net.Util.Version.LUCENE_CURRENT, "Name", analyzer).Parse(searchValue);
            var query2 = new QueryParser(Lucene.Net.Util.Version.LUCENE_CURRENT, "Description", analyzer).Parse(searchValue);

            query.Boost = 2.0F;
            query2.Boost = 0.5F;

            BooleanQuery booleanQuery = new BooleanQuery();
            booleanQuery.Add(query, Occur.SHOULD);
            booleanQuery.Add(query2, Occur.SHOULD);

            var hits = indexSearcher.Search(booleanQuery, 20);
            if (!hits.ScoreDocs.Any())
            {
                Console.WriteLine("查無相關結果。");
                return;
            }
            Document doc;
            foreach (var hit in hits.ScoreDocs)
            {
                doc = indexSearcher.Doc(hit.Doc);
                Console.WriteLine("Score :" + hit.Score + ", Id :" + doc.Get("Id") + ", Name :" + doc.Get("Name") + ", Description :" + doc.Get("Description"));
            }
        }
    }
}


這次我們搜尋兩個欄位"Name"與"Description",並使用 BooleanQuery來將其組合。
BooleanQuery中的 Occur有三種參數 : "MUST","MUST_NOT","SHOULD",功能與字面上的意思一樣為"必須要有","必須沒有"與"有無都包含"。

 
查詢出來的分數就不一樣囉!

以上就是這一次的分享,Lucene是一款容易入門但是要實際上戰場卻又十分複雜的功能,想要達成真正高效能的全文檢索,在前期的文件規畫配置與資料的權重配比都是一個巨大的挑戰。未來會繼續分享關於Lucene的其他有趣功能,還請繼續期待呦!
另外也可以到GitHub下載我的範例來參考呦!
GitHub: https://github.com/g13579112000/Lucene

參考文件:
1. 黑暗大大的全文檢索筆記 : https://blog.darkthread.net/blog/lucene-net-notes-1/
2. Makble : http://makble.com/lucene-field-boost-example
3. CSDN Jack2013tong 文章 : https://blog.csdn.net/huwei2003/article/details/53408388
More...
梨子, 2022/4/20 下午 09:34:03
IIS 配合 AD (Active Directory) 認証, 使用 .Net 6.0
環境說明:

AD Server: dc1 (192.168.101.109)
PC: pc110 (192.168.101.110)
PC: pc111 (192.168.101.111)

第一步,把 PC 加入 AD, 這個算是基本操作,網路上說明很多, 就不再截圖了。不過在這裡還是遇到了第一個問題,解決過程請參考另一份文件: https://blog.uwinfo.com.tw/Article.aspx?Id=486

第二步,在 Visual Studio 的測試環境中測試:
一開始是使用 .Net 6.0 來實作,沒想到找到的文件都是 .Net Core 3.1 的,所以先用 .Net Core 3.1 實做了一次,後來改用 .Net 6.0 實作才成功。使用 .Net 6.0 實作的過程如下:

1. 建立一個 MVC 的標準專案: 
 
為了避免憑証問題,所以拿掉了 HTTPS 的設定


2. 改寫 launchSettings.json:
iisSettings 中的 windowsAuthentication 改為 True, anonymousAuthentication 改為 false。如下圖:
 
3. 修改 Program.cs, 加入以下四行指令:
builder.Services.AddAuthentication(IISDefaults.AuthenticationScheme);
builder.Services.AddAuthorization();
app.UseAuthentication();
app.UseAuthorization();
(注意: UseAuthentication 要加在 UseAuthentication 之後, VS 2022 應該會提示要新增 using Microsoft.AspNetCore.Server.IISIntegration;)
 
4. 在 HomeController 增加一個 Action, 以讀取驗証資料:

        [Route("GetAuthenticatedUser")]
        [HttpGet("[action]")]
        public IdentityUser GetUser()
        {
            return new IdentityUser()
            {
                Username = User.Identity?.Name,
                IsAuthenticated = User.Identity != null ? User.Identity.IsAuthenticated : false,
                AuthenticationType = User.Identity?.AuthenticationType
            };
        }

        public class IdentityUser
        {
            public string Username { get; set; }
            public bool IsAuthenticated { get; set; }
            public string AuthenticationType { get; set; }
        }
 
5. 啟動時記得要改用 IIS Express (感覺早上花了兩三個小時在為了這個問題打轉):


6. 執行結果:

第三步,在 IIS 中安裝網站:
1. 在安裝 IIS 時,記得要勾選 windows 驗證


2. 安裝 .Net 6.0 的 Hosting Bundle
https://dotnet.microsoft.com/en-us/download/dotnet/6.0
 
3. 新增網站:
主機名稱留空白 (AD 驗証在網域內好像不會使用指定的主機名稱,這個有待後續再做確認)


如果沒有刪除預設網站,會遇到警告,直接確認即可.


要把 Default Web Site 關閉,再啟動測試站
 
要啟動 windows 驗証: 
在 web.config 中增加 
      <security>
        <authentication>
          <anonymousAuthentication enabled="false" />
          <windowsAuthentication enabled="true" />
        </authentication>
      </security>



修改 applicationHost.config:

檔案位置: %windir%\system32\inetsrv\config\applicationHost.config
這兩地方的 Deny 改為 Allow
<section name="anonymousAuthentication" overrideModeDefault="Deny" />
<section name="windowsAuthentication" overrideModeDefault="Deny" />

 
參考文件: https://docs.microsoft.com/zh-tw/iis/get-started/planning-for-security/how-to-use-locking-in-iis-configuration

3. 可以取得登入資訊如下:

4. 從 Domain 中另一台主機來存取,不用登入,自動取得目前登入者的資訊。
 
5. 從非網域主機連線: 會要求認証
 

目前遇到問題: 在網域中的電腦只能用主機名稱登入,非網域的電腦,才能夠使用網址登入。

測試專案下載: https://github.com/bikehsu/AD60
 
More...
Bike, 2022/3/19 下午 09:10:08
|< 123 >|
頁數 1 / 3 下一頁
~ Uwinfo ~