独自ドメインに移行しました

見ての通り独自ドメインに移行しました。
winserver.ne.jpというphpMyAdminすらマトモに動かない劣悪なレンタルサーバーから、苦労してBLOGデータをサルベージし、なんとか形に仕上がって良かった良かった。
なお昔と違ってSSLも安く導入できるようになったので、ついでにSSLにも対応させました。
10年前とは隔世の感があります。

ちなみに移行時にMySQLからSQLiteに変換してバックアップしやすくしようとしましたが、プラグインによってはエラーとなるので諦めました。
結局、MySQL(MariaDB)から逃げるのは困難だというのが結論です。
2年ぐらい前から劣悪レンサバから移転するべくVPSは契約していたのですが、どうにも移行にやる気が出ず、そのままVPSの月額料金をただ無駄に払い続けてました。

1CPU 1GBのVMでも今のところ不都合はないですが、初めての試みなので今年いっぱいは様子見という感じで。

AzureのSQL DATABASEから多層ネストされたJSONを返すREST APIを作る場合、SELECT文だけで間に合ってしまう(Transact-SQL)

たまたまAzureのSQL DATABASE(≒SQL Server)のドキュメントを読んでいたときに気が付いたのですが、最近のSQL Serverってクエリ末尾に”FOR JSON”を付与すればクエリ結果をそのままJSONに出来るんですって!?そんなこと知らずにDapperでガリガリとSQLをパースしてJSONを作っていたとか馬鹿丸出しですねワタシ。パトラッシュ・・次から頑張るよ・・・・。

ということでSQLだけで多層ネストJSONが出来るまでを検証しました。以下にその方法を示します。

準備:Azure上にサンプルDBを作成 (テンプレートの”AdventureWorkLT”を利用)

このサンプルデータを利用して「セールスの存在する顧客別にオーダーとそのオーダー詳細を表示する」という3層ネスト構造を再現します。

SQLで表現するとこんな感じです。

SELECT  CUST.CustomerID, 
        CUST.FirstName, 
        CUST.LastName, 
        CUST.Phone,
        SHDR.SalesOrderID, 
        SHDR.OrderDate, 
        SHDR.SubTotal,
        DETL.SalesOrderDetailID,
        DETL.ProductID,
        PRDT.Name,
        DETL.LineTotal
FROM SalesLT.Customer CUST
INNER JOIN SalesLT.SalesOrderHeader SHDR
ON SHDR.CustomerID = CUST.CustomerID
INNER JOIN SalesLT.SalesOrderDetail DETL
ON DETL.SalesOrderID = SHDR.SalesOrderID
INNER JOIN SalesLT.Product PRDT
ON PRDT.ProductID = DETL.ProductID
ORDER BY CUST.CustomerID,SHDR.SalesOrderID,DETL.SalesOrderDetailID

実行するとこんな感じ。

SQL実行結果

いかにもDapperで分割してくださいと言わんばかりの出力結果ですが、今回はDapperは使わずSQL DATABASEの基本機能だけしか使いません。ですのでこのSQLの文末に”FOR JSON AUTO”を付与すればめでたく解決・・・ではなく、ここからネストさせたい階層ごとにサブクエリを定義していく必要があります。それをストアドファンクション化したのが以下となります。

CREATE OR ALTER FUNCTION GetSalesInfoByValidCustomer() 
RETURNS nvarchar(MAX)
AS
BEGIN
	DECLARE @json nvarchar(MAX)
	SET @json = ( 
		SELECT	CUST.CustomerID, 
				CUST.FirstName, 
				CUST.LastName, 
				CUST.Phone,
				(
					SELECT
					SHDR.SalesOrderID, 
					SHDR.OrderDate, 
					SHDR.SubTotal
				   ,(
						SELECT 
							DETL.SalesOrderDetailID,
							DETL.ProductID,
							PRDT.Name,
							DETL.LineTotal
	   					FROM SalesLT.SalesOrderDetail DETL
						INNER JOIN SalesLT.Product PRDT
						ON PRDT.ProductID = DETL.ProductID
						WHERE DETL.SalesOrderID = SHDR.SalesOrderID
						ORDER BY DETL.SalesOrderDetailID
						FOR JSON PATH
				   ) AS DETAIL
					FROM SalesLT.SalesOrderHeader SHDR
					WHERE SHDR.CustomerID = CUST.CustomerID
					ORDER BY SHDR.SalesOrderID
					FOR JSON PATH
				) AS [ORDERS]
		FROM SalesLT.Customer CUST
		INNER JOIN SalesLT.SalesOrderHeader SHDR
		ON SHDR.CustomerID = CUST.CustomerID
		ORDER BY CUST.CustomerID
		FOR JSON PATH,ROOT('INFO')
	)
	RETURN @json;
END

ポイントは次の通りです。

  • JSONの受けは必ずNVARCHAR(MAX)とする
  • “FOR JSON PATH”とする(PATH付けないとコケる)
  • ROOT(‘INFO’)とすることでルートオブジェクト配列にアクセスしやすくする
  • サブクエリでのエイリアスがJSONでの型名となる

こうして作成したJSONをオンラインパーサーで確認するとちゃんと3層ネスト構造となっていました。

オンラインパーサーでのJSON構造確認結果

あとはこのまま普通にHTTP TriggerやPHPファイルからレスポンスを返してあげればREST APIの作成完了なのです。余談ですが逆にSQLだけでJSONを解析することも可能です。

--JSON⇒テーブル化用クエリ
DECLARE @json nvarchar(MAX)
SET @json= dbo.GetSalesInfoByValidCustomer();
SELECT 
	Level2.CustomerID,
	Level2.FirstName,
	Level2.LastName,
	Level2.Phone,
	Level2.SalesOrderID,
	Level2.OrderDate,
	Level2.SubTotal,
	SalesOrderDetailID,
	ProductID,
	Name ,
	LineTotal 
FROM
(
	SELECT Level1.CustomerID,
		   Level1.FirstName,
		   Level1.LastName,
		   Level1.Phone,
		   SalesOrderID,
		   OrderDate,
		   SubTotal,
		   DETAIL
	FROM (
		SELECT CustomerID,
			   FirstName,
			   LastName,
			   Phone,
			   ORDERS
		FROM OPENJSON(@json,N'strict $.INFO')
		WITH (
		  CustomerID int,
		  FirstName nvarchar(50),
		  LastName nvarchar(50),
		  Phone nvarchar(25),
		  ORDERS nvarchar(MAX) AS JSON
		)
	) Level1
	CROSS APPLY OPENJSON(Level1.ORDERS)
	WITH(
		SalesOrderID int,
		OrderDate datetime,
		SubTotal money,
		DETAIL nvarchar(MAX) AS JSON
	)
) Level2
CROSS APPLY OPENJSON(Level2.DETAIL)
WITH(
	SalesOrderDetailID int,
	ProductID int,
	Name nvarchar(50),
	LineTotal numeric(38,6)
)
;

実はこの逆変換の方が大変でした。ポイントは次の通りです。

  • “CROSS APPLY OPENJSON”を用いてJSON型を手動で展開する
  • 展開したJSONの中に再度JSON型があった場合はさらに展開する
  • OPENJSONのWITHにて型指定する必要がある(型推論ないんかい・・・
  • ”CROSS APPLY”で展開した列名は展開対象のエイリアスとは別となる
  • “CROSS APPLY”しても展開元のエイリアスは呼べる(これになかなか気付かなかった・・・)

今どきのSQLServerってこんな感じでSQLを駆使していけばJSON化もパースもできるんですね。でも楽になった気があまりしないのは気のせいではないかもしれません。正直、慣れてなければ面倒くさいからこれならDapper使うわってのもアリではないでしょうか。あとSQLの見通しが悪くなってるのがかなり保守観点でマイナス。

ですので間違ってもこれらの「SQLだけでREST APIは作れる」という知識だけで 判断してはダメです。
「(言語名)経験○○年の要員がホゲホゲ」なんて考えのマトモにスキル判定も出来ない無能揃いのSES業者が「運用でSQLバリバリやってましたHAHAHA」という定型作業しか経験のない人材を押し込んでくることになりかねないので要注意です。

Azure Cosmos DBにストアドプロシージャからパーティションキーを指定してドキュメントを追加する(C#)

Azure Cosmos DBをSQL(document)で作成すれば普通のSQL文でCRUDできるのではないかと思いましたが、どうやらそうではなく使えるのはSELECT、しかもGROUP BYもできない、トランザクションスコープも適用されないという非常に癖のある代物でした。オールドタイプにはなかなか馴染めないですね。

特にトランザクションスコープは面倒で、ストアドプロシージャを経由すればパーティション単位でトランザクションが保証されるというものです。ちなみにストアドプロシージャの実装方法ははAzureポータル上にてJavaScriptにより定義する必要があります。

仕方がないと諦め、ベタ書きでJavaScriptを書いてストアドプロシージャを作成します。テストデータでは”uniqid”でパーティション化されたコレクションを想定しています。

// SAMPLE STORED PROCEDURE
// ID「InsertDocuments」としてポータル上から登録します
//
// 引数 documents : JSON配列形式の文字列
// 戻値 作成したドキュメントの配列
function InsertDocuments(documents) {
    const collection = getContext().getCollection();
    const collLink = collection.getSelfLink();
    console.log("documents=" + documents +"\n");
    let jsonList;
    try {
        jsonList = JSON.parse(documents);
    } catch(e) {
        console.log(e);
        return null;
    }

    console.log("length=" + jsonList.length +"\n");
    let createdList = [];
    for(let i=0 ; i<jsonList.length ; i++){
        let doc = jsonList[i];

        // Query documents and tolist processed documents.
        let isAccepted = 
            collection.createDocument(
                    collLink, 
                    doc, 
                    function (err, document) {
                if (err) {
                    console.log(err);

                } else {
                    console.log("created " + document.id + "\n");
                    createdList.push(document);
                    if(i==jsonList.length-1){
                        getContext().getResponse().setBody(JSON.stringify(createdList));
                    }
                }
            });
        if (!isAccepted) throw new Error('The query was not accepted by the server.');
    }

    return;
}

次に利用側のコードを書きます。今回、Functions V2(.NET Core)からC#を利用してCosmos DBにアクセスします。

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Newtonsoft.Json.Converters;
using System.Collections.Generic;

namespace FunctionsTest
{
    public static class FuncInsertDocument
    {
        private static readonly string EndpointUrl = "<your endpoint URL>";
        private static readonly string PrimaryKey = "<your primary key>";  //[Read-only Keys]で問題ない
        private static readonly string DataBaseId = "<your DataBase ID>";
        private static readonly string CollectionId = "<your Collection ID>";

        [FunctionName("FuncInsertDocument")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            DocumentClient client = new DocumentClient(new Uri(EndpointUrl), PrimaryKey);

            // 書込データ作成
            var partitionKeyValue = "U001";
            var dataList = new List<TestData>();
            dataList.Add(new TestData{
                UniqId = partitionKeyValue,
                SessionId = "SES001",
                Time = new DateTimeOffset(new DateTime(2018,1,15,0,0,0,DateTimeKind.Local))
            });

            // ストアドプロシージャ"InsertDocuments"を実行する
            Uri spUri = UriFactory.CreateStoredProcedureUri(
                DataBaseId,
                CollectionId,
                "InsertDocuments");
            string spParam = JsonConvert.SerializeObject(dataList);
            RequestOptions options = new RequestOptions() {
                EnableScriptLogging = true,
                PartitionKey = new PartitionKey(partitionKeyValue)
            };
            var result = await client.ExecuteStoredProcedureAsync<string>(
              spUri,
              options,
              spParam);

            // クエリ成功時にはスクリプトログを出力
            ActionResult ret = null;
            if (result.Response != null) {
                log.LogInformation(result.ScriptLog);
                ret = new OkObjectResult($"ret=" + result.ScriptLog);
            } else {
                ret = new BadRequestObjectResult("Please pass a name on the query string or in the request body");
            }
            return ret;
        }
    }

    public class TestData {
        [JsonProperty("uniqid")]
        public string UniqId {get;set;}

        [JsonProperty("sessionid")]
        public string SessionId { get; set; }

        [JsonProperty("time")]
        [JsonConverter(typeof(MyCustomDateTimeConverter))]
        public DateTimeOffset Time { get; set; }
    }

    public class MyCustomDateTimeConverter : IsoDateTimeConverter {
        public MyCustomDateTimeConverter() {
            base.DateTimeFormat = "yyyy-MM-dd'Z'HH:mm:ss'Z'";
        }
    }
}

指定したパーティションキー値と投入データの値が一致しない場合はストアドプロシージャの呼び出しは失敗します。よって実際の利用にあたっては投入前にLINQからパーティションキー単位でグループ化を行い、またコレクションに主キー(Primary Keys)設定を行い、ストアドプロシージャ側に主キー重複時の例外処理などを実装する必要があると思います。

なおストアドプロシージャを経由する追加処理は非常に遅く、あくまでもトランザクションを必要とした少量データの更新などにしか使えません。大量データの取込にはBulkExecuterライブラリを利用して一気に追加/更新をやるのが本来望まれる処理のようです(ただしV1限定)。

Dapperのマルチマッピング機能で3層ネストされたリストを取得する(C#)

お久しぶりです。

今年はWindows Embedded Compact 7によるハンディターミナルの開発ばかりやってたのですが、今月からうってかわってAzure Functions V2によるWebアプリ開発になりました。

振れ幅の大きさに自分でも笑ってしまいます。

それはさておきDBへのクエリ結果から配列がネストされた構造のJSONに変換するのに簡単な方法はないものか色々と悩みましたが、あまり簡単な方法はなかったようでエクセレントな解法ではないものの、まあ許容できる範囲かと思ったので検証結果を公開します。

検証には次のような条件を前提としています。

  • JOINするのは3テーブル
  • それぞれのテーブルには単一カラムの主キーがある
  • クエリ解析にはDapperのマルチマッピング機能を利用する

そして以上の条件を踏まえ、Dapper公式サイトのマルチマッピング機能のソースを改変した検証コードが次です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
using Dapper;

namespace ConsoleApp1 {

    class Program {

        public class MtProducts {
            public int ProductID { get; set; }
            public string ProductName { get; set; }
        }
        public class OrderDetail {
            public int OrderDetailID { get; set; }
            public List ProductList { get; set; }
            public int Quantity { get; set; }
        }

        public class Order {
            public int OrderID { get; set; }
            public int CustomerID { get; set; }
            public int EmployeeID { get; set; }
            public DateTime OrderDate { get; set; }
            public int ShipperID { get; set; }
            public List OrderDetails { get; set; }
        }

        /// 

        /// Dapperマルチマッピングの検証
        /// original: https://dapper-tutorial.net/result-multi-mapping
        /// 
        /// 
        static void Main(string[] args) {
            string sql = @"
                    SELECT TOP 10 
	                A.OrderID,
	                A.CustomerID,
	                A.EmployeeID,
	                A.OrderDate,
	                A.ShipperID,
	                B.OrderDetailID,
	                B.Quantity,
	                MP.ProductID,
	                MP.ProductName
                FROM dbo.Orders AS A 
                INNER JOIN OrderDetails AS B 
                ON A.OrderID = B.OrderID
                INNER JOIN MtProducts AS MP
                ON B.ProductID = MP.ProductID
                          ;";

            SqlConnectionStringBuilder cb = new SqlConnectionStringBuilder() {
                DataSource = @"myserver\SQLSERVER,1433",
                InitialCatalog = "MyDbName",
                UserID = "myuser",
                Password = "mypassword",
                IntegratedSecurity = true

            };

            // 出力用リスト
            var orders = new List();

            // ルックアップテーブル代わりの連想配列
            var ordDict = new Dictionary, List>();
            using (var connection = new SqlConnection(cb.ToString())) {

                // Query(sql,func map,param)
                var list = connection.Query(
                    sql: sql,
                    map: (ord, detail, products) => {
                        // 現在のOrderIDとOrderDetailIDが読込済でなければ初期化する
                        var key = new Tuple(ord.OrderID, detail.OrderDetailID);
                        var pBuf = new List();
                        if (!ordDict.TryGetValue(key, out pBuf)) {
                            detail.ProductList = new List();
                            ord.OrderDetails = new List();
                            ord.OrderDetails.Add(detail);
                            ordDict.Add(key, detail.ProductList);
                            if (orders.Find(x => x.OrderID == ord.OrderID) == null)
                                orders.Add(ord);
                        }

                        // 現在のOrderIDとOrderDetailIDにproductsをリスト追加
                        if (ordDict.TryGetValue(key, out pBuf)) {
                            ordDict[key].Add(products);
                        }
                                
                        // 出力用リストを検索し設定がなければ追加、設定済であれば上書
                        var od1 = orders.First(x => x.OrderID == ord.OrderID);
                        if (od1 != null) {
                            var od2 = orders.Find(x => x.OrderID == ord.OrderID).OrderDetails.Find(y => y.OrderDetailID == detail.OrderDetailID);
                            if (od2 == null) {
                                // 追加
                                detail.ProductList = ordDict[key];
                                orders.Find(x => x.OrderID == ord.OrderID).OrderDetails.Add(detail);
                            } else {
                                // 上書
                                detail.ProductList = ordDict[key];
                                orders.Find(x => x.OrderID == ord.OrderID).OrderDetails.Find(y => y.OrderDetailID == detail.OrderDetailID).ProductList = detail.ProductList;
                            }
                        }

                        // この戻り値はダミー
                        return ord;
                    },
                    commandType: null,
                    splitOn: "OrderDetailID,ProductID"
                );

                // 出力用リストを返す
                var s = JsonConvert.SerializeObject(orders);
                Console.Write(s);
            }

        }
    }
}
-- DDLなど
CREATE TABLE [dbo].[Orders](
	[OrderID] [int] NOT NULL,
	[CustomerID] [int] NOT NULL,
	[EmployeeID] [int] NOT NULL,
	[OrderDate] [datetime] NOT NULL,
	[ShipperID] [int] NOT NULL,
 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
	[OrderID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[OrderDetails](
	[OrderDetailID] [int] NOT NULL,
	[OrderID] [int] NOT NULL,
	[ProductID] [int] NOT NULL,
	[Quantity] [int] NOT NULL,
 CONSTRAINT [PK_OrderDetail] PRIMARY KEY CLUSTERED 
(
	[OrderDetailID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[MtProducts](
	[ProductID] [int] NOT NULL,
	[ProductName] [ntext] NULL,
 CONSTRAINT [PK_MtProducts] PRIMARY KEY CLUSTERED 
(
	[ProductID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
INSERT INTO [dbo].[MtProducts]([ProductID],[ProductName]) VALUES (1,'肉');
INSERT INTO [dbo].[MtProducts]([ProductID],[ProductName]) VALUES (2,'魚');
INSERT INTO [dbo].[MtProducts]([ProductID],[ProductName]) VALUES (3,'米');
GO
INSERT INTO [dbo].[Orders]([OrderID],[CustomerID],[EmployeeID],[OrderDate],[ShipperID]) VALUES (10248,90,5,'2018/07/04 12:00:00',3);
INSERT INTO [dbo].[Orders]([OrderID],[CustomerID],[EmployeeID],[OrderDate],[ShipperID]) VALUES (10249,81,6,'2018/07/05 12:00:00',1);
INSERT INTO [dbo].[Orders]([OrderID],[CustomerID],[EmployeeID],[OrderDate],[ShipperID]) VALUES (10250,34,4,'2018/07/06 12:00:00',2);
INSERT INTO [dbo].[Orders]([OrderID],[CustomerID],[EmployeeID],[OrderDate],[ShipperID]) VALUES (10251,84,3,'2018/07/07 12:00:00',1);
GO
INSERT INTO [dbo].[OrderDetails]([OrderDetailID],[OrderID],[ProductID],[Quantity]) VALUES (1,10248,1,12);
INSERT INTO [dbo].[OrderDetails]([OrderDetailID],[OrderID],[ProductID],[Quantity]) VALUES (2,10248,2,10);
INSERT INTO [dbo].[OrderDetails]([OrderDetailID],[OrderID],[ProductID],[Quantity]) VALUES (3,10249,3,5);

上記のコードのポイントは次の通りです。

  • レコードの読込判定にディクショナリを用いる
  • ディクショナリに格納するのは最下層の配列
  • 最終的に出力するリストにひとまず追加し、追加済みであれば上書する

じゃあ3階層を超えたネストはどうすればいいんだ、という疑問については

「3階層目からメソッド分割して別途Query呼び出せばいいんじゃね?」

と思ったりしますが、DapperのQueryがネストして呼び出せるかどうか試していないので断言は出来ません。

それにしてもWordpressを更新したら、前のような記述が出来なくなって、やたら平坦て簡素な書き方を強いられてしまいます。

しかもシンタックスハイライターが動かんのでソースコード見づらいし・・・。

勝手に変えるなよ・・・と思っても、何も考えずよく知らないWordpressを選んだ私の自業自得なのでしょう。

Windows7 32bitでエラーコード9C59が出てIE11が入れられない問題

はじめに

Windows7 32bitを使用していましたが、色々ととち狂ってライセンスをEnterprise版を導入していたせいでWindows10への無料アップグレード期間を逃してしまいました。

そのまま今後の課題として2年ぐらい放置していましたが、どうやらレジストリを弄ってアップグレードインストールさえすればデータはそのままでエディション変更できると知り、今さらながらEnterprise版からUltimate版に変更しました。
(もちろん別途Windows7のUltimate版の正規ライセンスは入手しています)

そのため特に何も考えずSP1適用済みDVDからアップグレードインストールしたところ、プログラムは確かにあらかた無事でしたが、ファイアウォール設定がリセットされていたり、ATOKが消滅してたり、IE11がIE8になっていたりと、そこそこ不都合が発生しました。

「IE8はIE11を入れ直せばいいだけだろ」、と思って気楽にWindowsUpdateをしましたが
エラーコード 9C59 Windows Update で不明なエラーが発生しました。
と表示されて一向にIE11にできません。

エラーコードで検索すると情報がないわけではなく、情報がありすぎて今回の問題に合致しない対策法ばかり引っ掛かるという有様でした。

結論

色々と悪戦苦闘をした結果から書くと

  1. SP1が当たった状態でIE8になっている
  2. この状態で特定のWindows更新パッチが当たっていると更新に失敗する

この条件を満たした場合の解決策は「古いパッチを消してからIE11を入れる」しか対応策がないようです。ディスクのクリーンナップとか時間の無駄です。

対応策

急がば回るしかないのかと諦めて以下のように対応しましょう。

  1. 更新パッチを削除

実際、どれが原因なのか考えるのも検証するのも面倒なので初期状態までスカッと消しました。実施にあたっては以下のようなバッチファイルを作成しました。
削除完了まで1時間ぐらいかかったような気がします。
なお実行前の注意としてWindowsUpdateは自動ではなく手動にしておかないと、苦労して消しても再起動する度にゾンビのように復活してしまいます。

@echo on
wusa.exe /uninstall /kb:4025341 /quiet /norestart
wusa.exe /uninstall /kb:4014504 /quiet /norestart
wusa.exe /uninstall /kb:3170455 /quiet /norestart
wusa.exe /uninstall /kb:3161958 /quiet /norestart
wusa.exe /uninstall /kb:3161949 /quiet /norestart
wusa.exe /uninstall /kb:3159398 /quiet /norestart
wusa.exe /uninstall /kb:3156019 /quiet /norestart
wusa.exe /uninstall /kb:3156016 /quiet /norestart
wusa.exe /uninstall /kb:3155178 /quiet /norestart
wusa.exe /uninstall /kb:3150220 /quiet /norestart
wusa.exe /uninstall /kb:3139914 /quiet /norestart
wusa.exe /uninstall /kb:3139398 /quiet /norestart
wusa.exe /uninstall /kb:3138910 /quiet /norestart
wusa.exe /uninstall /kb:3138612 /quiet /norestart
wusa.exe /uninstall /kb:3127220 /quiet /norestart
wusa.exe /uninstall /kb:3126587 /quiet /norestart
wusa.exe /uninstall /kb:3124275 /quiet /norestart
wusa.exe /uninstall /kb:3122648 /quiet /norestart
wusa.exe /uninstall /kb:3115858 /quiet /norestart
wusa.exe /uninstall /kb:3110329 /quiet /norestart
wusa.exe /uninstall /kb:3109560 /quiet /norestart
wusa.exe /uninstall /kb:3109103 /quiet /norestart
wusa.exe /uninstall /kb:3108664 /quiet /norestart
wusa.exe /uninstall /kb:3108381 /quiet /norestart
wusa.exe /uninstall /kb:3108371 /quiet /norestart
wusa.exe /uninstall /kb:3101722 /quiet /norestart
wusa.exe /uninstall /kb:3097989 /quiet /norestart
wusa.exe /uninstall /kb:3093513 /quiet /norestart
wusa.exe /uninstall /kb:3086255 /quiet /norestart
wusa.exe /uninstall /kb:3084135 /quiet /norestart
wusa.exe /uninstall /kb:3078601 /quiet /norestart
wusa.exe /uninstall /kb:3076895 /quiet /norestart
wusa.exe /uninstall /kb:3075220 /quiet /norestart
wusa.exe /uninstall /kb:3074543 /quiet /norestart
wusa.exe /uninstall /kb:3072305 /quiet /norestart
wusa.exe /uninstall /kb:3071756 /quiet /norestart
wusa.exe /uninstall /kb:3067904 /quiet /norestart
wusa.exe /uninstall /kb:3067903 /quiet /norestart
wusa.exe /uninstall /kb:3061518 /quiet /norestart
wusa.exe /uninstall /kb:3060716 /quiet /norestart
wusa.exe /uninstall /kb:3059317 /quiet /norestart
wusa.exe /uninstall /kb:3055642 /quiet /norestart
wusa.exe /uninstall /kb:3046269 /quiet /norestart
wusa.exe /uninstall /kb:3046017 /quiet /norestart
wusa.exe /uninstall /kb:3045685 /quiet /norestart
wusa.exe /uninstall /kb:3037574 /quiet /norestart
wusa.exe /uninstall /kb:3035126 /quiet /norestart
wusa.exe /uninstall /kb:3030377 /quiet /norestart
wusa.exe /uninstall /kb:3023215 /quiet /norestart
wusa.exe /uninstall /kb:3022777 /quiet /norestart
wusa.exe /uninstall /kb:3021674 /quiet /norestart
wusa.exe /uninstall /kb:3010393 /quiet /norestart
wusa.exe /uninstall /kb:3019978 /quiet /norestart
wusa.exe /uninstall /kb:3011780 /quiet /norestart
wusa.exe /uninstall /kb:3010788 /quiet /norestart
wusa.exe /uninstall /kb:3004375 /quiet /norestart
wusa.exe /uninstall /kb:3004361 /quiet /norestart
wusa.exe /uninstall /kb:3003743 /quiet /norestart
wusa.exe /uninstall /kb:2992611 /quiet /norestart
wusa.exe /uninstall /kb:2991963 /quiet /norestart
wusa.exe /uninstall /kb:2984972 /quiet /norestart
wusa.exe /uninstall /kb:2978742 /quiet /norestart
wusa.exe /uninstall /kb:2978120 /quiet /norestart
wusa.exe /uninstall /kb:2977292 /quiet /norestart
wusa.exe /uninstall /kb:2973351 /quiet /norestart
wusa.exe /uninstall /kb:2973201 /quiet /norestart
wusa.exe /uninstall /kb:2973112 /quiet /norestart
wusa.exe /uninstall /kb:2972211 /quiet /norestart
wusa.exe /uninstall /kb:2972100 /quiet /norestart
wusa.exe /uninstall /kb:2968294 /quiet /norestart
wusa.exe /uninstall /kb:2943357 /quiet /norestart
wusa.exe /uninstall /kb:2937610 /quiet /norestart
wusa.exe /uninstall /kb:2931356 /quiet /norestart
wusa.exe /uninstall /kb:2929733 /quiet /norestart
wusa.exe /uninstall /kb:2911501 /quiet /norestart
wusa.exe /uninstall /kb:2894844 /quiet /norestart
wusa.exe /uninstall /kb:2893294 /quiet /norestart
wusa.exe /uninstall /kb:2892074 /quiet /norestart
wusa.exe /uninstall /kb:2884256 /quiet /norestart
wusa.exe /uninstall /kb:2882822 /quiet /norestart
wusa.exe /uninstall /kb:2871997 /quiet /norestart
wusa.exe /uninstall /kb:2868116 /quiet /norestart
wusa.exe /uninstall /kb:2868038 /quiet /norestart
wusa.exe /uninstall /kb:2864202 /quiet /norestart
wusa.exe /uninstall /kb:2862335 /quiet /norestart
wusa.exe /uninstall /kb:2862330 /quiet /norestart
wusa.exe /uninstall /kb:2862152 /quiet /norestart
wusa.exe /uninstall /kb:2861698 /quiet /norestart
wusa.exe /uninstall /kb:2847927 /quiet /norestart
wusa.exe /uninstall /kb:2840631 /quiet /norestart
wusa.exe /uninstall /kb:2834140 /quiet /norestart
wusa.exe /uninstall /kb:2813430 /quiet /norestart
wusa.exe /uninstall /kb:2807986 /quiet /norestart
wusa.exe /uninstall /kb:2786081 /quiet /norestart
wusa.exe /uninstall /kb:2770660 /quiet /norestart
wusa.exe /uninstall /kb:2758857 /quiet /norestart
wusa.exe /uninstall /kb:2742599 /quiet /norestart
wusa.exe /uninstall /kb:2736422 /quiet /norestart
wusa.exe /uninstall /kb:2729094 /quiet /norestart
wusa.exe /uninstall /kb:2727528 /quiet /norestart
wusa.exe /uninstall /kb:2705219 /quiet /norestart
wusa.exe /uninstall /kb:2698365 /quiet /norestart
wusa.exe /uninstall /kb:2690533 /quiet /norestart
wusa.exe /uninstall /kb:2667402 /quiet /norestart
wusa.exe /uninstall /kb:2654428 /quiet /norestart
wusa.exe /uninstall /kb:2639308 /quiet /norestart
pause

なお新しいのから消すのが基本かと思ったので大きい番号から並べていますが効果は不明です。

  1. 再起動

大事。

  1. IE11必須コンポーネントをインストール

Internet Explorer 11 用必須更新プログラム にある9個の更新プログラムをインストールする。
個別に再起動せずKB順に当てて最後に再起動すれば良い。

  1. 再起動

なんか10分ぐらい待たされる。

  1. IE11を単体インストール

以下から自分の環境に応じたIE11をダウンロードしてインストールする。

Windows 7 SP1 32 ビット
Windows 7 SP1 64 ビット
Windows Server 2008 R2 SP1 64 ビット

インストールが始まったら勝ったも同然

  1. 再起動

私の環境だと再起動すると1.5時間ぐらい処理中になりました。

  1. システム更新準備ツール (KB947821)を当てる

Windows 7 用のシステム更新準備ツール (KB947821) [2014 年 10 月] をダウンロードしてインストールする
これを当てないとWindowsUpdateが延々と処理中のまま進まなくなります。
事前に当てておいても無駄になるので、必ずIEを当ててから処理するようにします。

  1. 再起動

20分ぐらい待たされたような

  1. ひたすらWindowsUpdateする

多大な時間がかかります。
時間の無駄なので寝る前にするようにしましょう。

 

あとは2020年まで粘ってから観念してWindows10にアップグレードするだけです。

 

参考サイト

http://siwon-g.hateblo.jp/entry/20150703/1435926555

http://www.smilebanana.com/archives/2016/01/12-2109.shtml

 

フロントトリプルのクロスバイクにチェーンケースを装着する2

前回の記事から1年も経過してしまいましたがクロスバイクにチェーンケースを装着しました。

MERIDA CROSSWAY TFS200MD改

 

何故こんなに時間がかかったのかというと、それなりに苦労があったからというのが答えなわけですが、その悪戦苦闘の状況を今さら記してもさして意味がないので以下に要点のみ記します。

  • ボトムブラケットの軸長を5mm短くする
BB共締め用の部品の分だけ軸長を短くする必要があります。

色々と試して「BB-UN26-K  チェーンケース対応」(型番:EBBUN26KBY17B)がベストだと分かりました。
  • チェーンガードはあきらめる
クランクセットの外側に付けるチェーンガードについてはギリギリで干渉してしまうのであきらめた方がいいです。

色々と粘りましたがXtremeChainCaseの内寸では無理というのが結論です。
  • 後部固定はインシュロック
下手に凝ろうとせずインシュロックだけで固定すれば十分だと自分を納得させた方が幸せになれます。
  • RoseBikesでの注文はステータスが緑色のみにする
うっかり黄色のステータスの商品も混ぜて注文すると何ヶ月も待たされます。

RoseBikesには結構安い商品がありますが納期についてはまったく信用できないので、緑色の即納商品以外は絶対に注文しないようにしましょう。

チェーンケースだけじゃもったいないと思って納期黄色のチェーンも一緒に注文したらえらいことになりました。

しかも納期の予定がデタラメであるにもかかわらず、やつらは絶対に謝りません。

心の平穏のために気をつけましょう。

これらを気をつければクロスバイクのチェーンケース化の難易度は高くないです。

今では普通にスラックスで乗ってたりしますが、裾が汚れることもなく目的はほぼ達成しました。

ちなみにこのクロスバイク(MERIDA CROSSWAY TFS200MD)は純正から以下のような部分を変えてます。

ハンドル:ノーマルの両端を3cmづつ計6cmカット
Fホイール:SHIMANO WH-S501-3D ALFINE ハブダイナモホイール
Rホイール:タキザワ手組32Hホイール KINLIN XR-240+SHIMANO FH-M665(SLX)
Fブレーキ:AVID BB7
Rブレーキ:AVID BB7
Rシフター:SHIMANO ST-MC20(Alivio) 8SP ブレーキレバー一体型
Lシフター:SHIMANO ST-MC20(Alivio) 3SP ブレーキレバー一体型R
Fディレイラー:SHIMANO FD-M360[AFDM360M3] (Acera)
Rディレイラー:SHIMANO RD-M310(Altus) 8SP ※初期仕様のまま
スプロケット:SHIMANO CS-HG51-8 (11-32T)
チェーン:SHIMANO CN-HG40
Fライト:BUSCH&MULLER LUMOTEC IQ CyoT senso plus (60lux)
Rライト:INFINI I-501R2
クランク:SHIMANO FC-M171(BLACK 170mm)
ペダル:Wellgo CNC B54
BB:SHIMANO BB-UN26-K 68mm-117mm
シートポスト:Charge x Wiggle Chopstick Seat Post
サドル:純正
マッドガード:純正(たぶんFLINGER SW-814)
フロントキャリア:純正
リアキャリア:TOPEAK Uni Explorer Disc
参考:CROSSWAY TFS200MD オリジナル仕様

とまあサドルとフォークとヘッドパーツ以外は弄りまくってます。
実用一辺倒を目的に全天候自転車通勤仕様として数年にわたり改造してきましたが、正直これ以上改造するネタが思いつきません。
いや正確にはリアライトのハブダイナモ化やUSB給電対応などをしたいのですが、ケーブリングが面倒でやる気がしないのです。
バッテリー内蔵ケーブリング対応なフレームの新モデルも未だにないようですし、残念ながら時代がまだ私の要望に追いついていないようです:-p)

 

常盤薬品 豆乳スムージーの栄養成分

最近、カロリー計算と運動を併用して減量しています。
そんな中、LOHACOのアウトレットで買った常盤薬品 豆乳スムージーが公式ページに栄養素の記載がないため摂取カロリー計算に困りました。
パッケージに小さく書いてあるのですが、中小企業ってこういうところが大企業と比べて落ちるんですよね。
いちいちパッケージを見るのも面倒なので、ここに備忘録として記載します。

常盤薬品 豆乳スムージー 17gあたりの栄養成分表示
成分名 内容量
エネルギー 58kcal
タンパク質 4.4g
脂質 0~1g
糖質 6.4g
食物繊維 4.0g
ナトリウム 46mg
ビタミンC 60mg

常盤薬品 豆乳スムージー成分表

常盤薬品 豆乳スムージー成分表

それにしても脂質のブレ幅が大きすぎる。
どういう製法をしとるんね。

フロントトリプルのクロスバイクにチェーンケースを装着する1

今までこのブログで紹介していた自転車は見た目ママチャリのシェボーFアルミばかりでしたが、今回は通勤チャリとして使っていたMerida CrossWay TFS200MDというクロスバイクを題材としています。
この機種も通勤戦争の果てに行き着いたカスタムを色々と施しているのですが、唯一の不満がママチャリ的なチェーンケースが装着できないことでした。
チェーンケースがないと常にズボンの裾まくりが前提となり、急いでいるときにうっかりズボンの裾を汚したりということになります。
仕方なく「アキワールド 変速車対応アルミチェーンカバー」という簡易カバーを取り付けていましたが、効果はないよりマシという程度でした。

AKI WORLD 変速車対応アルミチェーンカバー

AKI WORLD 変速車対応アルミチェーンカバー

そんな折り、いいものがないかと検索していたところ、RoseBikesというドイツの自転車通販サイトで以下のものを発見しました。

XtremeChainCase for front triple

フロントトリプル用チェーンケース

このXtreme chain caseというチェーンケースを使えば念願が達成できそう!
しばし悩むこと3日、BB軸長が変えないといけない可能性はあるが、取り付けは可能だろうと判断。
ということでRoseBikesで注文してみました。
なおRoseBikesについてググっても日本語の情報が殆ど出てこなかったので、おそらく(2016年3月初旬の購入時には)日本における使用者はほとんどいなかったのでしょう。
若干不安ながら、まあサイトの作り的に大丈夫だろうなと判断して注文したわけです。
そして注文から1週間でドイツから届きました。

XtremeChainCase到着

XtremeChainCase到着


ドイツから月曜発送で土曜着でした。早い!
ついでにRoseBikesブランドのカタログも同梱されていたので自社ブランドバイクの販売もやってるところみたいですね。
送料はかかりますが個人的にはオススメできる自転車海外通販だと思います。

それから1ヶ月半ほどは忙しくて手が付けられませんでしたが、この連休にようやく手を付けることが出来ました。

しかし駄目でした!

XtremeChainCaseの取り付け

XtremeChainCaseの取り付け


これ取り付けの1番最初の手順ですが、かなり分かりにくいのは置いて、見ての通りフレームのクランク軸にステイを取り付ける形になります。
てっきりBBに共締めするものかと思っていたら、まさかこう来ましたか。
なので私の自転車のようなダウンチューブがぶっとい↓のには取り付け不可能です。
Merida TFS200MDのBB部分

清掃が行き届いていないですが御容赦を


昔ながらのクロモリの細くて優美な車体でないと厳しいような感じです。

まさか初っぱなから駄目だとは思いもしませんでした。
せっかくなので分かりにくい説明書をスキャンしました。
これを読んで自分の車体ならいける!と思った人は是非、RoseBikesに注文してください。

XtremeChainCase_Manual1
XtremeChainCase_Manual1

それから今回、使えなかったXtremeChainCaseですが、クロモリフレームの自転車を買う予定がないので欲しい人がいらっしゃるなら差し上げます。
ただし以下の条件です。

  1. 送料は着払い
  2. 宅配業者指定不可
  3. 営業所止め不可
  4. ブログなどで記事化できる人

まあ、こんな最果てのブログを見てる人などいないかもしれませんが、情報は共有してナンボですので記事化できる人というのが大条件です。

ご応募を気長にお待ちしております。
※.捨てアドを添えてコメント投稿してください


追記
よくよく部品を見てみたらBB共締用っぽい部品があることを発見!
説明書の2ページ目とか読んですらいなかったけど、まさにBB共締めじゃないですか!?
あああ、あとは私のやる気だけ。

続く

現在のシェボーFアルミの状態

快速ママチャリとして日々を過ごしているシェボーFアルミですが、ちょこちょこと改修は継続しており、現在は以下のような構成です。

ハンドル:ノーマル(MTB-AL-153)
Fホイール:XTハブダイナモ(E2端子)のカスタムホイール@ヤフオク
Rホイール:NOB-G-700-14F (クロスバイク用の安い36Hホイール)
Fブレーキ:ノーマル(TEKTRO 型番未調査)
Rブレーキ:BR-M590(DEORE)
Rシフター:SL-M590(DEORE)
Rディレイラー:RD-M772-GS(XT)
スプロケット:CS-HG61-9 (11-34T)
チェーン:CN-HG53 110リンク
Fライト:LP-X101(Nexus E2に改造済)
クランク:謎の46T 170mmクランク@ヤフオク
ペダル:Wellgo PD-WG-033
BB:TANGE LN7922 68mm-122.5mm
シートポスト:ノーマル
サドル:ノーマル
タイヤ:パナレーサー ツーキニスト 28c

改造版シェボーFアルミの全景

改造版シェボーFアルミの全景

まあ「そこそこいじってるぜ?(キリッ」って言っても差しつかえないレベルになったとは思います。
かれこれ3年も乗ってると、さすがに所々にサビが浮いてきたりするわけですが、まあまだなんとか使ってます。
とはいえフレームの小ささという大きな不満点があるので、壊れたら壊れたでGIANTのFlourish2のMサイズをアメリカから個人輸入して改造したろうかとか思っているのですが、当分そこまでは壊れそうもないので乗り続けることでしょう。
それにしてもFlourish2ええなあ。

Flourish-2-Cream
良すぎてドライブトレイン的に改造するところがほぼない。
強いて言えばタイヤが糞ダサいのと、前カゴがアップライトすぎるのが気にくわないぐらいか。

閑話休題。

シェボーFアルミの直近の改造部分はリアディレイラーをRD-M772-GSに変更したことですが、SGSからGSという短めのものにしたことでチェーンを2コマ詰めることにしました。
そうしたところチェーンのばたつきがかなり改善されました。
しかも今日は強風だったもんで、コンビニに停めたチャリが派手に転倒していてリアディレイラーが曲がったかも・・とちょっと青くなりましたが、ディレイラーガードが余裕で守ってくれました。
前のRD-M591-SGSだと危なかったかもしれません。

つまりフロントシングルにSGSタイプはアカン!
ということですね。
SSタイプだともっといいんだけどねー。
なんで売ってないんだろ・・・ってニッチすぎるからか。

メタバリアは効果以前の駄目商品

チャリ通しなくなってから太る一方で完全メタボマンになってしまったので、とりあえず楽して痩せられそうなメタバリアスリムを買ってみました。
しかも一番高いプレミアムってのを3個も。
だって富士フイルムって言われたら信用できそうじゃないですか。

メタバリア プレミアム

メタバリア プレミアム

結果から言うとやたら屁が出て使い物になりません。
仕事をしててもお構いなしに屁をこきたくなるけど、普通は我慢せざるを得ないですよね。
もちろん我慢するとえらい腹痛ですよ。
もし会議中とかにこうなったら人間の尊厳を失う事態になりかねないです。
つまり効果(痩せる痩せない)以前にQOLが下がって社会生活が困難になります。
したがってこんなもん使えません。
あくまでも個人の見解に過ぎませんがオススメはしません。
富士フイルムの中の人いわく「それは腸内環境が改善されたら治る」とか言ってたけど、正直そんな機序が不明な戯言は信用できないのが偽らざるところ。
私は成分のサラシアが糖と反応してガスになると推測しているので、腸内環境はあまり関係ないと踏んでますし。

結論から言うと楽して痩せようというのが無理でした。
当たり前ですよね。馬鹿ですよねホント。
やはりカロリー制限してファットメタボライザーを飲んで自転車を漕ぐのが、自分にとっては楽なようです。
特保ではなく機能性食品って時点で、天下の富士フイルムだろうが関係なくメタバリアの価値が低いものだと気が付くべきでした。

そもそも買う前によく調べろよと言われれば、まあごもっともなのですが、調べてもアフィリエイトに目がくらんだノイズまみれのゴミ情報ばかり出てくるんですよね。
その中でやっと見つけた「なんか効果あるんだよ」と謳ってた筋トレ系ブログを無条件に信用してしまったわけです。
弱った人間は都合のいい情報にすがってしまうと言う、愚者丸出しの行動を取ってしまったようで深く反省しております。

幸いこのブログは投稿が少ないにもかかわらず、検索は割と上位に来るようなので、これからはこういう情報も発信していきたいです。
アフィに目がくらんでノイズまき散らすゴミ虫どもが頭にくるので、駆除の一助になればという思いです。

9速用リアディレイラーと10速用リアディレイラーには互換性がない

日々の生活の足として大活躍しているシェボーFアルミのリアディレイラーを曲げてしまったようなので、9s(DEORE RD-M591)から10s(SLX RD-M675)に交換しました。
何故こうしたかというと、RD-M675にはチェーンスタビライザーという機能があり、それに期待したためです。
なにせ前々から走行時に上下に振動が走ったとき、一瞬チェーンが落ちそうになるのを感じていたため、そこに不満を感じていたのです。

とまあ、差し替えてみた訳なのですが、いくらやってもローギアに変速ができない。

SLX

シェボーFアルミにSLX(RD-M675)を載せたらローギアに入らない図

一応、換装する前にリアディレイラーの互換性についてちょろっと検索をしてはいたのですが、大体が
リアディレイラーは伸縮するだけだから互換性を気にする必要ない」みたいな記事ばかりだったので、特に問題はないと判断したのが全ての間違いだったわけですが。
ひたすら調整し、チェーンも縮めたり伸ばしたりと悪戦苦闘した結果、「これは自分の腕の問題じゃない」という結論になり、そこからひたすら検索しまくって下記ページを発見しました。
9sMTBに10sのリアディレイラーのみを使用できますか? – Yahoo知恵袋

「シマノのMTB用ディレーラ」という条件であれば、
9速と10速には互換性がありません。

微妙に合わないとかでは無く、
ワイヤ稼動量に対するレバー比自体が違うので、
10速用ディレーラと9速シフターの組み合わせでは、
完全に使えない状態になります。

なるほど。
「レバー比」という物の考え方があるわけですね。
確かにテコの原理で変形してるわけだから、考えてみれば有って当然なわけですが、私がちょろっと調べたようなページでレバー比に言及したところは皆無でした。
つまりどこかの半可通が書いた適当な情報を真に受けて失敗してしまった訳なのですが、情報の取捨選択は受け取り側にある以上は文句も言えませんね。
正直、ムカツいてるけどw
ムカツいて仕方がないのでCRCで9速ディレイラーを注文しました。
今度はRD-M772というXTグレードで(^ω^)
日本で買おうとすると中古しか選択肢がないですが、海外通販ならバリバリの現役パーツです。しかも日本より安い。
日本は右向け右の国民性で9sの上位グレードは絶滅してるようですが、海外ではそういうメーカーの身勝手が通用しないのか、まだまだ9sの上位グレードが新品で売ってます。
冷静に考えるとこんなママチャリモドキにSLXやXTを載せるとか完全に頭がおかしいですが、見えないところのお洒落みたいなものです。

こうして痛い失敗をしてしまいましたが、せめて私のブログでは間違った情報を発信しないように気をつけたいところです。

ExpressWebからWinserverに引っ越しました[非推奨案件]

ExpressWebが廃止されるということで契約が切れる3月末で追い出されることになりました。
なのでギリギリまで粘ってWinserverに引っ越しました。
Winserverにした理由は以下の通り。

  1. ASP.NET+MySQL+SQLServer使用可
  2. 当たり前だよね。

  3. 安い
  4. たかだか個人のホムペだし・・。なのでAzureは論外ですわ。

  5. 運用実績
  6. ExpressWebみたいに勝手に値下げして短期間で勝手に撤退する自爆テロをやられると迷惑なので

  7. WindowsServer2012
  8. Win2008Srvベースよりは長くサポートされるので

とにかく年齢を経ると面倒なことは極力したくなるわけですよ。
ただでさえ面倒くさがりなのに金にならないことで面倒は御免なのです。
ということで横移動で安く済み、それなりに運用実績があるところを選んだわけです。
それでも年払いで月々1000円近い金額はそこそこ抵抗があったので、安価なLinux VPSにしてみることも考えましたが、データ移行に苦戦している内に終戦になる恐れがあったので検討除外しました。
ひょっとして思いの外、簡単に移行できたかも知れませんが、元々ASP.NETが好きで選んだことなので心中することにしました。

とまあこうして移転してきたわけですが、私は契約時期として難民化が早い方でしたので、これから私のようなExpressweb難民が続々と出てくることでしょう。
ということで以下に私がたどってきた苦難の道をを紹介します。

1.Expresswebのデータをエクスポート

  • 準備
  • ExpressWebのコントロールパネルにログインする。

  • ファイルの取得
  • [ファイルマネージャ]から”wwwroot”を選択して[Zip形式]ボタンをクリック⇒出力ファイル名(wwwroot.zipみたいなの)を入力する。
    そうするとZIPアーカイブが作成されるのでクリックしてダウンロード。

    ファイルマネージャ

    ExpresWebコントロールパネルのファイルマネージャ

  • DBデータの取得(Blogデータ)
  • [データベース]から”MySQL5.1″をクリック⇒[データベースを参照]をクリックしてphpMyAdminを開く。
    phpMyAdmin-[エクスポート]タブをクリック⇒blogのDB名を選択して[実行する]をクリックするとDBデータのダウンロードが始まる。

    phpMyAdminのエクスポート機能

    phpMyAdminのエクスポート機能


    デフォルト設定に対して「作成するクエリの最大長」だけ増やしましたが、半端にインポートしても意味がないので「エクスポートをトランザクションで囲む」にチェックしても良いかも。

  • DBデータの取得(SQLServer)
  • この作業はSQLServerにDBを作成した人のみがやればいいです。
    [データベース]から”SQL Server 2008″をクリック⇒データベース名をクリックして”SQL データベースプロパティ”を開く。
    [メンテナンスツール]⇒[バックアップ]をクリック⇒ファイル名を指定して”ZIP形式バックアップ”にして[バックアップ]をクリックするとDBデータのダウンロードが始まる。

    ExpressWebのコントロールパネル(SQLServer)

    ExpressWebのコントロールパネル(SQLServer)

2.データをインポート(MySQL)

  • sqlファイルを開いてCREATE DATABASEから始まるクエリを削除
  • sqlファイルの”expressweb.jp”を新サイトほホスト名に置換
  • sqlをZIPに固める
  • WinserverのphpMyAdminにログインしてインポートタブをクリック
  • ブログデータをインポートする
  • phpMyAdminのインポート画面

    phpMyAdminのインポート画面


    ちなみにphpMyAdminへのアップロード制限は2MBだって・・・。かなりここヤバい気がしてきた。

3.データをインポート(SQLServer)

  • SQLServer Management Studio 2012 Express の導入。
  • WinserverのSQLServerはクライアントPCから直接SQLServer認証で接続する必要があるため、下記からSSMSEをダウンロードしてインストールする。
    https://www.microsoft.com/ja-JP/download/details.aspx?id=50003

  • SQLServerのデータベースリストア
  • 通常は以下のメニューからbakファイルを選べばスイスイ行くはずが、何故かローカルファイルが選択できない。

    リストア

    SQLServer Management Studio 2012によるリストア


    どうやらWinserver側でローカルからのリストアが制限されている様子。
    というかSQL打つしかできない。
    やっぱり、ここヤバかった・・・。
    だって自分のデータを持ち込むのに金を取るんだってさ↓。しかも5400円という高額。
    SQLServer 移転 [Winserver]
    ちょっとこれマジでありえねえだろ・・・。
    FAQにも一切書いてないから事前に調べようがないじゃん。酷いよ。
    と呪詛を吐いても詮無きことなので回避策を講じました。

  • [回避策]
    • bakファイルをローカルリストア
    • Expresswebから取得したbakファイルをローカルリストアするためにSQLServerを導入します。
      ただ一時作業のために余分なリソースを常駐させたくないので、常時インスタンス起動せずオンディマンドらしいSQL Server 2012 Express LocalDBを使うことにしました。
      マシン環境が潤沢な人は通常版SQLServerを使えば良いだけなので読み飛ばして良いです。
      インストールしたらSSMSEからbakファイルをリストアします。SSMSEの使い方は下記サイトを参考にしました。
      http://sqlazure.jp/r/sql-server/233/
      参照先のおかげで助かりました。ありがとうございます。だってアクセス指定子が「(localdb)\LocalDB Instance」なんて分からんって!

    • DBをスクリプトファイル化
    • データベースを右クリックして[タスク]→[スクリプトの生成]を選択してDBデータをファイル化する

      DBのスクリプトファイル化

      SQLServer Management Studio 2012によるDBスクリプトファイル化

    • SSMSE上でスクリプト実行
    • SSMSEの接続先をWinserverにしてから出力したスクリプトファイルを流します。
      これでやっと完了・・・。
      逆にWinserverから引っ越す場合は、やはりSSMSEにてDBをスクリプトファイル化する流れで行けるかと思います。

4.ファイルの復元

  • FTPによるファイルアップロード
  • バックアップしたファイルを解凍してFTPで”web”ディレクトリ配下に展開すれば完了です。
    時間がかかるので始まったら放置して別作業をするのが吉。
    ちなみにMySQLのDB名などを変えたらWordpress側のwp-config.phpもいじらないと駄目ですよ。

で、WinServerの使い勝手ですがExpresswebの3倍の価格ながら残念ながらパフォーマンスも3倍とは行かないようです。
あとやたら手作り感が半端ないです。SQLServerを使いたい場合はメールに必要事項を書いて送ってくれってノリです。
しかも前述したようにDBのリストアみたいな簡単な作業すらユーザー側には開放されておらず、金くれれば代行するぞとか平気で書いてある有様。
調べたら10人ぐらいしかいない会社みたいなので、まあむべなるかなって感じなのですが、正直Expresswebから比べるとかなり落ちると言わざるを得ません。
1年払いしてしまったので仕方なく使いますが、正直オススメはしません。

ABLENETのVPSを利用したDELEGATEプロキシサーバーの作成

格安VPSであるABLENETにてDELEGATEによるHTTPプロキシ/FTPプロキシサーバーを構築してみたので、その備忘録代わりに載せておきます。

プラン:KVM 512M
OS:CentOS 6.6

こういうのを検索する人には分かりきったことかと思いますので、VPSとはなにかとかABLENETがなにかとかは省略します。
それとvimの使い方も書きませんので適宜脳内補完して下さい。

ということで以下にHTTPポート:8080 , FTPポート:21 , SSHポート:2200での設定例を記載します。
なおHTTPは使い勝手確保のためにプロキシ特有の環境変数を出力しないような設定としており、
診断くんによるプロキシ判定で”総合評価:?(A 以上 or 生 IP)”と診断される設定です。
いわゆる匿名串というやつです。
FTPについてはNextFTPにてPASV接続を確認しております。
一度、設定してしまえば極めて安定しているので放置運用となるかと思います。
そのため踏み台にされないようにセキュリティには充分に配慮してください。

###ABLENETのコントロールパネルのコンソールから操作

###ツ黴€ホスト名の設定
$ツ黴€vi /etc/sysconfig/network
適宜変更 ex)h000-00-00-00

### hostsの設定
$ vi /etc/hosts
127.0.0.1ツ黴€ツ黴€ツ黴€ツ黴€ツ黴€ツ黴€ localhost localhost.localdomain
000.00.00.00ツ黴€ツ黴€h000-00-00-00 h000-00-00-00.vps.ablenet.jp

### ネットワーク設定
$ vi /etc/sysconfig/network-scripts/ifcfg-eth0
ONBOOT=yes
BOOTPROTO=none
IPADDR=グローバルIPアドレス
NETMASK=255.255.255.0
GATEWAY= 指定のアドレス
DNS1=指定のアドレス1
DNS2=指定のアドレス2

$ service network restart
→(もしくは/etc/init.d/network restart)

###SSHのポート変更
$ vi /etc/ssh/sshd_config
Port 2200
$ service sshd restart

### ここから先はSSH経由でターミナルが使える

###ファイアウォール設定
$ vi /etc/sysconfig/iptables
===========(ここから)
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state –state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -p tcp -m state –state NEW -m tcp –dport 20 -j ACCEPT
-A INPUT -p tcp -m state –state NEW -m tcp –dport 21 -j ACCEPT
-A INPUT -p tcp -m state –state NEW -m tcp –dport 22 -j ACCEPT
-A INPUT -p tcp -m state –state NEW -m tcp –dportツ黴€2200 -j ACCEPT
-A INPUT -p tcp -m state –state NEW -m tcp –dport 8080 -j ACCEPT
COMMIT
===========(ここまで)

### PassiveFTP許可
$ modprobe ip_conntrack_ftp
$ cp -p /etc/sysconfig/iptables-config iptables-config.bak
$ vi /etc/sysconfig/iptables-config
IPTABLES_MODULES=”ip_conntrack_ftp”

###ネットワーク再起動
$ service iptables restart

###ファイアウォール起動状態確認
$ chkconfig network on
$ chkconfig –list iptables
→(3:OnになってればOK)

###パッケージ更新
$ yum install wget
$ yum install make
$ yum install gcc-c++
$ yum install libstdc++
$ yum install libstdc++-devel
$ yum install openssl-devel
$ yum update

###一度再起動
$ reboot

### DELEGATE導入準備
$ cd /usr/local/src
$ wget ftp://delegate.hpcc.jp/pub/DeleGate/delegate9.9.13.tar.gz
$ tar zxvf delegate9.9.13.tar.gz
$ cd delegate9.9.13/src
$ PATH=$PATH:/usr/bin/gcc
$ export PATH

###DELEGATEインストール
$ make PATH=”.:$PATH” CC=gcc
→途中メールアドレスの入力を求められるので入力
→間違えても一端Enter押下して[n]を選べば再入力できる
$ cp delegated /usr/sbin

###DELEGATEバイナリに署名をする
$ /usr/sbin/delegated -Fesign -w

###DELEGATEログ設定
$ mkdir -p /var/delegate/log
$ chown -R nobody:nobody /var/delegate/log
$ mkdir /var/delegate/cache
$ chown -R nobody:nobody /var/delegate/cache
$ ln -s /var/delegate/log /var/log/delegate
$ mkdir /etc/delegate

$ vi /etc/passwd
delegate:*:10001:10001:delegate daemon:/var/delegate:

$ vi /etc/group
delegate:*:10001:

$ chown -R nobody:nobody /var/delegate

###DELEGATE HTTP設定
→BASIC認証のユーザー名:パスワードは各自で事前に決めておくこと
→PERMITは私のISPのトップレベルドメインを指定したが各自の好みで変更して下さい

$ vi /etc/delegate/delehttp.conf
===========(ここから)
SERVER=http
RESOLV=cache,file:/etc/hosts,dns,sys,nis
LOGFILE=”/var/delegate/log/delehttp_[date+%m-%d].log”
CACHE=do
CACHEDIR=”/var/delegate/cache”
CONNECT=c,d:*:*
PERMIT=”*:*:*.net,*.jp”
HTTPCONF=’kill-head:Via,HTTP-VIA,DeleGate-Ver’
HTTPCONF=’add-qhead:CONNECTION:keep-alive’
AUTHORIZER=-list{ユーザー名:パスワード}
CRON=’0 3 * * * -expire 2′
===========(ここまで)

###DELEGATE FTP設定
→BASIC認証のユーザー名/パスワードは各自で事前に決めておくこと
→PERMITは私のISPのトップレベルドメインを指定したが各自の好みで変更して下さい

vi /etc/delegate/deleftp.conf
===========(ここから)
SERVER=ftp
STLS=-fcl
RELAY=proxy
LOGFILE=”/var/delegate/log/deleftp_[date+%m-%d].log”
PROTOLOG=”
PERMIT=”*:*:*.net,*.jp”
AUTHORIZER=-list{ユーザー名:パスワード}
===========(ここまで)

###DELEGATE起動
$ delegated -P8080 +=/etc/delegate/delehttp.conf
$ delegated -P21 +=/etc/delegate/deleftp.conf

###DELEGATE終了
$ delegated -P8080 -Fkill
$ delegated -P21 -Fkill

ちなみにDELEGATEの自動起動は必要を感じてないので設定してません。
リブートしたら手動でDELEGATE起動して下さい。

ASROCKのUEFI BIOS更新後に5回ビープ音が鳴り起動しない問題

先週の火曜にエアコンが水漏れして直下に置いていたPCが全損してしまった。

よく火事にならなかったものだとホッとしているが、とにかくその復旧で大わらわだった。

丸っと壊れたらRAID組んだ意味なんかないからね・・・。

あまりにも迂闊だったことに自己嫌悪しつつ、復旧ついでにメイン機(ASROCK H67M)のファームウェアを更新したのだが、更新直後にビープ音が5回鳴って起動しない。

“AMI BIOS ビープ音”かなんかで検索したところ「CPU不良」とか出てきたので安いCeleronを買って差し替えてみたが、症状は変わらない。

なんかCPU不良って結論が嘘くさいなと思って英語で”ASROCK 5Beeps” かなんかで検索したところグラフィックボードがないことで発生する警告らしいと分かった。

http://www.asrock.com/support/faq.asp?id=286

 

ゲームをやらないので外付けグラフィックボードの類を所有しておらず、無駄なことにお金を使いたくなかったが、仕方なく買ってきたところやっと起動した。

さらりと書いたけど、ここに行き着くまでに結構な手間暇を使ってる。

買ったのはASUSTeKのNVIDIA GT630-SL-1GD3-Lとかいうファンレスのボード。

良いか悪いかよく分からないけど安くて人気があってファンレスだったのでそれにした。

まあそれは良いとして・・・UEFIメニューに入った後に、iGPU(CPU内蔵のGPU)を先に起動するようにすれば完了だな、と思ったら”Primary Graphics Adapter”に”OnBoard”という項目が存在しない。

外付GPU専用になってしまったようだ。

ASROCKってメーカーはこんなこともテストしないでリリースするのか・・・・。

もうASROCKは二度と買わない・・・。

一切、信用できない。

Thinkpad保守用ディスケットのUSB汎用化(失敗)

最後の7段配列キーボードということで大事に使っていたThinkpad T520にうっかりヤキトリのタレをどぼどぼとこぼしてしまったら、

起動しなくなるというステキな現象に陥ってしまったので、とりあえずシステムボードを注文してみた。

CTOなので保守マニュアルを見ても対象システムボードが特定できないので、以下のサイトでマシンタイプを選択したらハイフン抜いたS/Nを入力すると対象
FRUが特定できる。

http://support.lenovo.com/jp/ja/ibasepartslookup/selectproduct?c=1&returnUrl=%2fja%2fibasepartslookup」

これで換装用システムボードが特定できたのだが、問題はマシンタイプとかシリアルとか、このあたりを正しく変更する必要があるのだが、そのために表題
のThinkpad保守用ディスケット(Thinkpad Maintenance Diskette 以下TMD)というソフトで読み取り/設定が可能になる。

ちなみに昔はCE用に限定された非公開のソフトウェアだったらしいが、今では普通にUSで公開しているようだ。

http://download.lenovo.com/ibmdl/pub/pc/pccbbs/mobiles/i7tm34us.exe

これで現時点で最新の1.86が入手できる。

しかし、このソフトいくつか問題があって・・・

1.USBに書き込んでもFAT12にされる

2.DOSアプリとして自由に起動できない

つまりいくら容量があっても40MBに制限されてTMD専用の使い勝手の悪いメモリーキーにされてしまうということ。

そんでFAT32で起動ディスク化したUSBメモリーキーに、色々と試行錯誤して汎用性の高いものを作ろうと悪戦苦闘

起動するカラクリが分からなくて、どういうことかと悩んだところTMDのCOMMAND.COM(PC-DOS)ではアプリの起動パラメータがハードコードされていた。

ここまで分かったところで早速作業開始。

(1).TMDから保守用ディスケットを作成し、作成されたディスケット中のファイルを全て保管しておく

(2).HP USB Disk Storage Format ToolでFAT32でUSBブートを作成(MSDOS)

(2).保管しておいたTMDのファイルのCOMMAND.COMをBIOSUTIL.EXEにリネーム

(3)IBMBIO.COM、IBMDOS.COM、CONFIG.SYS以外のファイルを(2)のUSBブートに格納

(4).適当にCONFIG.SYSを記述(device=flshbios.sys があればいい)。

これでUSBメモリーキーからブートしてBIOSUTIL.EXEを叩けば上手く行くはずだったが、”WARNING :EEPROM IS WRITE PROTECTED”とか表示されて書き換え
が出来ない

ちょっと、これ以上はよく分からんなぁ

もしかしてTMDのブートセクターあたりにパスワードが記述してあるとか??

もうダメポ

ということで、今回の企みは失敗であった。