2021年11月25日木曜日

[SQLServer]エラー「プールから接続を取得する前にタイムアウト期間が過ぎました。プールされた接続がすべて使用中で、プール サイズの制限値に達した可能性があります」

ある時いきなり以下のエラーが出て、その後同じアプリケーションプールを使っているシステムがすべてタイムアウトになった

プールされた接続がすべて使用中で、プール サイズの制限値に達した可能性があります


調べると 1つのアプリケーションプールがDBに接続する際に使用するプール数MAXはデフォルト100。 
これを超えた場合、上記のような現象になる。 
プール数が異常に溜まる場合は 基本的にはプログラムが原因で、
きちんとDBをクローズしないようなプログラムを書いていると 処理が終わった後もプールが4分~10分残り続ける。 
その間に溜まりに溜まって100以上になるとこの現象になる。 

プログラムの原因は別途調べるとして
ひとまず現状を回復したい場合は web.configにてMaxPool数をデフォルトの100から200等にUPすればよい。
デメリットとしては永遠に増え続けた場合。メモリを大量に使うことになる懸念がある。
<add name="Constr" connectionString="Server=XXXX;database=DBName;Trusted_connection=true;pooling=true;Max Pool Size=200;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />



現在のプール数を調べるSQL文は以下(sa権限が必要)
--①hostname内訳
SELECT DB_NAME(sP.dbid) AS the_database,hostname
, COUNT(sP.spid) AS total_database_connections
FROM sys.sysprocesses sP
--where DB_NAME(dbid) = 'XXXXDB' --DB名で絞りたい場合
--and hostname in ('webサーバ名で絞りたい場合')
GROUP BY DB_NAME(sP.dbid),hostname
ORDER BY 1;


--②DB別のsleepコネクション数の分布をチェック
SELECT spid,DB_NAME(sP.dbid) AS the_database , hostname AS host, *
FROM sys.sysprocesses sP
--WHERE DB_NAME(sP.dbid) = 'XXXXDB' --DB名で絞りたい場合
--and hostname in ('webサーバ名で絞りたい場合')
WHERE status = 'sleeping'
ORDER BY hostname,login_time;

--③さらに詳細が知りたい場合
Select text,* from master.dbo.sysprocesses
OUTER APPLY sys.dm_exec_sql_text(sql_handle) AS qt
where
--spid = *** -- ②で取得したsession_idをここにセット
spid IN (267,
271,
264,
245,
119
)

上記SQL文で、溜まっているSQLを割り出し、そこから該当のプログラムを探し出すといいかも

2020年10月29日木曜日

[パワポマン]パワポマンコレクション【内勤の女の人】

 使い勝手が良くてコレクション&独自作成している廃止されたパワポマン画像


その3.内勤の女の人











2019年8月8日木曜日

[パワポマン]パワポマンコレクション【工場の人】

使い勝手が良くてコレクション&独自作成している廃止されたパワポマン画像

その2.工場の人








2019年7月29日月曜日

[パワポマン]パワポマンコレクション【サラリーマン】

使い勝手が良くてコレクション&独自作成している廃止されたパワポマン画像

その1.サラリーマン








[C#]LINEWORKS用のJWT認証を行ってメッセージを送る

そのものズバリな記事がなかったので、作成。
C#のLINEのチャットbotの作り方はありますけどLINE WORKSはまだまだ全然ない…
そもそもC#でLINE WORKS作る人少なそう…

①ー1 まずはLINEWORKSとの連携に必要な値の設定
以下の様に値を宣言します。
LINE WORKS Developersで設定した値をセットします。

static int botNo = 9999;
static string apiId = "kfew******Oy";
static string serverId = "afei*******24239e";
static string consumerKey = "WOpe*******elK";
static string privateKey = "-----BEGIN PRIVATE KEY-----MAIEvA******JZhJIZ=-----END PRIVATE KEY-----";

serverId …ServerList(ID固定タイプ)のIDをセット
consumerKey …ServerApiConsumerKeyをセット
privateKey ServerList(ID固定タイプ)の認証キーを1行につなげます。


②ー1 JWT認証を使ってアクセストークンの取得(準備)


Pemキー(privatekey)を.NETで扱うのは難しく、RSAとして使用するには一度xmlに変換しないといけないらしい…これがつらかった。
これを解消する為に、Nugetで「opensslkey」を追加しておきます
(OpensslKey.csというプログラムが出来るだけ)

②ー2 JWT認証を使ってアクセストークンの取得(関数)


OpensslKeyを使って以下の関数を作ります。これでアクセストークンが取得できます。


public string GetAccessToken()
        {
            //JWTトークンの作成=======================================================
            string request_jwt = null;
            //privatekeyRSAとして使用できるように変換
            var pkcs8privatekey = Security.opensslkey.DecodePkcs8PrivateKey(privateKey); 
            var rsa = Security.opensslkey.DecodePrivateKeyInfo(pkcs8privatekey);  
            var key = new Microsoft.IdentityModel.Tokens.RsaSecurityKey(rsa);
            // トークン操作用のクラスを用意
            var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
            // 署名情報クラスを生成
            var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(key, "RS256");
            var descriptor = new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor
            {
                Issuer = serverId,
                SigningCredentials = credentials,
                IssuedAt = DateTime.UtcNow,
                Expires = (DateTime.UtcNow).AddMinutes(10),
            };
            // トークンの生成
            var token = handler.CreateJwtSecurityToken(descriptor);
            // トークンの文字列表現を取得
            var tokenString = handler.WriteToken(token);


            //アクセストークンの取得=======================================================
            //認証URL
            string TOKEN_ENDPOINT = @"https://auth.worksmobile.com/b/" + apiId + "/server/token";
            var client = new WebClient();
            client.Encoding = Encoding.UTF8;
            var content = new NameValueCollection();
            request_jwt = tokenString;
            content["assertion"] = request_jwt;
            content["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer";
            string response = Encoding.UTF8.GetString(client.UploadValues(TOKEN_ENDPOINT, "POST", content));
            var result = JsonConvert.DeserializeObject<Token>(response);           
            return result.AccessToken;
        }






③ー1 送信してみる
content(内容)は省きますが、以下の様に行えば実際に送信できます。


//(obj_contentは各自でModelを作って作成)
 string jsonRequest = JsonConvert.SerializeObject(obj_content);

 StringContent content = new StringContent(jsonRequest);
 content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

 HttpClient client = new HttpClient(); 
 var url = "https://apis.worksmobile.com/"apiId + "/message/sendMessage/v2";
 var token = GetAccessToken();
 var request = new HttpRequestMessage(HttpMethod.Post, url);
 request.Headers.Add("ContentType", "application/json");
 request.Headers.Add("Authorization", "Bearer " + token);
 request.Headers.Add(@"consumerKey", @""consumerKey );

 request.Content = content;
 var response = await client.SendAsync(request);

 //string responseContent = await response.Content.ReadAsStringAsync();




2019年2月15日金曜日

[.NET]コード内Enumだけで宣言したデータでドロップダウンの候補をセット

オリジナルのドロップダウンを作成する場合、宣言部だけで使いまわしたい…
ということで以下。

①Enum宣言
----------------------------------------------

public enum enumHito : byte
 {
            [Display(Name = "太郎")]
            Tarou = 12334,
            [Display(Name = "花子")]
            Hanako = 19483,
}

----------------------------------------------
②宣言したEnumから属性:NameをGetする共通関数を作成しておく。

----------------------------------------------

//属性:Nameを取得
public static class GetAttribute
    {
        public static string Name(object obj)
        {
            var fieldInfo = obj.GetType().GetField(obj.ToString());
            var descriptionAttributes = fieldInfo.GetCustomAttributes(typeof(DisplayAttribute), false) as DisplayAttribute[];
            if (descriptionAttributes != null && descriptionAttributes.Length > 0)
            {
                return descriptionAttributes[0].Name;
            }
            else
            {
                return string.Empty;
            }
        }

    }
----------------------------------------------

③②を使って①情報からドロップダウン候補をデータバインドする。

----------------------------------------------
var data = new List<Tuple<int, string>>();
foreach (enumKenaskuShubetsu row in Enum.GetValues(typeof(enumHito)))
{
  data.Add(new Tuple<int, string>((int)row, GetAttribute.Name(row)));
}
ddlhoge.DataValueField = "item1";
ddlhoge.DataTextField = "item2";
ddlhoge.DataSource = data;

ddlhoge.DataBind();

----------------------------------------------

[.NET]オブジェクト内に同じプロパティがあった場合、同じ値をセットする

①以下のような関数を作成。
------------------------------------------------

/// <summary>
/// 同じプロパティから同じ値をセットします
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="self"></param>
/// <param name="Frommodel"></param>
public static void CopyFromSameProperty<T>(this T self, object Frommodel, List<string> Lstexcept = null)
        {
            PropertyInfo[] propertyInfo = Frommodel.GetType().GetProperties();
            foreach (PropertyInfo p in propertyInfo)
            {
                if (Lstexcept != null && Lstexcept.Contains(p.Name)){ continue; }

                //コピー元
                var arg = p.GetValue(Frommodel, null);
                //コピー先
                PropertyInfo pTo = self.GetType().GetProperty(p.Name);
                if (pTo != null)
                {
                    pTo.SetValue(self, arg);
                }

            }
}
------------------------------------------------
②使い方は以下。
------------------------------------------------
// ベースとなる行
var baseRow = db.T_hoge.Where(d => d.番号 == 1234).FirstOrDefault();
var newRow = new T_hoge();

newRow.CopyFromSameProperty(baseRow); //一度全項目コピーする
------------------------------------------------

※「このプロパティはコピーしたくない!」というものがあるならば
 以下の様にプロパティ名を指定すればOK
newRow.CopyFromSameProperty(baseRow, new List<string> { "番号" }); //一度全項目コピーする