我们日常进行程序的更新升级可能会用到winform的发布功能,但有些文件可能会无法伴随着发布一同发布出去或者每次发布后文件的数据被覆盖,下面一起看一下怎么解决:
winform发布功能参考我另一篇文章https://blog.csdn.net/qq_39569480/article/details/127212245
仅限于读的文件:
这里我们以json文件为例,在项目中创建json文件,保存时以utf-8格式保存https://blog.csdn.net/qq_39569480/article/details/119610914 ,有一点需要注意这里创建的文件虽然可以同发布一同将文件发布出去,使用时仅限于读取,如果在程序运行过程中写入json文件,在下次程序升级时会覆盖原来写入的内容。
文件属性选择
右键项目属性——发布——应用程序文件,找到创建的文件选择包括,这样在发布时文件会同程序一同发布。
可读可写的文件
如果想发布可读可写的文件我们需要换一个思路,如果直接通过项目发布在程序运行时写入文件数据后,如果程序下次升级会重新覆盖这个文件之前写入的数据就会被覆盖掉。
第一种思路可以在程序中增加一个文件,在程序首次运行时检测本地有没有此文件如果没有则复制程序目录下的文件到本地指定文件夹,我们以后对这个文件进行操作。
第二种思路我们在程序启动时检测本地有无文件,没有文件则创建文件,动态的数据存在这个新创建的文件中,
每次读写这个新创建的文件
在程序的load事件中检测有无文件,没有则创建
if (!Directory.Exists(@"C:\SetUp")) Directory.CreateDirectory(@"C:\SetUp");
try
{
string FilePath = $@"C:\SetUp\appsettings.xml";
XmlDocument xmlDoc = new XmlDocument();
if (File.Exists(FilePath))
{
xmlDoc.Load(FilePath);
XmlNode 二级 = xmlDoc.SelectSingleNode($"Configure/{InstrumentName}");
if (二级 == null)
{
XmlNode memberlist = xmlDoc.SelectSingleNode($"Configure");
XmlElement lq = xmlDoc.CreateElement(InstrumentName);
memberlist.AppendChild(lq);
}
//读取节点数据
if (二级 != null)
{
//二级.RemoveAll();
XmlNode node1 = xmlDoc.SelectSingleNode($"Configure/{InstrumentName}/SqlServerInstanceName");
if (node1 != null) 二级.RemoveChild(node1);
XmlNode node2 = xmlDoc.SelectSingleNode($"Configure/{InstrumentName}/SqlServerDataBaseName");
if (node2 != null) 二级.RemoveChild(node2);
}
XmlElement el1 = xmlDoc.CreateElement("SqlServerInstanceName");
el1.InnerText = InstanceName;
XmlElement el2 = xmlDoc.CreateElement("SqlServerDataBaseName");
el2.InnerText = DataBaseName;
XmlNode 二级节点 = xmlDoc.SelectSingleNode($"Configure/{InstrumentName}");
二级节点.AppendChild(el1); 二级节点.AppendChild(el2);
xmlDoc.Save(FilePath);
}
else
{
//加入XML的声明段落,Save方法不再xml上写出独立属性
xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null));
//加入根元素
XmlElement root = xmlDoc.CreateElement("Configure");
xmlDoc.AppendChild(root);
XmlElement memberlist = xmlDoc.CreateElement($"{InstrumentName}");
XmlElement el1 = xmlDoc.CreateElement("SqlServerInstanceName");
el1.InnerText = InstanceName;
XmlElement el2 = xmlDoc.CreateElement("SqlServerDataBaseName");
memberlist.AppendChild(el1); memberlist.AppendChild(el2);
root.AppendChild(memberlist);
xmlDoc.Save(FilePath);
}
bool Result = ReadLocalSetUpFile();
if (Result) MessageBox.Show("配置成功");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
db文件
数据库文件或者其它文件同上原理,我们先将文件放在项目中,属性选择始终复制,生成操作为内容,这样发布时会同程序一同发布
然后在程序运行时将文件拷贝至本地的指定路径
string ApplicationPath = Application.StartupPath;
string LocalDirectory=@"C:\";
string FilePath = $@"{LocalDirectory}MicrosoftStorage";
if (!Directory.Exists(LocalDirectory)) Directory.CreateDirectory(LocalDirectory);
if (!File.Exists(FilePath)) File.Copy($"{ApplicationPath}\\MicrosoftStorage", $@"{LocalDirectory}MicrosoftStorage", true);//允许覆盖目的地的同名文件
复制到指定文件夹后以后我们只需要读取本地的文件即可,这样每次发布升级就不会受影响
到这里可能大家还会有疑问,如果这个数据库以后有字段的新增修改怎么办呢?这里给大家提供一个思路:
首先我们可以在程序中创建一个文件,文件中记录所有表和字段的内容,程序每次升级时检测文件中的字段或表是否与数据库的表字段一致,不一致则新增
检测字段方法
可参考我另一篇文章检测sqlite字段是否存在https://blog.csdn.net/qq_39569480/article/details/128465934
private void CheckField()
{
try
{
List<FieldSetUpModel> Fields = new List<FieldSetUpModel>();
using var streamReader = new StreamReader(@"Menu\Field.json", Encoding.UTF8);
string FieldJson = streamReader.ReadToEnd();
Fields = JsonConvert.DeserializeObject<List<FieldSetUpModel>>(FieldJson);
FieldSetUpModel TableSetUp = new FieldSetUpModel();
if (InstrumentName == "Excel文件") TableSetUp = Fields.Where(a => a.InstrumentName.Contains(InstrumentName) && a.Type == InstrumentType)?.FirstOrDefault();
else TableSetUp = Fields.Where(a => a.InstrumentName.Contains(InstrumentName))?.FirstOrDefault();
string[] FieldArray = TableSetUp.Fileds.Split(',');
string TableName = TableSetUp.TableName;
string LocalDB = $"{LocalDirectory}MicrosoftStorage";
var (FieldCheckResult, FieldMsg) = _checkField.CheckFieldPresenceOrNot(TableName, FieldArray, _DB, LocalDB);
if (!FieldCheckResult) MessageBox.Show("检查新增本地库表和字段时出现异常!请联系管理员");
}
catch (Exception e)
{
Log.Error($"检查新增本地数据库字段时失败:{e.Message + e.StackTrace + e.InnerException}");
throw;
}
}
public class FieldSetUpModel
{
public string InstrumentName { get; set; }
public string Type { get; set; }
public string TableName { get; set; }
public string Fileds { get; set; }
}
public (bool, string) CheckFieldPresenceOrNot(string TableName,string[] Field, DBConnHelper DB,string LocalDBPath) {
using (IDbConnection db = DB.DBConnection("Sqlite", "", LocalDBPath, ""))
{
try
{
string TableSql = $"select count(*) from sqlite_master where type='table' and name='{TableName}' ";
int ExistTable= db.ExecuteScalar<int>(TableSql);
if (ExistTable<1)
{//表不存在则新增表
List<string> NeedAddField = new List<string>();
for (int i = 0; i < Field.Length; i++)
{
NeedAddField.Add($"{Field[i]} TEXT");
}
string FieldStr = string.Join(",", NeedAddField);
string AddTableSql = $"create table {TableName}({FieldStr})";
db.Execute(AddTableSql);
string CreateAfter = $"select count(*) from sqlite_master where type='table' and name='{TableName}'";
bool TableAddResult = db.ExecuteScalar<int>(TableSql)>0;
if (!TableAddResult) return (false, $"添加本地库新表时失败,请联系管理员");
else return (true, "");
}
List<string> FailureField = new List<string>();
for (int i = 0; i < Field.Length; i++)
{
string Sql = $"PRAGMA table_info({TableName})";
List<TableStructureModel> TableStructure= db.Query<TableStructureModel>(Sql).ToList();
bool Exist= TableStructure.Where(a=>a.name==Field[i]).ToList().Count()>0;
if (!Exist)
{//如果不存在则新增字段
string AddField = $"alter table {TableName} add column '{Field[i]}' TEXT";
bool EditResult= db.Execute(AddField)>0;
if (!EditResult)
{
FailureField.Add(Field[i]);
}
}
}
if (FailureField.Count>0)
{
string ErrorField= string.Join(",",FailureField);
return (false,$"添加本地库字段:{ErrorField}时失败,请联系管理员");
}
return (true,"");
}
catch (Exception ex)
{
Log.Error($"检查新增本地数据表或库字段时失败:{ex.Message + ex.StackTrace + ex.InnerException}");
return (false,ex.Message);
}
}
}
public class TableStructureModel
{
public string cid { get; set; }
public string name { get; set; }
public string type { get; set; }
public string notnull { get; set; }
public string dflt_value { get; set; }
public string pk { get; set; }
}