dotnet core Worker ServiceのWindows Service化

dotnet core(.NET 5も含む)のWorker ServiceをWindowsのServiceとして動作させる例を見つけたのでメモ。

Worker Serviceは下記コマンドで作成する。

dotnet new worker -o <プロジェクト名>

通常は、上記によって作成されたWorker.csに処理を付け加えればOKなのだが、Windows Service化させたい場合は、プロジェクトにMicrosoft.Extensions.Hosting.WindowsServicesパッケージを追加する必要がある。

dotnet add <プロジェクト名> package Microsoft.Extensions.Hosting.WindowsServices

さらに、Program.cs中のCreateHostBuilderに.UseWindowsService()を追加する。

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<Worker>();
        });
}

後は、Worker.csに処理を追加して、デバッグ後、下記コマンドでサービス登録を行う。(これはWindowsのコマンド)

sc create <サービス名> binPath= 実行ファイルパス [オプション...]

登録が済めば、MMCコンソールの「コンピューターの管理」-「サービスとアプリケーション」-「サービス」からも起動・停止や設定変更が可能。

単純なサービスの開始や停止は下記コマンドでも操作可能。

sc start <サービス名>
sc stop <サービス名>

.NET Framework時代のサービス作成より楽だね。

カテゴリー: .NET, C#, dotnetcore, 技術系 | コメントする

.NET 5のターゲットフレームワーク名

現地時間11/10 .NET 5がリリースされた。

.NET 5では、フレームワーク名にプラットフォームを指定出来るようになった。プロジェクト種別によっては、プラットフォームを指定しないとエラーになるものもある。なお、ターゲットフレームワークは;で区切ることにより複数指定が可能である。

例えば、Windows Formsプロジェクトでは、単なるnet5.0の指定だけでは、プラットフォームエラーが出てBuildに失敗する。.NET 5で作成したプロジェクトでは、問題無いが、.net core 3.1で作成したプロジェクトを.NET 5にアップグレードする場合等には注意が必要だ。

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

また、プラットフォームにOSやOSバージョンを指定する事により、プログラムから、それらの値を取得することも可能。

.NET 5でサポートされている、プラットフォームは以下のようになっている。

プラットフォームバージョン指定可能
net5.0-androidYes
net5.0-iosYes
net5.0-macosYes
net5.0-tvosYes
net5.0-watchosYes
net5.0-windowsNo

※プロジェクトがクロスプラットフォームの場合は単にnet5.0で良い。

カテゴリー: .NET, C#, 技術系 | コメントする

EF CoreによるDBからのEntity作成(Scaffolding)

以前、少し書いたと思うが、Entity Framework Coreを使用して、既存のDBからDbContextクラスとEntityクラスを作成する事が可能である。

例えば以下のようなテーブルを含むDBがあった場合、どのようなEntityおよびDbContextとなるか試してみよう。

データベース(SQLite)

CREATE TABLE Person(
  Id int atuoincrement,
  Name nvarchar(50),
  primary key (Id)
);
CREATE TABLE MailAddress(
  Address nvarchar(100),
  DisplayName nvarchar(50),
  PersonId int,
  primary key(Address),
  foreign key(PersonId) references Person(Id)
);

プロジェクトには下記のパッケージを追加する

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameoworkCore.Design
  • Microsoft.EntityFrameworkCore.Sqlite
  • Microsoft.EntityFrameowrkCore.Sqlite.Design

下記コマンドを実行して、DbContextとEntityを作成
(scaffoldのパラメータは「接続文字列」と「データプロバイダ」)

$ dotnet ef dbcontext scaffold "Data Source=SQLiteTest.db" Microsoft.EntityFrameworkCore.Sqlite

出来上がったDbContextとEntity

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

namespace SQLiteTest
{
    public partial class SQLiteTestContext : DbContext
    {
        public SQLiteTestContext()
        {
        }

        public SQLiteTestContext(DbContextOptions<SQLiteTestContext> options)
            : base(options)
        {
        }

        public virtual DbSet<MailAddress> MailAddress { get; set; }
        public virtual DbSet<Person> Person { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
                optionsBuilder.UseSqlite("Data Source=SQLiteTest.db");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<MailAddress>(entity =>
            {
                entity.HasKey(e => e.Address);

                entity.Property(e => e.Address).HasColumnType("nvarchar(100)");

                entity.Property(e => e.DisplayName).HasColumnType("nvarchar(50)");

                entity.Property(e => e.PersonId).HasColumnType("int");

                entity.HasOne(d => d.Person)
                    .WithMany(p => p.MailAddress)
                    .HasForeignKey(d => d.PersonId);
            });

            modelBuilder.Entity<Person>(entity =>
            {
                entity.Property(e => e.Id)
                    .HasColumnType("int atuoincrement")
                    .ValueGeneratedNever();

                entity.Property(e => e.Name).HasColumnType("nvarchar(50)");
            });

            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
}
using System;
using System.Collections.Generic;

namespace SQLiteTest
{
    public partial class Person
    {
        public Person()
        {
            MailAddress = new HashSet<MailAddress>();
        }

        public long Id { get; set; }
        public string Name { get; set; }

        public virtual ICollection<MailAddress> MailAddress { get; set; }
    }
}
using System;
using System.Collections.Generic;

namespace SQLiteTest
{
    public partial class MailAddress
    {
        public string Address { get; set; }
        public string DisplayName { get; set; }
        public long? PersonId { get; set; }

        public virtual Person Person { get; set; }
    }
}

とまあ、こんな感じでDBからDbContextとEntityを作成することができる。
接続文字列が埋め込みになっているので、この部分はappsettings.json等から取得するように変更した方が良いだろう。

なお、scaffoldのオプションで、scaffoldするテーブルを指定したり、出力先ディレクトリを指定することも可能。

カテゴリー: C#, dotnetcore, Entity Framework, 技術系 | コメントする

Windows TerminalでSSH接続プロファイルを作成してみる

Windows Terminalとは、Mircosoft Storeで入手できる、Microsoft社製の無料のTerminalアプリケーション。

インストールすると、デフォルトで、Powershellとコマンドプロンプトおよび、wslのディストリビューションがインストールされていれば、そのプロファイルが自動追加され、メニューより選択してコンソールを開く事が可能となる。

ちなみに、メニューから「設定」を選ぶと、所謂設定画面ではなく、jsonファイルが拡張子が関連づけられたエディタで開く。このファイルを編集することにより、プロファイル毎に背景色を変更したりすることが可能である。

ただ、自動的に設定されるのは、ローカルリソースのみなので、手動にてSSHでリモートに接続するプロファイルを作成してみた。

Windows10では2017年10月のFall Creators Updateからsshクライアントが標準でサポートされているので、新しいプロファイルを作成して、起動コマンドとして、”commandline”にsshを指定してあげれば良い。

{
    "$schema": "https://aka.ms/terminal-profiles-schema",
    "alwaysShowTabs" : true,
	・・・
    "profiles" : 
    [
		・・・
        {
            "acrylicOpacity" : 0.8,
            "background" : "#102040",
            "closeOnExit" : true,
            "colorScheme" : "Campbell",
            "commandline" : "ssh <username>@<host>",←SSHでリモートホストに接続
            "cursorColor" : "#FFFFFF",
            "cursorShape" : "bar",
            "fontFace" : "Consolas",
            "fontSize" : 16,
            "guid" : "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}",←GUIDジェネレータ等で取得
            "historySize" : 9001,
            "icon" : "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png",
            "name" : "SSH-Connect(<host>)",
            "padding" : "0, 0, 0, 0",
            "snapOnInput" : true,
            "useAcrylic" : true
        },
		・・・
	]
	・・・
}

(←・・・の部分は実際には書いてはいけない)

上記を保存して、メニューからそのプロファイルを選択してみる。

見事ログインできましたとさ。

今までは、Teratermを使っていたけれど、このアプリ1個開いておけばCMDやPowershellなどとの操作切替が楽なので、今後はこれを使おうかな。

ちなみに、同じプロファイルのタブを複数開くことも可能です。

カテゴリー: Windows, 技術系, 開発環境 | コメントする

asp.net coreでのセッション変数

asp.net coreではセッション等は基本的にはサーバー上で管理しない。

asp.netのセッション変数をasp.net coreでほぼ同様に使用するには、Microsoft.AspNetCore.Sessionを使用する。

Microsoft.AspNetCore.Sessionでは、Cookieを使用して、セッション情報を保持する。

セッションをaps.net coreで使用するためには、StartupとConfigureに以下のコードを追加する必要がある。

public void ConfigureServices(IServiceCollection services)
{
	// 分散キャッシュの指定(アプリのインスタンス内で有効)
    services.AddDistributedMemoryCache();
	// セッションサービスの追加
    services.AddSession(opt=>{
		// オプション指定
        opt.IdleTimeout = TimeSpan.FromMinutes(20);
        opt.Cookie.IsEssential = true;
    });
	・・・
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	・・・
    app.UseRouting();

    app.UseAuthorization();

	// セッションを使用
    app.UseSession();
	・・・
}

セッション変数に値をセットしたり、取得するには、暗黙で宣言されている、HttpContext変数を使用する。

// セッション変数へ値をセット(文字列)
HttpContext.Session.SetString("MyString","Hello");
// セッション変数へ値をセット(整数)
HttpContext.Session.SetInt32("MyInt",123);

// セッション変数から値を取得(文字列)
string mystring = HttpContext.Session.GetString("MyString");
// セッション変数から値を取得(整数)
int myint = HttpContext.Session.GetInt32("MyInt");

// クラスインスタンス等を保存するには、Json等でシリアライズする必要がある
class MyClass {
	public string MyString { get; set; }
	public int MyInt { get; set; }
}
・・・
MyClass c = new MyClass() { MyString="Hello", MyInt = 123 };
// JSON文字列へ変換
string sessvalue = JsonSerializer.Serialize(c);
// セッション変数へ文字列として保存
HttpContext.Session.SetString("MyClassInsance", sessvalue);
・・・
// 文字列からインスタンスへデシリアライズ
MyClass cc = JsonSerializer.Deserialize<MyClass>(HttpContext.Session.GetString("MyClassInstance"));

以下のような拡張メソッドを作っても良い。

public static class SessionExtention {
    public static T Get<T>(this ISession session, string key) {
        string val = session.GetString(key);
        return val == null ? default : JsonSerializer.Deserialize<T>(val);
    }
    public static void Set<T>(this ISession session, string key, T val) {
        string jsonstr = JsonSerializer.Serialize(val);
        session.SetString(key,jsonstr);
    }
}

と、こんな感じでセッション変数を扱うことが可能。

カテゴリー: asp.net core, C#, 技術系 | コメントする

Microsoft Graphを使用して、ユーザがグループに属しているかどうか調べる

うちの社内アプリの認証方法は今までバラバラで、メンテナンスとか非常に大変だったので、今後はAzure ADに統一していきたいなと思っている今日この頃。

とりあえず、Microsoft Graph APIを使用して、認証とアクセス権チェック(指定グループに属しているかどうかで判断する)を試してみた。

dotnet core 3.1コンソールアプリで実装。
まず、プロジェクトにMicrosoft.Graph,Microsoft.Graph.Auth,Microsoft.Identity.Clientパッケージを追加する。

Webアプリではないので、今回は前回と同様、ユーザ名・パスワード指定でTokenを取得する。(実際の取得はGraph API内部で行う)

IPublicClientApplication app = PublicClientApplicationBuilder
    .Create(ApplicationID)
    .WithAuthority(Authority)
    .Build();
// Scopeはユーザ情報の読取とグループ情報の読取があればいいかな
string[] scopes = new string[] { "https://graph.microsoft.com/User.Read", "https://graph.microsoft.com/Group.Read.All" };

// ユーザ名・パスワードでアクセスするので、Microsoft.Graph.Auth.UserNamePasswordProviderを使用する
var provider = new UsernamePasswordProvider(app,scopes);

// Microsoft.GraphGraphServiceClientを作成
var graphClient = new GraphServiceClient(provider);

// ログインユーザの情報を取得する
var me = await graphClient.Me.Request()
    .WithUsernamePassword(username,secPwd)
    .GetAsync();

// ログインユーザが属している、グループを取得する
var members = await graphClient.Me.MemberOf.Request().GetAsync();
if (members != null) {
    foreach(var m in members) {
        if (m.GetType() == typeof(Group)) {
            Group g = (Group)m;
            Console.WriteLine($"{g.Id} : {g.DisplayName}");
        }
    }
}

MemberOfは自分が属しているグループの一覧なので、このグループが他のグループに属している場合、そのグループに属していることはこの一覧からは確認できない。

つまり、以下のような状態で、自分が最終的にAグループに属しているかどうかはこの一覧からは分からないということ。MemberOfで取得されるのは、BグループとCグループとなるからだ。

こういうグループの入れ子は配布リストなどには良く見られるよね。セキュリティグループはこういう設計をしない方が良いとは思うけど、場合によりけりだろうか・・・

で、自分がAグループに属しているかどうかを調べるには、グループを手繰っていく必要がある。効率はともかくとして、とりあえず以下のようなメソッドを作ってみた。

// グループを手繰って、指定グループに属しているかどうか調べる
// 最初に指定するグループが指定グループ
static async Task<bool> IsMember(Group g,string mail,GraphServiceClient graphClient,string username, SecureString secPwd) {
    foreach(var m in g.Members) {
        if (m.GetType() == typeof(User)) { // メンバーがユーザならメールアドレスをチェック
            User u = (User)m;
            if (u.Mail == mail) {
                return true;
            }
        } else if (m.GetType() == typeof(Group)) { // グループならグループ内をチェック
            Group sg = (Group)m;
            var grps = await graphClient.Groups.Request()
                .WithUsernamePassword(username,secPwd)
                .Expand("Members")	// これを入れないと、Membersが取れない
                .Filter($"id eq '{sg.Id}'")	// IDを指定
                .GetAsync();
            var grp = grps.FirstOrDefault();
            if (grp != null) {
				// 再帰呼出し
                return await IsMember(grp,mail,graphClient,username,secPwd);
            } else {
                return false;
            }
        }
    }
    return false;
}

グループ深度が深くなれば、何回もhttpリクエストが発生するので、効率的ではないとは思うけど・・・
まぁ、結果はOKでした。

カテゴリー: C#, Microsoft Graph, OAuth2, 技術系 | コメントする

MailKitとOAuth2を使ったOffice365へのPOP3アクセス

以前、Office365とPOP3プロトコルを使用した、自社用安否確認システムを作成したのだが、今年の10月以降、Office365にPOPアクセスするのに基本認証方式が使えなくなるとのアナウンスが去年辺りからあり、Office365のOAuth2による認証を試験していたのだが、どうもうまく行かなくて、結構悩んだのだが、単純にSCOPEの指定の仕方だったOrz

Office365用Azure AD管理ポータルで、アプリケーションを登録して、Microsoft GraphへPOP.AccessAsUser.Allアクセス許可を設定しているのだが、このプロパティを見ると、

“https://graph.microsoft.com/POP.AccessAsUser.All”

となっている。なので、SCOPEにも当然これを指定するのだろうと思って指定していたのだが、これが間違いの元。
アクセストークンは取得できるのだが、認証時に”Authentication failed.”のエラーが出て、POP3ログインに失敗する・・・

で、実際に指定するSCOPEは以下のようにしなければいけないのであった。

“https://outlook.office365.com/POP.AccessAsUser.All”

確かに、サンプルとか見ると、そうなっていました・・・
こんな事で結構ずーっと悩んでたんだよね・・・
で、自社用の安否確認システムはWindowsサービスで動作させているので、Office365のログインダイアログを出す事は出来ない。
そこで、あまりお薦めできないけれど、一番簡単な、ユーザ名とパスワードを使用した形で、アクセストークンを取得することにした。
以下のような形でアクセストークンの取得が可能。

// OAuth2アクセストークン取得
IPublicClientApplication app = PublicClientApplicationBuilder
    .Create(ApplicationID) // アプリケーション(クライアント) IDの事
    .WithAuthority(Authority) // https://login.microsoftonline.com/ + テナントID
    .Build();
string[] scopes = new string[] { "https://outlook.office365.com/POP.AccessAsUser.All" };
SecureString secPass = new SecureString();
・・・
// 指定するのはOffice365にログインに使用するユーザー名とパスワード
AuthenticationResult res = await app.AcquireTokenByUsernamePassword(scopes, Pop3User, secPass).ExecuteAsync();

↑のスコープをずっと間違えていたんだよね・・・でも、この時点ではエラー出ないんだよ。

で、後はMailKit.Net.Pop3Clientでの認証用のOAuth2の情報を作成して、認証を行えばPOP3で、Office365(Exchange Online)からメールを取得できる。

// OAuth2用の認証機構を作成
var oauth2 = new SaslMechanismOAuth2(Pop3User, res.AccessToken);

Pop3Client cli = new Pop3Client();
// POP3サーバー接続
await cli.ConnectAsync(Pop3Host, Pop3Port, Pop3Security);
// 認証
await cli.AuthenticateAsync(oauth2);
int count = await cli.GetMessageCountAsync();
if (count != 0)
{
	// POP3からメールの受信
    var msgs = await cli.GetMessagesAsync(0, count);
    foreach (var msg in msgs)
    {
		・・・

いや~、くだらないとこで引っかかってました・・・

追記:
このサンプルで、Office365のAOuth2アクセストークンを得るには、Micrsoft.Identity.Clientパッケージが必要。

カテゴリー: C#, OAuth2, 技術系 | コメントする

WSLをWSL2に更新してみた

Windows 10 2004がリリースされ、WSL2が利用可能になったので、WSLのUbuntu LinuxをWSL2に変更してみた。

まず、このページのリンクから、wsl_update_x64.msiをダウンロードして実行。

その後、コマンドプロンプト等で、wslコマンドを使用して、WSLのバージョンを変更する。
まず、下記コマンドを実行して現在のバージョンを見てみよう。

C:\>wsl -l -v
  NAME                  STATE           VERSION
* openSUSE-Leap-15-1    Running         1
  Ubuntu                Running         1

次に、バージョンを2に変更したいディストリビューションを指定して、下記コマンドを実行する。今回はUbuntuをバージョン2にしてみる。

C:\>wsl Ubuntu --set-version 2

実行が終わると、指定したディストリビューションはWSL2で動作するようになる。

WSL2に変換後、使用してみて、ちょっと困った点がいくつか。

まず、WSL2はVMとして動作するので、今までは、Windowsと同じIPで動作していたものが、別IPとなる点。(仮想アダプタが作成され、Windows側とWSL側に別IPが振られる)

これは、WSL上のプログラムとWindowsのプログラムでポートがバッティングしなくなるので悪い点ばかりではないが、例えば、WSL上でasp.dotnet coreのアプリをdotnet run等で動かしてWindows側からブラウザでテストしようとした場合、launchsettings.jsonのIPを直さないと、Windows側からアクセスできない。
(デフォルトではhttps://localhost:5001;http://localhost:5000が指定されているため。)

後、デフォルト設定では名前解決が出来ない場合がある。これは、/etc/resolv.confの内容がクライアント(Windows)の仮想IPアドレスに設定されているので、Windows側に名前解決機能がないと、ホスト名等が解決されず、dotnet new等も失敗する。
(nuget.orgが解決できないため)

そういう場合は、/etc/resolv.confの内容をクライアント(Windows)のDNSサーバ設定に合わせる必要がある。
私の場合は以下のようなアドレスが振られていた。

イーサネット アダプター vEthernet (WSL):

   接続固有の DNS サフィックス . . . . .:
   リンクローカル IPv6 アドレス. . . . .: fe80::bdb1:e7a4:4e3:46c4%45
   IPv4 アドレス . . . . . . . . . . . .: 172.23.240.1
   サブネット マスク . . . . . . . . . .: 255.255.240.0
   デフォルト ゲートウェイ . . . . . . .:
eth0      Link encap:Ethernet  HWaddr 00:15:5d:5d:24:31
          inet addr:172.23.251.65  Bcast:172.23.255.255  Mask:255.255.240.0
          inet6 addr: fe80::215:5dff:fe5d:2431/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12294 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2691 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:15734461 (15.7 MB)  TX bytes:6991441 (6.9 MB)
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /e
tc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.23.240.1 # ←Windows側のIPアドレスが設定されている

速度に関してはVMになったことで、動作が遅くなるかと心配したが、普通に使う分には問題なさそうだ。

ちなみに、複数のディストリビューションをWSL2にしてみたところ、全て同じVMを共有しているようだ。

カテゴリー: Windows, 技術系 | 2件のコメント

Blazor WASMが正式リリースへ!

asp.net core blazor wasm版が正式リリースされた!!

とりあえず、以前から気になっていた、既存のSignalR Hubへの接続を試してみた。
Preview版では、接続できなかったので・・・

で、結論から言うと、接続できました!!

単純にServer Side版からrazorファイルと関連ファイルをコピーしただけで問題無く動作。

RC版では試していなかったので、リリース版からかどうかは不明だけど、とりあえず、既存のHUBに接続できるようになったのはうれしい限り。

正式リリースされたことにより、仕事にも使えるようになるね。

カテゴリー: asp.net core, Blazor, dotnetcore, SignalR, 技術系 | コメントする

EF CoreでSQL Serverのフルテキストインデックスを使う

Entity Framework CoreでSQL Serverのフルテキストインデックスを使用した検索を使ったアプリを作成してみた。

SQL Serverのフルテキストインデックスは、対象テーブルの行に対して、ユニークキーを指定しなければならない。例えば以下のようなテーブル構成にしておく必要がある。

検索結果として、このキーだけを取得するには、CONTAINSTABLE関数で、テーブルとカラム、検索文字列を指定すれば良い。上記構成で、対象テーブルのカラムデータ中から指定文字列を持つ行を取得するには、以下のようなSQLを発行する。

select * from BusinessHistoryDetail d where d.FullTextUniqKey
                in (select [KEY] from CONTAINSTABLE(BusinessHistoryDetail,*,'"C#*"'))

これにより、全てのカラムを対象に指定したキーワードを含む、レコードのユニークキーを取得する事が可能である。

EF Coreで生SQLを発行した結果をLINQクエリに返すにはFromSqlRawメソッドを使用する。上記の例で、キーワード検索結果をベースとして、氏名なども結果として取りたい場合は、以下のような感じのクエリを発行すればよい。

// 検索キーワードはstrkeywに入力されている
// フルテキストインデックス検索のキーワードは"<キーワード>*"の形で指定
string keyw = @"""" + strkeyw + "*"""; 
var q = from d in ctx.BusinessHistoryDetail.FromSqlRaw(
    @"select * from BusinessHistoryDetail d 
        where d.FullTextUniqKey
        in (select [KEY] from CONTAINSTABLE(BusinessHistoryDetail,*,{0}))",keyw)
    join h in ctx.BusinessHistoryHeader on d.EmployeeNo equals h.EmployeeNo
    select new {
        EmpNo = d.EmployeeNo,
        Name = h.Name,
        Project = d.ProjectName,
        Customer = d.CustomerName,
        OS = d.OS,
        Language = d.Language,
        Tools = d.Tools
    };

こんな感じでフルテキスト検索を行う事ができた。

なぜ、フルテキスト検索をEF Coreでやりたかったのかというと、以前作成した社内システムをdotnet coreに置き換えたいから。
以前のシステムでもフルテキスト検索を行っているが、その部分だけ、EFクエリではなく、SqlClientを使わなければならなかったため。
「EFだけで出来ればいいのになぁ」と思っていたので。

まぁ、EF使ってるのにSQLをプログラムに直書きするのは如何な物かとも思うけど・・・

カテゴリー: C#, dotnetcore, Entity Framework, 技術系 | コメントする