在将数据插入数据库之前检查数据是否存在于 sqlite-net 中

Checking if data exist in sqlite-net before inserting them to database

我正在制作一个将使用 "templates" 的 Xamarin.Android 应用程序,模板是这样的:

[Table("Templates")]
    public class Template
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
        public int Category { get; set; }
        //[TextBlob("imagesBlobbed")]
        [OneToMany, Unique]
        public List<TemplateImage> TemplateImages { get; set; }
        public string ImagesHash { get; set; }
        //public string imagesBlobbed { get; set; }
    }
    [Table("TemplateImages")]
    public class TemplateImage
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
        public int Category { get; set; }
        public string ImagesHash { get; set; }
        public int Image { get; set; }
        [ForeignKey(typeof(Template))]
        public int TemplateId { get; set; }
    }

我希望所有的 TemplateImages 对象在我的数据库中都是唯一的,属性 Unique 没有做任何事情,因为我猜是因为 TemplateImage table 有一个自动递增 ID将始终是唯一的,无论 Image 或 ImageHash 在 2 条记录中是否相同。

我在想我还能如何确定 TemplateImages 将是唯一的(Notice:当我说唯一时,我的意思是对于每个 TemplateImage,没有任何其他 List 具有相同顺序的确切 Image ).

我还使用图像的 ResourceID 作为图像,这是错误的,因为它们可能会在每次新编译的应用程序更新时发生变化。

所以我决定从图像中生成哈希 md5。他们是我找到的在 Xamarin.Android 中加载资源图像(我的图像是 vector .xml 文件的唯一方法就是把resource转成bitmap,再把bitmap转成byte,再把byte转成md5。

而且我也应该有一个用于模板项的哈希字符串。所以我通过将图像的所有 byte[] 合并为一个然后拥有它来为模板创建一个 md5 哈希。

我为此工作创建了这个疯狂的代码:

        public static string GetMD5Hash(byte[] content)
        {
            using (var md5 = MD5.Create())
            {
                byte[] computedHash = md5.ComputeHash(Encoding.UTF8.GetBytes(BitConverter.ToString(content).Replace("-", "")));
                return new System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary(computedHash).ToString();
            }
        }

        private static SQLiteConnection instance;
        public static SQLiteConnection db()
        {
            if (instance == null)
                instance = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "TemplatesData.db"));

            return instance;
        }
        public static void CloseConnection()
        {
            if (instance != null)
            {
                instance.Close();
                instance.Dispose();
                instance = null;
            }
        }

    }
    public class TemplateDB
    {
        public static byte[] ConcatByteList(List<byte[]> list)
        {
            return list
                .SelectMany(a => a)
                .ToArray();
        }

        public static Bitmap GetBitmapFromVectorDrawable(Context context, int drawableId)
        {
            Drawable drawable = ContextCompat.GetDrawable(context, drawableId);
            if (Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.Lollipop)
            {
                drawable = (DrawableCompat.Wrap(drawable)).Mutate();
            }

            Bitmap bitmap = Bitmap.CreateBitmap(drawable.IntrinsicWidth,
                    drawable.IntrinsicWidth, Bitmap.Config.Argb8888);
            Canvas canvas = new Canvas(bitmap);
            drawable.SetBounds(0, 0, canvas.Width, canvas.Height);
            drawable.Draw(canvas);

            return bitmap;
        }

        public byte[] DrawableToByteArray(int resourceId)
        {
            var context = AppState.ApplicationState;
            using (var bitmap = GetBitmapFromVectorDrawable(context, resourceId))
            {
                int size = bitmap.ByteCount;
                byte[] byteArray = new byte[size];
                ByteBuffer byteBuffer = ByteBuffer.Allocate(size);
                bitmap.CopyPixelsToBuffer(byteBuffer);
                byteBuffer.Rewind();
                byteBuffer.Get(byteArray);
                return byteArray;
            }
        }
        public static void AddTemplate(int category, List<int> images)
        {
            var templateDB = new TemplateDB();
            var imageByteList = new List<byte[]>();

            foreach (int image in images)
            {
                imageByteList.Add(templateDB.DrawableToByteArray(image));
            }
            var tmpl = new Template()
            {
                Category = category,
            };
            var img1 = new TemplateImage()
            {
                Category = category,
                Image = images[0],
                ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[0]),
            };
            var img2 = new TemplateImage()
            {
                Category = category,
                Image = images[1],
                ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[1]),
            };
            var img3 = new TemplateImage()
            {
                Category = category,
                Image = images[2],
                ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[2]),
            };
            var img4 = new TemplateImage()
            {
                Category = category,
                Image = images[3],
                ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[3]),
            };
            var img5 = new TemplateImage()
            {
                Category = category,
                Image = images[4],
                ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[4]),
            };
            tmpl.TemplateImages = new List<TemplateImage>() { img1, img2, img3, img4, img5 };
            tmpl.ImagesHash = DatabaseHelper.GetMD5Hash(ConcatByteList(imageByteList));
            var result = DatabaseHelper.db().Query<TemplateImage>("Select * from Templates where ImagesHash=?", tmpl.ImagesHash);
            if (result.Count == 0)
            {
            DatabaseHelper.db().InsertAll(tmpl.TemplateImages);
            DatabaseHelper.db().Insert(tmpl);
            DatabaseHelper.db().UpdateWithChildren(tmpl);
            }
        }

突然出现内存不足异常。

在考虑了一段时间我应该停止编程并开始肚皮舞之后,我认为自从我在 sqlite AddTemplate 函数中提供了一个 ResourceIds 我必须手动创建(无法避免),那么为什么不给他们我自己的字符串哈希码并比较它以查找记录是否存在?

正确的做法是什么?

最后,我想使用 guid 作为额外的 id 字段 并添加唯一参数,这样我就可以确保插入了哪些模板,但我决定使用 transactions。通过事务,我可以知道我的所有数据都已创建或 none 并将创建保存到 SharedPreferences 中的变量中,避免重新创建甚至检查数据库是否存在。

为了检查数据是否正确更新并且不需要重新创建,我使用了以下代码:

public bool FirstRun { get; set; } = true;
public int DatabaseCreatedVersionOf { get; set; } = -1;

public override void OnCreate()
{
    base.OnCreate();
}

public void UpdateDatabaseCreatedVersion()
{
    DatabaseCreatedVersionOf = Preferences.Get(Settings.DatabaseCreatedVersionOfKey,
       Settings.DatabaseCreatedVersionOfDefault);
}

public void CreateTemplateDB()
{
    UpdateDatabaseCreatedVersion();
    if (DatabaseCreatedVersionOf == -1)
        TemplateDB.CreateDB();
    FirstRun = false;
}
public Template GetTemplateById(int id)
{
    if (FirstRun)
    {
        CreateTemplateDB();
        FirstRun = false;
    }

    return TemplateDB.GetTemplate(id);
}
public List<Template> GetAllTemplates()
{
    if (FirstRun)
    {
        CreateTemplateDB();
        FirstRun = false;
    }

    return TemplateDB.GetAllTemplates();
}

现在我所要做的就是调用 GetTemplateByIdGetAllTemplates,如果需要任何创建,它就会发生。