简体   繁体   中英

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

Im making an Xamarin.Android application that will use "templates" , template is this:

[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; }
    }

i want all the TemplateImages objects to be unique in my database, the attribute Unique doesnt do anything, cause i guess because TemplateImage table has an auto increment Id will always be unique, no matter if the Image or ImageHash is the same in 2 records.

I was thinking then how else i can be sure that TemplateImages will be unique ( Notice : when i say unique i mean that there isn't any other List that have the exact Image for every TemplateImage , in the same order).

Also i was using the ResourceID of the images, as image, which is wrong cause they might change on every new compiled app update.

So i decided to make a hash md5 from the images. They only way that i have find to load resource images (my images are vector .xml files) in Xamarin.Android is by converting the resource to bitmap, and then convert the bitmap to byte, and then the byte to md5.

And also i should have a hash string for the template item too. so im creating an md5 hash for the template by combining all the byte[] of the images into one and then hasing that.

I create this crazy code for this job:

        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);
            }
        }

Which all of a sudden, gives out of memory exception.

After thinking for a while that i should stop programming and start belly dancing, i think that since im giving in sqlite AddTemplate function a list of ResourceIds that i have to manually create (can't avoid it), then why not to give them my own string hash code and compare that to find if a record exist?

What is the correct approach?

In the end, i was thinking to use guid as extra id fields and add the parameter unique,in that case i could be sure which templates have been inserted, but i decided to use transactions . With transactions i can know that all my data is created or none and also save the creation into a variable in SharedPreferences and avoid recreating or even checking if the database exist.

For checking if the data is correctly updated and no recreation is needed i used this code:

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();
}

Now all i have to do is call GetTemplateById or GetAllTemplates and if any creation is required, it will happen.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM