C# 存储状态以跨应用程序启动执行某些操作

C# 存储状态以跨应用程序启动执行某些操作,c#,error-handling,dependency-injection,C#,Error Handling,Dependency Injection,我正在寻找一种永久存储状态+序列化类或其他东西的方法,这样当计算机/程序崩溃时,我可以在下一个程序启动时修复这些东西。例如,当我上传一个文件时,我需要在下次启动时知道它是否没有完成。这样,我可以删除远程文件或附加到它,如果可能等 现在,我提出了一些伪代码,基本上通过一个接口使用一个数据库来轻松地交换以创建存储状态的表+列+行,并在作业成功完成时删除该行。在每次启动时,我们现在可以从数据库中读取数据,并根据上次处于的状态调用相应的操作 问题是: 我不确定我所做的是否是完成这个任务的必要条件。。。选

我正在寻找一种永久存储状态+序列化类或其他东西的方法,这样当计算机/程序崩溃时,我可以在下一个程序启动时修复这些东西。例如,当我上传一个文件时,我需要在下次启动时知道它是否没有完成。这样,我可以删除远程文件或附加到它,如果可能等

现在,我提出了一些伪代码,基本上通过一个接口使用一个数据库来轻松地交换以创建存储状态的表+列+行,并在作业成功完成时删除该行。在每次启动时,我们现在可以从数据库中读取数据,并根据上次处于的状态调用相应的操作

问题是:

我不确定我所做的是否是完成这个任务的必要条件。。。选择? 我有点希望有一个图书馆已经做到了这一点,如果这是正确的方式去没有必要重新发明车轮 依赖注入呢?在我的代码中,我只是简单地将类名添加到行中,但是如果我想要呢 为插件公开所有这些,并且类名冲突?事实上,我如何知道我必须首先传递存储数据的注入dll?我不确定我在这一点上是否还有意义。 或者,如果一个依赖项被删除,如果使用的文件属于被删除的插件,我将如何识别哪些数据库文件,并要求用户保留它或不保留它? 到目前为止的示例代码:

class CPersistantthingy
{
    IDatabase mDatabase;
    string _mTablename;
    List<string> _mCols = new List<string>(new string[]{"State","SerializedClass","ClassType"}); //can't contain "ID"

    public Dictionary<string,string> AcquireRow()
    {
        //Make sure table exists and has the columns we need
        CreateTableIfNotExist();
        if(!VerifyTableHasColumns())
            throw new CorruptedTableException();

        //Create new row with State "undef", fetch the Auto Increment ID and return it
        return mDatabase.CreateRow();
    }

    public void WriteRow(Dictionary<string,string> row)
    {
        mDatabase.UpdateRow(row); //The database will lookup by ID and update fields.
    }

    private void CreateTableIfNotExist()
    {
        mDatabase.CreateTableIfNotExist(_mTablename, _mCols);
    }

    private bool VerifyTableHasColumns()
    {
        return mDatabase.VerifyTableHasColumns(_mTablename, _mCols);
    }
}
interface IDatabase
{
    //CreateTable / CreateRow / UpdateRow etc etc
}
所以一行看起来像:

State: Acquired|Started|Failed
SerializedClass: {host: "127.0.0.1", user: "usr", local:"C:\somefile.txt", remote:"/we/somefile.txt"}
ClassType: CFileObj
所以现在在我的程序中,我可以像这样使用它

SomeDatabaseClass_Object db = new SomeDatabaseClass_Object(blabla);
CPersistantthingy pers = new CPersistantthingy(db, "fileuploads");

private void StartUpload(string localfile)
{
    var row = pers.AcquireRow();
    row["State"] = "Acquired";
    row["ClassType"] = "CFileObj";

    /*Imagine that GetRemotePath reserves a remote path on the server we'll 
      upload our file to. That's how my current system works.*/
    string remfile = GetRemotePath(localfile);
    CFileObj obj = new CFileObj(currenthost, currentuser, localfile, remfile);

    row["SerializedClass"] = obj.Serialize();

    //Write the acquired state
    pers.WriteRow(row);

    //Uploading class has a callback to let us know when the first bytes have been written.
    try
    {
        StartUpload(obj.local, obj.remote, () => { row["State"] = "Started"; pers.WriteRow(row); } ); 
    }
    catch(Exception ex)
    {
        row["State"] = "Failed";
        pers.WriteRow(row);
        throw; //Catch at caller to immediately fix rather than waiting for next boot.
    }

    //Now do whatever else you want to do on a succesful upload. 
    //Maybe add new states for this too so we know at least the upload succeeded.

    //Finally delete the entry so it's not picked up at next boot.
    pers.DeleteByKey(row["ID"]);
}
然后,为了确保服务器在崩溃后清除失败的文件,请执行以下操作:

public static void Main()
{
    SomeDatabaseClass_Object db = new SomeDatabaseClass_Object(blabla);
    CPersistantthingy pers = new CPersistantthingy(db, "fileuploads");

    CUploadServerObj serverObj = new CUploadServerObj(bla,di,bla);
    serverObj.Connect();

    //Now let's imagine a class that hooks a state to an action etc.
    var ima = new CImagineIt(pers); 

    /*I may have picked a bad example because I'd have to do the same for all States
      but you get the idea. */
    ima.AddStateAction("Failed", (row) => { FixFailedUpload(serverObj, pers, row); });

    //Read all rows from database and fire actions accordingly
    ima.DoWork();
}
在本例中,通过该操作,只需检查服务器上的文件是否小于本地文件

private void FixFailedUpload(CUploadServerObj serverObj, CPersistantthingy pers, Dictionary<string,string> row)
{
    if(!row["ClassType"].Equals("CFileObj"))
    {
        //handle error
        return;
    }

    CFileObj obj;
    try
    {
        obj = DeSerialize(row["SerializedClass"]);
    }//Catch and handle etc etc

    //Are we using different credentials this boot? Then we can't check.
    if(obj.host != currenthost || obj.usr != currentuser)
    {
        //handle error and return
    }

    try
    {
        if(serverObj.RemoteSizeSmaller(obj.local, obj.remote))
        {
            if(serverObj.DeleteFromRemote(obj.remote))
            {
                pers.DeleteByKey(row["ID"]);
            }
        }
    }
    catch(RemoteFileNotExistsException)
    {
        //The file didn't even exist so no worries
    }
    catch(RemoteFileDeleteException)
    {
        //The file existed but could not be removed.. it's probably time to request manual user input now because that's just weird.
    }
}
我认为这可以奏效,但并不理想。可能上载失败,因为另一个进程已写入保留路径。这意味着我们不能使用获取的状态来证明删除文件的合理性。事后检查失败原因也不会有帮助,因为程序可能会在两者之间崩溃。也就是说我们被困在了被收购的公司里。或者,我们可能在写入的第一个字节和它的回调之间崩溃,这意味着即使我们确实获得了保留的文件路径,我们仍然被困在获取中

private void FixFailedUpload(CUploadServerObj serverObj, CPersistantthingy pers, Dictionary<string,string> row)
{
    if(!row["ClassType"].Equals("CFileObj"))
    {
        //handle error
        return;
    }

    CFileObj obj;
    try
    {
        obj = DeSerialize(row["SerializedClass"]);
    }//Catch and handle etc etc

    //Are we using different credentials this boot? Then we can't check.
    if(obj.host != currenthost || obj.usr != currentuser)
    {
        //handle error and return
    }

    try
    {
        if(serverObj.RemoteSizeSmaller(obj.local, obj.remote))
        {
            if(serverObj.DeleteFromRemote(obj.remote))
            {
                pers.DeleteByKey(row["ID"]);
            }
        }
    }
    catch(RemoteFileNotExistsException)
    {
        //The file didn't even exist so no worries
    }
    catch(RemoteFileDeleteException)
    {
        //The file existed but could not be removed.. it's probably time to request manual user input now because that's just weird.
    }
}