情况: 我做了一个应用程序更新程序。 问题是更新的应用程序使用的数据库可能不同。
我搜索了一个可以进行备份转储的.dll,并且可以单独地为不同的数据库类型恢复该转储。但遗憾的是没有一个可用。
我的第一个想法是为每个DatabaseType创建一个用例,并使用config.txt文件指定的正确用例。 但我的老板不喜欢这个解决方案。 所以我试着看看我是否可以NHibernate(它也用于发送更新文件)来进行数据库备份。可悲的是,尽管这是可能的,但这是不可行的。因为这会产生太多的开销。 我还看了一下Rhino.ETL,但遗憾的是没有足够的文档供我使用。
所以我想问一下是否有人知道其他任何可行的方法。 还是我辞职了,反对我的老板他的意愿,并使用每个用例的方法?
答案 0 :(得分:3)
我使用NHibernate进行备份和恢复。导出和导入中的实体顺序很重要
ExportData(config, config.BuildSessionFactory(), dataFile, config.ClassMappings.Select(m => m.EntityName));
public void ExportData(Configuration config, ISessionFactory factory, string dataFile, IEnumerable<string> entitiesToExport)
{
File.Delete(dataFile);
using (var zip = new ZipFile(dataFile) { CompressionMethod = CompressionMethod.Deflate, CompressionLevel = CompressionLevel.BestCompression })
using (var session = factory.OpenStatelessSession())
using (var tx = session.BeginTransaction())
{
foreach (var name in entitiesToExport)
{
// copy for closure
var entityName = name;
zip.AddEntry(entityName + ".csv", (tablename, outstream) =>
{
using (var writer = new StreamWriter(outstream))
{
// mapping der Klasse
var entityMap = config.GetClassMapping(entityName);
// only properties, no Collections (toMany)
var properties = entityMap.PropertyClosureIterator.Where(prop => prop.ColumnSpan > 0).ToArray();
// write csv(tsv) header
writer.WriteLine(string.Join(SEPERATOR, ToPropertyNames(properties)));
var classMetaData = factory.GetClassMetadata(entityName);
const int pagesize = 10000;
int page = 0;
IList<object> objects;
do
{
objects = session.CreateCriteria(entityName)
.SetFirstResult(page * pagesize)
.SetMaxResults(pagesize)
.AddOrder(Order.Asc(Projections.Id()))
.List<object>();
foreach (var obj in objects)
{
// get property values as string
var values = ToPropertyValues(factory, classMetaData, properties, string.Empty, obj).Select(Serialize);
writer.WriteLine(string.Join(SEPERATOR, values));
}
page++;
} while (objects.Count > 0);
}
});
}
zip.Save(dataFile);
}
}
并导入
public void ImportData(NHCfg.Configuration config, ISessionFactory factory, string dataFile)
{
using (var zip = new ZipFile(dataFile))
using (var session = factory.OpenSession())
using (var tx = session.BeginTransaction())
{
var filesAndMaps = from file in zip.EntryFileNames
join map in config.ClassMappings on Path.GetFileNameWithoutExtension(file) equals map.EntityName
select new { Filename = file, Entitymap = map };
foreach (var fileAndMap in filesAndMaps)
{
using (Stream stream = zip[fileAndMap.Filename].OpenReader())
using (StreamReader reader = new StreamReader(stream))
{
// mapping der Klasse
var classMetadata = factory.GetClassMetadata(fileAndMap.Entitymap.EntityName);
var propertyNames = reader.ReadLine().Split(SEPERATOR[0]).Select(str => str.EndsWith("_Id") ? str.Remove(str.Length - "_Id".Length) : str).ToArray();
// propertyNamen(/pfade) und typen aus dem mapping holen
List<string> names = new List<string>();
List<IType> itypes = new List<IType>();
names.AddRange(classMetadata.PropertyNames);
itypes.AddRange(classMetadata.PropertyTypes);
for (int index = names.Count - 1; index >= 0; index--)
{
var propertyType = itypes[index];
if (propertyType.IsComponentType)
{
var basename = names[index];
var componentType = (ComponentType)propertyType;
names.RemoveAt(index);
names.InsertRange(index, componentType.PropertyNames.Select(name => basename + "." + name));
itypes.RemoveAt(index);
itypes.InsertRange(index, componentType.Subtypes);
index += componentType.Subtypes.Length;
}
else if (propertyType.IsEntityType)
{
itypes[index] = factory.GetClassMetadata(((EntityType)propertyType).GetAssociatedEntityName()).IdentifierType;
}
}
// mit den namen aus der csv abgleichen um den index im array der namen und typen zu bestimmen
int[] indices = new int[propertyNames.Length];
for (int i = 0; i < indices.Length; i++)
{
indices[i] = names.IndexOf(propertyNames[i]);
}
object[] deserialisedValues = new object[itypes.Count];
object[] objectValues = new object[classMetadata.PropertyTypes.Length];
string line;
while ((line = reader.ReadLine()) != null)
{
string[] serialisedValues = line.Split(SEPERATOR[0]);
// Werte deserialisieren
for (int i = 0; i < serialisedValues.Length; i++)
{
// nur deserialisieren wenn es das property überhaupt noch gibt
if (indices[i] >= 0)
deserialisedValues[indices[i]] = Deserialize(serialisedValues[i], itypes[indices[i]].ReturnedClass);
}
int offset = 0;
for (int i = 0; i < objectValues.Length; i++)
{
var propertyType = classMetadata.PropertyTypes[i];
if (propertyType.IsComponentType)
{
objectValues[i] = ToComponentObject(deserialisedValues, ref offset, (ComponentType)propertyType, session);
}
else
{
objectValues[i] = (propertyType.IsEntityType && deserialisedValues[offset] != null) ?
session.Load(((ManyToOneType)propertyType).GetAssociatedEntityName(), deserialisedValues[offset]) :
deserialisedValues[offset];
offset++;
}
}
object entity = classMetadata.Instantiate(null, EntityMode.Poco);
classMetadata.SetPropertyValues(entity, objectValues, EntityMode.Poco);
session.Save(entity);
}
}
}
tx.Commit();
}
}
不存在的功能是读者的练习
答案 1 :(得分:0)
很好的回答Firo,可以把方法的代码和#34; ToPropertyNames&#34; &#34; ToPropertyValues&#34;和&#34; ToComponentObject&#34;?
我添加它以使用它,库DotNetZip是必需的