├── SimpleDatabase ├── Resources │ └── Resource.designer.cs ├── IPopulated.cs ├── SimpleDatabase.csproj ├── SqliteExtensions.cs ├── Model │ └── Group.cs └── SimpleDatabase.cs ├── Samples ├── iOS │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Entitlements.plist │ ├── Main.cs │ ├── AppDelegate.cs │ ├── LaunchScreen.storyboard │ ├── Info.plist │ └── Sample.iOS.csproj ├── Droid │ ├── Resources │ │ ├── drawable │ │ │ └── icon.png │ │ ├── drawable-hdpi │ │ │ └── icon.png │ │ ├── drawable-xhdpi │ │ │ └── icon.png │ │ ├── drawable-xxhdpi │ │ │ └── icon.png │ │ ├── layout │ │ │ ├── Toolbar.axml │ │ │ └── Tabbar.axml │ │ ├── values │ │ │ └── styles.xml │ │ └── AboutResources.txt │ ├── Properties │ │ ├── AndroidManifest.xml │ │ └── AssemblyInfo.cs │ ├── Assets │ │ └── AboutAssets.txt │ ├── MainActivity.cs │ └── Sample.Droid.csproj └── Sample │ ├── ViewModels │ ├── SongsViewModel.cs │ └── ContactsViewModel.cs │ ├── Pages │ ├── SongsPage.xaml.cs │ ├── ContactsPage.xaml.cs │ ├── SongsPage.xaml │ └── ContactsPage.xaml │ ├── Model │ ├── Song.cs │ └── Person.cs │ ├── Sample.shproj │ ├── Data │ ├── SongDatabase.cs │ └── Database.cs │ ├── App.cs │ ├── Sample.projitems │ └── SimpleDatabaseSource.cs ├── SimpleDatabase.iOS ├── Info.plist └── SimpleDatabase.iOS.csproj ├── .gitignore ├── README.md ├── SimpleDatabase.sln ├── SimpleDatabase.nuspec ├── SimpleDatabase.Samples.sln └── LICENSE /SimpleDatabase/Resources/Resource.designer.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Samples/iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Samples/Droid/Resources/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clancey/SimpleDatabase/HEAD/Samples/Droid/Resources/drawable/icon.png -------------------------------------------------------------------------------- /Samples/Droid/Resources/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clancey/SimpleDatabase/HEAD/Samples/Droid/Resources/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /Samples/Droid/Resources/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clancey/SimpleDatabase/HEAD/Samples/Droid/Resources/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /Samples/Droid/Resources/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clancey/SimpleDatabase/HEAD/Samples/Droid/Resources/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /SimpleDatabase.iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .droidres/ 2 | /Make.config.local 3 | /.dist 4 | /.vs 5 | *~ 6 | *.pidb 7 | *.userprefs 8 | *.resources 9 | bin/ 10 | obj/ 11 | .DS_Store 12 | test-results 13 | *.suo 14 | Components/ 15 | [Pp]ackages 16 | *.nupkg 17 | -------------------------------------------------------------------------------- /Samples/iOS/Entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Samples/Sample/ViewModels/SongsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace Sample 3 | { 4 | public class SongsViewModel 5 | { 6 | public SimpleDatabaseSource Songs { get; set; } = new SimpleDatabaseSource (SongDatabase.Main); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SimpleDatabase/IPopulated.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace SimpleDatabase 7 | { 8 | public interface IPopulated 9 | { 10 | void Populated(); 11 | } 12 | } -------------------------------------------------------------------------------- /Samples/Sample/ViewModels/ContactsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace Sample 3 | { 4 | public class ContactsViewModel 5 | { 6 | public SimpleDatabaseSource Contacts { get; set; } = new SimpleDatabaseSource (Database.Main); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Samples/Sample/Pages/SongsPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Xamarin.Forms; 5 | 6 | namespace Sample 7 | { 8 | public partial class SongsPage : ContentPage 9 | { 10 | public SongsPage() 11 | { 12 | InitializeComponent(); 13 | BindingContext = new SongsViewModel(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Samples/Droid/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Samples/Sample/Pages/ContactsPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Xamarin.Forms; 5 | 6 | namespace Sample 7 | { 8 | public partial class ContactsPage : ContentPage 9 | { 10 | public ContactsPage() 11 | { 12 | InitializeComponent(); 13 | BindingContext = new ContactsViewModel(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Samples/Droid/Resources/layout/Toolbar.axml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Samples/Sample/Model/Song.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleDatabase; 3 | using SQLite; 4 | 5 | namespace Sample 6 | { 7 | public class Song 8 | { 9 | [PrimaryKey] 10 | public string Id { get; set; } 11 | public string Title {get;set;} 12 | [OrderBy] 13 | public string TitleNorm { get; set; } 14 | public string Artist {get;set;} 15 | [GroupBy] 16 | public string IndexCharacter { get; set; } 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Samples/Droid/Resources/layout/Tabbar.axml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Samples/iOS/Main.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Foundation; 6 | using UIKit; 7 | 8 | namespace Sample.iOS 9 | { 10 | public class Application 11 | { 12 | // This is the main entry point of the application. 13 | static void Main(string[] args) 14 | { 15 | // if you want to use a different Application Delegate class from "AppDelegate" 16 | // you can specify it here. 17 | UIApplication.Main(args, null, "AppDelegate"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Samples/Sample/Pages/SongsPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Foundation; 6 | using UIKit; 7 | 8 | namespace Sample.iOS 9 | { 10 | [Register("AppDelegate")] 11 | public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate 12 | { 13 | public override bool FinishedLaunching(UIApplication app, NSDictionary options) 14 | { 15 | global::Xamarin.Forms.Forms.Init(); 16 | 17 | LoadApplication(new App()); 18 | 19 | return base.FinishedLaunching(app, options); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Samples/Sample/Pages/ContactsPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Samples/Droid/Assets/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories) and given a Build Action of "AndroidAsset". 3 | 4 | These files will be deployed with your package and will be accessible using Android's 5 | AssetManager, like this: 6 | 7 | public class ReadAsset : Activity 8 | { 9 | protected override void OnCreate (Bundle bundle) 10 | { 11 | base.OnCreate (bundle); 12 | 13 | InputStream input = Assets.Open ("my_asset.txt"); 14 | } 15 | } 16 | 17 | Additionally, some Android functions will automatically load asset files: 18 | 19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); 20 | -------------------------------------------------------------------------------- /Samples/Droid/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Android.App; 4 | using Android.Content; 5 | using Android.Content.PM; 6 | using Android.Runtime; 7 | using Android.Views; 8 | using Android.Widget; 9 | using Android.OS; 10 | 11 | namespace Sample.Droid 12 | { 13 | [Activity(Label = "Sample.Droid", Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] 14 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity 15 | { 16 | protected override void OnCreate(Bundle bundle) 17 | { 18 | TabLayoutResource = Resource.Layout.Tabbar; 19 | ToolbarResource = Resource.Layout.Toolbar; 20 | 21 | base.OnCreate(bundle); 22 | 23 | global::Xamarin.Forms.Forms.Init(this, bundle); 24 | 25 | LoadApplication(new App()); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Samples/Sample/Sample.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {F2472651-1440-4A8B-B034-19BE2A507AF4} 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/Sample/Data/SongDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using SimpleDatabase; 5 | 6 | namespace Sample 7 | { 8 | public class SongDatabase: SimpleDatabaseConnection 9 | { 10 | public SongDatabase () : base(dbPath) 11 | { 12 | CreateTable (); 13 | MakeClassInstant (); 14 | } 15 | 16 | static SongDatabase main; 17 | public static readonly string dbPath = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), "music.db"); 18 | public static SongDatabase Main 19 | { 20 | get{ 21 | if (main == null) 22 | { 23 | downloadIfNeeded(); 24 | main = new SongDatabase (); 25 | 26 | } 27 | return main; 28 | } 29 | } 30 | static void downloadIfNeeded() 31 | { 32 | if (File.Exists(dbPath)) 33 | File.Delete(dbPath); 34 | return; 35 | //var client = new WebClient (); 36 | //client.DownloadFile ("https://www.dropbox.com/s/r25qjjae25mk29g/music.db", dbPath); 37 | } 38 | 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /Samples/Sample/Model/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleDatabase; 3 | using SQLite; 4 | 5 | namespace Sample 6 | { 7 | public class Person 8 | { 9 | public Person () 10 | { 11 | } 12 | [PrimaryKeyAttribute, AutoIncrement] 13 | public int Id {get;set;} 14 | public string FirstName {get;set;} 15 | [OrderByAttribute] 16 | public string MiddleName { get; set; } 17 | public string LastName { get; set; } 18 | string indexCharacter; 19 | [GroupBy] 20 | public string IndexCharacter { 21 | get { 22 | if(string.IsNullOrWhiteSpace(indexCharacter) && !string.IsNullOrWhiteSpace(LastName)) 23 | indexCharacter = LastName.Substring(0, 1); 24 | return indexCharacter; 25 | } 26 | set { indexCharacter = value;} 27 | } 28 | public string Email { get; set; } 29 | public string PhoneNumber {get;set;} 30 | public string DisplayName => $"{LastName}, {FirstName}"; 31 | public override string ToString () 32 | { 33 | return string.Format ("{0} , {1}", LastName, FirstName); 34 | } 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /SimpleDatabase/SimpleDatabase.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1;net6.0;net6.0-ios;net6.0-android;net6.0-maccatalyst 5 | SimpleDatabase 6 | 7 | 8 | TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_1;CORE; 9 | preview 10 | 11 | 12 | TRACE;RELEASE;NETSTANDARD;NETSTANDARD2_1;CORE; 13 | preview 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Samples/Droid/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using Android.App; 4 | 5 | // Information about this assembly is defined by the following attributes. 6 | // Change them to the values specific to your project. 7 | 8 | [assembly: AssemblyTitle("Sample.Droid")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("")] 13 | [assembly: AssemblyCopyright("(c) James Clancey")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 18 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 19 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 20 | 21 | [assembly: AssemblyVersion("1.0.0")] 22 | 23 | // The following attributes are used to specify the signing key for the assembly, 24 | // if desired. See the Mono documentation for more information about signing. 25 | 26 | //[assembly: AssemblyDelaySign(false)] 27 | //[assembly: AssemblyKeyFile("")] 28 | -------------------------------------------------------------------------------- /Samples/Droid/Resources/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /Samples/iOS/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Samples/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | SimpleDatabase 7 | CFBundleName 8 | SimpleDatabase 9 | CFBundleIdentifier 10 | com.companyname.sample 11 | CFBundleShortVersionString 12 | 1.0 13 | CFBundleVersion 14 | 1.0 15 | LSRequiresIPhoneOS 16 | 17 | MinimumOSVersion 18 | 8.0 19 | UIDeviceFamily 20 | 21 | 1 22 | 2 23 | 24 | UILaunchStoryboardName 25 | LaunchScreen 26 | UIRequiredDeviceCapabilities 27 | 28 | armv7 29 | 30 | UISupportedInterfaceOrientations 31 | 32 | UIInterfaceOrientationPortrait 33 | UIInterfaceOrientationLandscapeLeft 34 | UIInterfaceOrientationLandscapeRight 35 | 36 | UISupportedInterfaceOrientations~ipad 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationPortraitUpsideDown 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | XSAppIconAssets 44 | Assets.xcassets/AppIcon.appiconset 45 | 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simple Database 2 | ================ 3 | 4 | Creating fast, responsive and grouped Tableviews is hard. Espcially with very large data sets. Simple Database takes care of this for you by extending [SQLite-net](https://github.com/praeclarum/sqlite-net) 5 | 6 | Available on Nuget 7 | ================ 8 | 9 | https://www.nuget.org/packages/Clancey.SimpleDatabase/ 10 | 11 | 12 | API 13 | ================ 14 | 15 | Simple tables gives you the interface you need to populate a ListView 16 | 17 | ```cs 18 | Database.Main.RowsInSection(section); 19 | 20 | Database.Main.NumberOfSections(); 21 | 22 | Database.Main.ObjectForRow(section, row); 23 | 24 | Database.Main.SectionHeader(section); 25 | 26 | Database.Main.QuickJump(); 27 | 28 | ``` 29 | 30 | Model Attributes 31 | === 32 | Add attributes for OrderBy and Grouping 33 | 34 | ```cs 35 | 36 | class MyClass 37 | { 38 | [Indexed, PrimaryKey] 39 | public string Id {get;set;} 40 | 41 | //Typically this is just one letter, and the first letter of the displayed text 42 | [GroupBy] 43 | public virtual string IndexCharacter {get;set;} 44 | 45 | [OrderBy] 46 | public string Name {get;set;} 47 | } 48 | ``` 49 | 50 | 51 | GroupInfo 52 | ================ 53 | 54 | Sometimes you need to filter or Add dynamically compose a query. Simple Auth uses named parameters 55 | 56 | ```cs 57 | var artist = new { Id = "Foo"}; 58 | var group = Database.Main.GetGroupInfo().Clone(); 59 | group.Filter = "ArtistId = @ArtistId"; 60 | group.Params["@ArtistId"] = artist.Id; 61 | 62 | Database.Main.RowsInSection(group , section); 63 | 64 | ``` 65 | 66 | #Open Source Software 67 | This project was built on the shoulders of giants! 68 | 69 | - [SQLite-net](https://github.com/praeclarum/sqlite-net) 70 | -------------------------------------------------------------------------------- /SimpleDatabase.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleDatabase", "SimpleDatabase\SimpleDatabase.csproj", "{4FE40048-B716-42ED-9E26-C114689BAAFD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleDatabase.iOS", "SimpleDatabase.iOS\SimpleDatabase.iOS.csproj", "{E8B052D6-9C21-4B3F-9520-29D7B38513C5}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(MonoDevelopProperties) = preSolution 29 | StartupItem = InstantSample\InstantiOSSample.csproj 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Samples/Sample/Data/Database.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using SimpleDatabase; 6 | using System.Net; 7 | 8 | namespace Sample 9 | { 10 | public class Database : SimpleDatabaseConnection 11 | { 12 | public Database () : this(dbPath) 13 | { 14 | } 15 | public Database(string path) : base(path) 16 | { 17 | CreateTable(); 18 | } 19 | 20 | static Database main; 21 | public static readonly string dbPath = Path.Combine (Environment.GetFolderPath(Environment.SpecialFolder.Personal), "contacts.db"); 22 | 23 | public static Database Main { 24 | get { 25 | if (main == null) 26 | main = new Database (); 27 | return main; 28 | } 29 | } 30 | 31 | public Task InsertPeople (List people) 32 | { 33 | return Task.Factory.StartNew (delegate { 34 | 35 | this.InsertAll (people); 36 | }); 37 | } 38 | 39 | public static Task SetDatabase1000() 40 | { 41 | return DownloadDAtabase("https://www.dropbox.com/s/kob54oioz56rsnb/contacts-1000.db?dl=1", "contacts-1000.db"); 42 | 43 | } 44 | 45 | public static Task SetDatabase10000() 46 | { 47 | return DownloadDAtabase("https://www.dropbox.com/s/llxh3zafm16lcrr/contacts-10000.db?dl=1", "contacts-10000.db"); 48 | 49 | } 50 | 51 | public static Task SetDatabase20000() 52 | { 53 | return DownloadDAtabase("https://www.dropbox.com/s/foyjdl9yv894ssx/contacts-20000.db?dl=1", "contacts-20000.db"); 54 | } 55 | 56 | static async Task DownloadDAtabase(string url, string database) 57 | { 58 | var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), database); 59 | if (!File.Exists(path)) 60 | { 61 | var client = new WebClient(); 62 | await client.DownloadFileTaskAsync(url, path); 63 | } 64 | main = new Database(path); 65 | } 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /Samples/Droid/Resources/AboutResources.txt: -------------------------------------------------------------------------------- 1 | Images, layout descriptions, binary blobs and string dictionaries can be included 2 | in your application as resource files. Various Android APIs are designed to 3 | operate on the resource IDs instead of dealing with images, strings or binary blobs 4 | directly. 5 | 6 | For example, a sample Android app that contains a user interface layout (main.axml), 7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 8 | would keep its resources in the "Resources" directory of the application: 9 | 10 | Resources/ 11 | drawable/ 12 | icon.png 13 | 14 | layout/ 15 | main.axml 16 | 17 | values/ 18 | strings.xml 19 | 20 | In order to get the build system to recognize Android resources, set the build action to 21 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 22 | instead operate on resource IDs. When you compile an Android application that uses resources, 23 | the build system will package the resources for distribution and generate a class called "R" 24 | (this is an Android convention) that contains the tokens for each one of the resources 25 | included. For example, for the above Resources layout, this is what the R class would expose: 26 | 27 | public class R { 28 | public class drawable { 29 | public const int icon = 0x123; 30 | } 31 | 32 | public class layout { 33 | public const int main = 0x456; 34 | } 35 | 36 | public class strings { 37 | public const int first_string = 0xabc; 38 | public const int second_string = 0xbcd; 39 | } 40 | } 41 | 42 | You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main 43 | to reference the layout/main.axml file, or R.strings.first_string to reference the first 44 | string in the dictionary file values/strings.xml. 45 | -------------------------------------------------------------------------------- /Samples/Sample/App.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Xamarin.Forms; 4 | 5 | namespace Sample 6 | { 7 | public class App : Application 8 | { 9 | ActivityIndicator spinner; 10 | public App() 11 | { 12 | spinner = new ActivityIndicator(); 13 | // The root page of your application 14 | var content = new ContentPage 15 | { 16 | Title = "Sample", 17 | Content = new TableView( 18 | new TableRoot { 19 | new TableSection("Download database"){ 20 | CreateCell("1,000 people",async ()=>{ 21 | await SetupDatabase(Database.SetDatabase1000()); 22 | }), 23 | CreateCell("10,000 people",async ()=>{ 24 | await SetupDatabase(Database.SetDatabase10000()); 25 | }), 26 | CreateCell("20,000 people",async ()=>{ 27 | await SetupDatabase(Database.SetDatabase20000()); 28 | }), 29 | new ViewCell{ 30 | View = spinner, 31 | }, 32 | }, 33 | } 34 | ), 35 | }; 36 | 37 | MainPage = new NavigationPage(content); 38 | } 39 | 40 | TextCell CreateCell(string text, Action action) 41 | { 42 | var cell = new TextCell 43 | { 44 | Text = text, 45 | }; 46 | cell.Tapped += (sender, e) => action?.Invoke(); 47 | return cell; 48 | } 49 | async Task SetupDatabase(Task setupDatabase) 50 | { 51 | spinner.IsRunning = true; 52 | try 53 | { 54 | await setupDatabase; 55 | await MainPage.Navigation.PushAsync(new ContactsPage()); 56 | 57 | } 58 | catch (Exception ex) 59 | { 60 | this.MainPage.DisplayAlert("Error", ex.Message, "Ok"); 61 | } 62 | finally 63 | { 64 | spinner.IsRunning = false; 65 | } 66 | } 67 | 68 | protected override void OnStart() 69 | { 70 | // Handle when your app starts 71 | } 72 | 73 | protected override void OnSleep() 74 | { 75 | // Handle when your app sleeps 76 | } 77 | 78 | protected override void OnResume() 79 | { 80 | // Handle when your app resumes 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Samples/Sample/Sample.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | {F2472651-1440-4A8B-B034-19BE2A507AF4} 7 | 8 | 9 | Sample 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ContactsPage.xaml 20 | 21 | 22 | SongsPage.xaml 23 | 24 | 25 | 26 | 27 | 28 | 29 | MSBuild:UpdateDesignTimeXaml 30 | 31 | 32 | MSBuild:UpdateDesignTimeXaml 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SimpleDatabase.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Clancey.SimpleDatabase 5 | 1.1.6 6 | Clancey 7 | https://github.com/Clancey/SimpleDatabase/blob/master/LICENSE 8 | https://github.com/Clancey/SimpleDatabase 9 | false 10 | The fastest way to group large data sets for mobile 11 | SimpleDatabase is built on top of Sqlite-Net. It gives you everything you need to build fast grouped TableViews in Xamarin 12 | Updated Nugets 13 | James Clancey 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /SimpleDatabase.iOS/SimpleDatabase.iOS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5} 7 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 8 | Library 9 | SimpleDatabase 10 | Resources 11 | SimpleDatabase 12 | 8.0.30703 13 | 2.0 14 | 15 | 16 | True 17 | full 18 | False 19 | bin\Debug 20 | DEBUG;iOS 21 | prompt 22 | 4 23 | False 24 | true 25 | true 26 | 27 | 28 | none 29 | True 30 | bin\Release 31 | prompt 32 | 4 33 | False 34 | iOS 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | IPopulated.cs 54 | 55 | 56 | SimpleDatabase.cs 57 | 58 | 59 | SqliteExtensions.cs 60 | 61 | 62 | Model\Group.cs 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /SimpleDatabase/SqliteExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Threading.Tasks; 6 | 7 | namespace SQLite 8 | { 9 | public static class SqliteExtensions 10 | { 11 | public static Dictionary CreateTables(this SQLiteConnection connection, params Type[] types) 12 | { 13 | //CreateTablesResult Result has internal constructor 14 | var results = new Dictionary(); 15 | foreach (Type type in types) 16 | { 17 | try 18 | { 19 | var aResult = connection.CreateTable(type); 20 | results[type] = aResult; 21 | } 22 | catch (Exception) 23 | { 24 | Debug.WriteLine("Error creating table for {0}", type); 25 | throw; 26 | } 27 | } 28 | 29 | return results; 30 | } 31 | 32 | public static int Delete(this SQLiteConnection connection, object objectToDelete, Type type) 33 | { 34 | var map = connection.GetMapping(type); 35 | var pk = map.PK; 36 | if (pk == null) 37 | { 38 | throw new NotSupportedException("Cannot delete " + map.TableName + ": it has no PK"); 39 | } 40 | var q = $"delete from \"{map.TableName}\" where \"{pk.Name}\" = ?"; 41 | var count = connection.Execute(q, pk.GetValue(objectToDelete)); 42 | 43 | return count; 44 | } 45 | 46 | public static int InsertOrReplaceAll( this SQLiteConnection connection, IEnumerable objects, bool runInTransaction = true) 47 | { 48 | var c = 0; 49 | if (runInTransaction) 50 | { 51 | connection.RunInTransaction(() => 52 | { 53 | foreach (var r in objects) 54 | { 55 | c += connection.InsertOrReplace(r); 56 | } 57 | }); 58 | } 59 | else 60 | { 61 | foreach (var r in objects) 62 | { 63 | c += connection.InsertOrReplace(r); 64 | } 65 | } 66 | return c; 67 | } 68 | 69 | public static int InsertOrReplaceAll(this SQLiteConnection connection, IEnumerable objects, Type objType, bool runInTransaction = true) 70 | { 71 | var c = 0; 72 | if (runInTransaction) 73 | { 74 | connection.RunInTransaction(() => 75 | { 76 | foreach (var r in objects) 77 | { 78 | c += connection.InsertOrReplace(r, objType); 79 | } 80 | }); 81 | } 82 | else 83 | { 84 | foreach (var r in objects) 85 | { 86 | c += connection.InsertOrReplace(r); 87 | } 88 | } 89 | return c; 90 | } 91 | 92 | public static int DeleteAll(this SQLiteConnection connection, IEnumerable objects) 93 | { 94 | var c = 0; 95 | connection.RunInTransaction(() => 96 | { 97 | foreach (var r in objects) 98 | { 99 | c += connection.Delete(r); 100 | } 101 | }); 102 | return c; 103 | } 104 | public static int DeleteAll(this SQLiteConnection connection, IEnumerable objects, Type type) 105 | { 106 | var c = 0; 107 | connection.RunInTransaction(() => 108 | { 109 | foreach (var r in objects) 110 | { 111 | c += connection.Delete(r, type); 112 | } 113 | }); 114 | return c; 115 | } 116 | 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Samples/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "iphone", 5 | "size": "29x29", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "iphone", 10 | "size": "29x29", 11 | "scale": "2x" 12 | }, 13 | { 14 | "idiom": "iphone", 15 | "size": "29x29", 16 | "scale": "3x" 17 | }, 18 | { 19 | "idiom": "iphone", 20 | "size": "40x40", 21 | "scale": "2x" 22 | }, 23 | { 24 | "idiom": "iphone", 25 | "size": "40x40", 26 | "scale": "3x" 27 | }, 28 | { 29 | "idiom": "iphone", 30 | "size": "57x57", 31 | "scale": "1x" 32 | }, 33 | { 34 | "idiom": "iphone", 35 | "size": "57x57", 36 | "scale": "2x" 37 | }, 38 | { 39 | "idiom": "iphone", 40 | "size": "60x60", 41 | "scale": "2x" 42 | }, 43 | { 44 | "idiom": "iphone", 45 | "size": "60x60", 46 | "scale": "3x" 47 | }, 48 | { 49 | "idiom": "ipad", 50 | "size": "29x29", 51 | "scale": "1x" 52 | }, 53 | { 54 | "idiom": "ipad", 55 | "size": "29x29", 56 | "scale": "2x" 57 | }, 58 | { 59 | "idiom": "ipad", 60 | "size": "40x40", 61 | "scale": "1x" 62 | }, 63 | { 64 | "idiom": "ipad", 65 | "size": "40x40", 66 | "scale": "2x" 67 | }, 68 | { 69 | "idiom": "ipad", 70 | "size": "50x50", 71 | "scale": "1x" 72 | }, 73 | { 74 | "idiom": "ipad", 75 | "size": "50x50", 76 | "scale": "2x" 77 | }, 78 | { 79 | "idiom": "ipad", 80 | "size": "72x72", 81 | "scale": "1x" 82 | }, 83 | { 84 | "idiom": "ipad", 85 | "size": "72x72", 86 | "scale": "2x" 87 | }, 88 | { 89 | "idiom": "ipad", 90 | "size": "76x76", 91 | "scale": "1x" 92 | }, 93 | { 94 | "idiom": "ipad", 95 | "size": "76x76", 96 | "scale": "2x" 97 | }, 98 | { 99 | "size": "24x24", 100 | "idiom": "watch", 101 | "scale": "2x", 102 | "role": "notificationCenter", 103 | "subtype": "38mm" 104 | }, 105 | { 106 | "size": "27.5x27.5", 107 | "idiom": "watch", 108 | "scale": "2x", 109 | "role": "notificationCenter", 110 | "subtype": "42mm" 111 | }, 112 | { 113 | "size": "29x29", 114 | "idiom": "watch", 115 | "role": "companionSettings", 116 | "scale": "2x" 117 | }, 118 | { 119 | "size": "29x29", 120 | "idiom": "watch", 121 | "role": "companionSettings", 122 | "scale": "3x" 123 | }, 124 | { 125 | "size": "40x40", 126 | "idiom": "watch", 127 | "scale": "2x", 128 | "role": "appLauncher", 129 | "subtype": "38mm" 130 | }, 131 | { 132 | "size": "44x44", 133 | "idiom": "watch", 134 | "scale": "2x", 135 | "role": "longLook", 136 | "subtype": "42mm" 137 | }, 138 | { 139 | "size": "86x86", 140 | "idiom": "watch", 141 | "scale": "2x", 142 | "role": "quickLook", 143 | "subtype": "38mm" 144 | }, 145 | { 146 | "size": "98x98", 147 | "idiom": "watch", 148 | "scale": "2x", 149 | "role": "quickLook", 150 | "subtype": "42mm" 151 | } 152 | ], 153 | "info": { 154 | "version": 1, 155 | "author": "xcode" 156 | } 157 | } -------------------------------------------------------------------------------- /Samples/Droid/Sample.Droid.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {A8B989BF-FB0A-40E0-9540-0700C1585C64} 7 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 8 | Library 9 | Sample.Droid 10 | Sample.Droid 11 | v7.1 12 | True 13 | Resources\Resource.designer.cs 14 | Resource 15 | Properties\AndroidManifest.xml 16 | Resources 17 | Assets 18 | 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug 25 | DEBUG; 26 | prompt 27 | 4 28 | None 29 | arm64-v8a;armeabi;armeabi-v7a;x86 30 | 31 | 32 | true 33 | pdbonly 34 | true 35 | bin\Release 36 | prompt 37 | 4 38 | true 39 | false 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {4FE40048-B716-42ED-9E26-C114689BAAFD} 73 | SimpleDatabase 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /SimpleDatabase/Model/Group.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SQLite; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace SimpleDatabase 7 | { 8 | class SimpleDatabaseGroup : GroupInfo 9 | { 10 | [Indexed] 11 | public string ClassName {get;set;} 12 | [Indexed] 13 | public int RowCount {get;set;} 14 | public int Order {get;set;} 15 | [Ignore] 16 | public bool Loaded {get;set;} 17 | } 18 | public class GroupInfo 19 | { 20 | bool stringIsValid = false; 21 | public GroupInfo() 22 | { 23 | Filter = ""; 24 | From = ""; 25 | } 26 | 27 | string groupBy; 28 | [Indexed] 29 | public string GroupBy { 30 | get { return groupBy; } 31 | set 32 | { 33 | groupBy = value; 34 | stringIsValid = false; 35 | } 36 | } 37 | 38 | public Dictionary Params { get; set;}= new Dictionary(); 39 | public bool OrderByDesc { get; set;} 40 | public string GroupString {get;set;} 41 | public bool GroupOrderByDesc { get; set;} 42 | 43 | string orderBy; 44 | [Indexed] 45 | public string OrderBy { 46 | get { return orderBy;} 47 | set 48 | { 49 | orderBy = value; 50 | stringIsValid = false; 51 | } 52 | } 53 | 54 | string filter; 55 | [Indexed] 56 | public string Filter 57 | { 58 | get { return filter; } 59 | set 60 | { 61 | filter = value; 62 | stringIsValid = false; 63 | } 64 | } 65 | 66 | string from; 67 | public string From { 68 | get { return from;} 69 | set { 70 | from = value; 71 | stringIsValid = false; 72 | } 73 | } 74 | 75 | public int Limit {get;set;} 76 | 77 | public GroupInfo Clone () 78 | { 79 | return new GroupInfo 80 | { 81 | GroupBy = this.GroupBy, 82 | OrderByDesc = this.OrderByDesc, 83 | GroupString = this.GroupString, 84 | GroupOrderByDesc = this.GroupOrderByDesc, 85 | OrderBy = this.OrderBy, 86 | Filter = this.Filter, 87 | From = this.From, 88 | Limit = this.Limit, 89 | Params = Params?.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary(), 90 | }; 91 | } 92 | 93 | public string FromString(string table) 94 | { 95 | return $" {table} {From} "; 96 | } 97 | 98 | public string OrderByString(bool includeOrerBy = true) 99 | { 100 | 101 | if (string.IsNullOrEmpty (OrderBy)) 102 | return ""; 103 | string orderby = includeOrerBy ? " order by " : " , "; 104 | orderby += (string.IsNullOrEmpty(GroupBy) ? "" : GroupBy + (GroupOrderByDesc ? " desc " : "") + " , ") + OrderBy + ( OrderByDesc ? " desc " : ""); 105 | return orderby; 106 | } 107 | 108 | public string FilterString(bool includeWhere) 109 | { 110 | if (string.IsNullOrEmpty (Filter)) 111 | return ""; 112 | string filter = includeWhere ? " where " : " and "; 113 | filter += Filter; 114 | return filter; 115 | } 116 | 117 | public void AddFilter(string filter) 118 | { 119 | if (string.IsNullOrEmpty (Filter)) 120 | Filter = filter; 121 | else 122 | Filter += " and " + filter; 123 | } 124 | 125 | public string LimitString() 126 | { 127 | return (Limit > 0 ? " Limit " + Limit : " "); 128 | } 129 | 130 | public override bool Equals (object obj) 131 | { 132 | return obj.ToString() == this.ToString(); 133 | } 134 | 135 | public override int GetHashCode() 136 | { 137 | return this.ToString().GetHashCode(); 138 | } 139 | 140 | public static bool operator == (GroupInfo x, GroupInfo y) 141 | { 142 | if (object.ReferenceEquals (x, y)) { 143 | // handles if both are null as well as object identity 144 | return true; 145 | } 146 | 147 | if ((object)x == null || (object)y == null) { 148 | return false; 149 | } 150 | return x.Equals (y); 151 | } 152 | 153 | public static bool operator != (GroupInfo x, GroupInfo y) 154 | { 155 | if (object.ReferenceEquals (x, y)) { 156 | // handles if both are null as well as object identity 157 | return false; 158 | } 159 | 160 | if ((object)x == null || (object)y == null) { 161 | return true; 162 | } 163 | return !x.Equals (y); 164 | } 165 | 166 | string toStringValue; 167 | public override string ToString () 168 | { 169 | if (!stringIsValid || string.IsNullOrEmpty(toStringValue)) 170 | { 171 | toStringValue = $"[GroupInfo: GroupBy={GroupBy}, OrderBy={OrderBy}, Filter={Filter}, From={From} ,Params{string.Join(",", Params)}]"; 172 | stringIsValid = true; 173 | } 174 | return toStringValue; 175 | } 176 | 177 | public Tuple ConvertSqlFromNamed(string sql, Dictionary injectedParams = null) 178 | { 179 | return ConvertSqlFromNamed(sql,Params,injectedParams); 180 | } 181 | 182 | public static Tuple ConvertSqlFromNamed(string sql, Dictionary namedParameters, Dictionary injectedParams = null) 183 | { 184 | var foundParamters = sql.Split(' ','(',')').Where(x => x.StartsWith("@")).Select(x => x.Trim().TrimEnd(')')).ToList(); 185 | var hasQuestion = sql.Contains("?"); 186 | if (hasQuestion) 187 | { 188 | throw new Exception("Please covert to named parameters"); 189 | } 190 | 191 | string returnSql = sql; 192 | List parameterValues = new List(); 193 | foreach (var param in foundParamters) 194 | { 195 | object value; 196 | returnSql = returnSql.Replace(param, "?"); 197 | if (!namedParameters.TryGetValue(param, out value) && !(injectedParams?.TryGetValue(param, out value) ?? false)) 198 | throw new Exception($"\"{param}\" was not found in the Named Parameters"); 199 | parameterValues.Add(value); 200 | } 201 | return new Tuple(returnSql, parameterValues.ToArray()); 202 | } 203 | 204 | } 205 | } 206 | 207 | -------------------------------------------------------------------------------- /Samples/iOS/Sample.iOS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | iPhoneSimulator 6 | {C8231E82-96F8-4889-8BDD-FF9C437951B6} 7 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 8 | Exe 9 | Sample.iOS 10 | Sample.iOS 11 | Resources 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\iPhoneSimulator\Debug 18 | DEBUG;ENABLE_TEST_CLOUD; 19 | prompt 20 | 4 21 | iPhone Developer 22 | true 23 | true 24 | 63067 25 | None 26 | x86_64 27 | HttpClientHandler 28 | Default 29 | false 30 | 31 | 32 | pdbonly 33 | true 34 | bin\iPhone\Release 35 | 36 | prompt 37 | 4 38 | iPhone Developer 39 | true 40 | Entitlements.plist 41 | SdkOnly 42 | ARMv7, ARM64 43 | HttpClientHandler 44 | Default 45 | 46 | 47 | pdbonly 48 | true 49 | bin\iPhoneSimulator\Release 50 | 51 | prompt 52 | 4 53 | iPhone Developer 54 | None 55 | x86_64 56 | HttpClientHandler 57 | Default 58 | 59 | 60 | true 61 | full 62 | false 63 | bin\iPhone\Debug 64 | DEBUG;ENABLE_TEST_CLOUD; 65 | prompt 66 | 4 67 | iPhone Developer 68 | true 69 | true 70 | true 71 | true 72 | Entitlements.plist 73 | SdkOnly 74 | ARMv7, ARM64 75 | HttpClientHandler 76 | Default 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5} 109 | SimpleDatabase.iOS 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Samples/Sample/SimpleDatabaseSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.Specialized; 5 | using System.Diagnostics; 6 | using SimpleDatabase; 7 | 8 | namespace Sample 9 | { 10 | public class SimpleDatabaseSource : IList, INotifyCollectionChanged where T : new() 11 | { 12 | public bool IsGrouped { get; set; } = true; 13 | public SimpleDatabaseSource(SimpleDatabaseConnection connection) 14 | { 15 | Database = connection; 16 | } 17 | public object this[int index] 18 | { 19 | get 20 | { 21 | try 22 | { 23 | Debug.WriteLine($"Loading {index}"); 24 | if (IsGrouped) 25 | return new GroupedList(Database, GroupInfo, index) 26 | { 27 | Display = Database?.SectionHeader(GroupInfo, index) ?? "", 28 | }; 29 | return Database != null ? Database.ObjectForRow(GroupInfo,0,index) : new T(); 30 | } 31 | catch (Exception ex) 32 | { 33 | Debug.WriteLine(ex); 34 | return new T(); 35 | } 36 | } 37 | 38 | set 39 | { 40 | throw new NotImplementedException(); 41 | } 42 | } 43 | 44 | GroupInfo groupInfo; 45 | 46 | public event NotifyCollectionChangedEventHandler CollectionChanged; 47 | 48 | public void ResfreshData() 49 | { 50 | CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset)); 51 | } 52 | 53 | public GroupInfo GroupInfo 54 | { 55 | get { return groupInfo ?? (groupInfo =Database.GetGroupInfo()); } 56 | set { 57 | groupInfo = value; 58 | ResfreshData(); 59 | } 60 | } 61 | 62 | public int Count 63 | { 64 | get 65 | { 66 | var c = (IsGrouped ? Database?.NumberOfSections(GroupInfo) : Database?.RowsInSection(GroupInfo, 0)) ?? 0; 67 | if (c == 0) 68 | Console.WriteLine("what?"); 69 | return c; 70 | } 71 | } 72 | 73 | public SimpleDatabase.SimpleDatabaseConnection Database { get; set; } 74 | 75 | public bool IsFixedSize 76 | { 77 | get 78 | { 79 | return true; 80 | } 81 | } 82 | 83 | public bool IsReadOnly 84 | { 85 | get 86 | { 87 | return true; 88 | } 89 | } 90 | 91 | public bool IsSynchronized 92 | { 93 | get 94 | { 95 | throw new NotImplementedException(); 96 | } 97 | } 98 | 99 | public object SyncRoot 100 | { 101 | get 102 | { 103 | throw new NotImplementedException(); 104 | } 105 | } 106 | 107 | public int Add(object value) 108 | { 109 | throw new NotImplementedException(); 110 | } 111 | 112 | public void Clear() 113 | { 114 | throw new NotImplementedException(); 115 | } 116 | 117 | public bool Contains(object value) 118 | { 119 | throw new NotImplementedException(); 120 | } 121 | 122 | public void CopyTo(Array array, int index) 123 | { 124 | throw new NotImplementedException(); 125 | } 126 | 127 | public IEnumerator GetEnumerator() 128 | { 129 | throw new NotImplementedException(); 130 | } 131 | 132 | public int IndexOf(object value) 133 | { 134 | throw new NotImplementedException(); 135 | } 136 | 137 | public void Insert(int index, object value) 138 | { 139 | throw new NotImplementedException(); 140 | } 141 | 142 | public void Remove(object value) 143 | { 144 | throw new NotImplementedException(); 145 | } 146 | 147 | public void RemoveAt(int index) 148 | { 149 | throw new NotImplementedException(); 150 | } 151 | 152 | 153 | } 154 | public class GroupedList : IList where T : new() 155 | { 156 | public GroupedList(SimpleDatabase.SimpleDatabaseConnection database, GroupInfo groupInfo, int section) 157 | { 158 | GroupInfo = groupInfo; 159 | Database = database; 160 | Section = section; 161 | } 162 | public GroupInfo GroupInfo { get; set; } 163 | string display = ""; 164 | public string Display { 165 | get 166 | { 167 | return display; 168 | } 169 | set { display = value; } 170 | } 171 | 172 | public SimpleDatabase.SimpleDatabaseConnection Database { get; set; } 173 | public int Section { get; set; } 174 | 175 | public bool IsReadOnly 176 | { 177 | get 178 | { 179 | return true; 180 | } 181 | } 182 | 183 | public bool IsFixedSize 184 | { 185 | get 186 | { 187 | return true; 188 | } 189 | } 190 | 191 | public int Count 192 | { 193 | get 194 | { 195 | return Database?.RowsInSection(GroupInfo,Section) ?? 0; 196 | } 197 | } 198 | 199 | public object SyncRoot 200 | { 201 | get 202 | { 203 | throw new NotImplementedException(); 204 | } 205 | } 206 | 207 | public bool IsSynchronized 208 | { 209 | get 210 | { 211 | throw new NotImplementedException(); 212 | } 213 | } 214 | 215 | public object this[int index] 216 | { 217 | get 218 | { 219 | try 220 | { 221 | Debug.WriteLine($"Loading {Section}:{index}"); 222 | var item = Database.ObjectForRow(GroupInfo, Section, index); 223 | return item; 224 | } 225 | catch (Exception ex) 226 | { 227 | Debug.WriteLine(ex); 228 | return new T(); 229 | } 230 | } 231 | 232 | set 233 | { 234 | throw new NotImplementedException(); 235 | } 236 | } 237 | 238 | public int Add(object value) 239 | { 240 | throw new NotImplementedException(); 241 | } 242 | 243 | public bool Contains(object value) 244 | { 245 | throw new NotImplementedException(); 246 | } 247 | 248 | public void Clear() 249 | { 250 | throw new NotImplementedException(); 251 | } 252 | 253 | public int IndexOf(object value) 254 | { 255 | throw new NotImplementedException(); 256 | } 257 | 258 | public void Insert(int index, object value) 259 | { 260 | throw new NotImplementedException(); 261 | } 262 | 263 | public void Remove(object value) 264 | { 265 | throw new NotImplementedException(); 266 | } 267 | 268 | public void RemoveAt(int index) 269 | { 270 | throw new NotImplementedException(); 271 | } 272 | 273 | public void CopyTo(Array array, int index) 274 | { 275 | throw new NotImplementedException(); 276 | } 277 | 278 | public IEnumerator GetEnumerator() 279 | { 280 | throw new NotImplementedException(); 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /SimpleDatabase.Samples.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{FAB3BE12-DCAA-4C6A-BA16-5CA6F56C368B}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FB300C5C-A6EB-4BE4-A32D-7FE553C71A5A}" 9 | EndProject 10 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Sample", "Samples\Sample\Sample.shproj", "{F2472651-1440-4A8B-B034-19BE2A507AF4}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.iOS", "Samples\iOS\Sample.iOS.csproj", "{C8231E82-96F8-4889-8BDD-FF9C437951B6}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Droid", "Samples\Droid\Sample.Droid.csproj", "{A8B989BF-FB0A-40E0-9540-0700C1585C64}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleDatabase", "SimpleDatabase\SimpleDatabase.csproj", "{4FE40048-B716-42ED-9E26-C114689BAAFD}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleDatabase.iOS", "SimpleDatabase.iOS\SimpleDatabase.iOS.csproj", "{E8B052D6-9C21-4B3F-9520-29D7B38513C5}" 19 | EndProject 20 | Global 21 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 22 | SimpleDatabase\SimpleDatabase.Shared.projitems*{24b10281-9934-466b-bd72-7e439e72288d}*SharedItemsImports = 13 23 | SimpleDatabase\SimpleDatabase.Shared.projitems*{dab83efa-1d12-4f40-a6e9-1f1b5470faa1}*SharedItemsImports = 4 24 | SimpleDatabase\SimpleDatabase.Shared.projitems*{e8b052d6-9c21-4b3f-9520-29d7b38513c5}*SharedItemsImports = 4 25 | EndGlobalSection 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | Debug|iPhoneSimulator = Debug|iPhoneSimulator 30 | Release|iPhone = Release|iPhone 31 | Release|iPhoneSimulator = Release|iPhoneSimulator 32 | Debug|iPhone = Debug|iPhone 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator 36 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator 37 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Release|Any CPU.ActiveCfg = Release|iPhone 38 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Release|Any CPU.Build.0 = Release|iPhone 39 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator 40 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator 41 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Release|iPhone.ActiveCfg = Release|iPhone 42 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Release|iPhone.Build.0 = Release|iPhone 43 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator 44 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator 45 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Debug|iPhone.ActiveCfg = Debug|iPhone 46 | {C8231E82-96F8-4889-8BDD-FF9C437951B6}.Debug|iPhone.Build.0 = Debug|iPhone 47 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 52 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 53 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Release|iPhone.ActiveCfg = Release|Any CPU 54 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Release|iPhone.Build.0 = Release|Any CPU 55 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 56 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 57 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Debug|iPhone.ActiveCfg = Debug|Any CPU 58 | {A8B989BF-FB0A-40E0-9540-0700C1585C64}.Debug|iPhone.Build.0 = Debug|Any CPU 59 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 64 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 65 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Release|iPhone.ActiveCfg = Release|Any CPU 66 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Release|iPhone.Build.0 = Release|Any CPU 67 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 68 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 69 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Debug|iPhone.ActiveCfg = Debug|Any CPU 70 | {4FE40048-B716-42ED-9E26-C114689BAAFD}.Debug|iPhone.Build.0 = Debug|Any CPU 71 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 72 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Debug|Any CPU.Build.0 = Debug|Any CPU 73 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 76 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 77 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Release|iPhone.ActiveCfg = Release|Any CPU 78 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Release|iPhone.Build.0 = Release|Any CPU 79 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 80 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 81 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Debug|iPhone.ActiveCfg = Debug|Any CPU 82 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5}.Debug|iPhone.Build.0 = Debug|Any CPU 83 | EndGlobalSection 84 | GlobalSection(SolutionProperties) = preSolution 85 | HideSolutionNode = FALSE 86 | EndGlobalSection 87 | GlobalSection(MonoDevelopProperties) = preSolution 88 | StartupItem = InstantSample\InstantiOSSample.csproj 89 | EndGlobalSection 90 | GlobalSection(NestedProjects) = preSolution 91 | {F2472651-1440-4A8B-B034-19BE2A507AF4} = {FAB3BE12-DCAA-4C6A-BA16-5CA6F56C368B} 92 | {C8231E82-96F8-4889-8BDD-FF9C437951B6} = {FAB3BE12-DCAA-4C6A-BA16-5CA6F56C368B} 93 | {A8B989BF-FB0A-40E0-9540-0700C1585C64} = {FAB3BE12-DCAA-4C6A-BA16-5CA6F56C368B} 94 | {4FE40048-B716-42ED-9E26-C114689BAAFD} = {FB300C5C-A6EB-4BE4-A32D-7FE553C71A5A} 95 | {E8B052D6-9C21-4B3F-9520-29D7B38513C5} = {FB300C5C-A6EB-4BE4-A32D-7FE553C71A5A} 96 | EndGlobalSection 97 | EndGlobal 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /SimpleDatabase/SimpleDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using SQLite; 5 | using System.Reflection; 6 | using System.Collections; 7 | using System.Threading.Tasks; 8 | //using Java.Lang; 9 | using System.Threading; 10 | using System.Diagnostics; 11 | using System.Linq.Expressions; 12 | 13 | namespace SimpleDatabase 14 | { 15 | public class SimpleDatabaseConnection 16 | { 17 | Dictionary, Dictionary>> MemoryStore = new Dictionary, Dictionary>>(); 18 | Dictionary> ObjectsDict = new Dictionary>(); 19 | //Dictionary> Objects = new Dictionary> (); 20 | Dictionary, List> Groups = new Dictionary, List>(); 21 | Dictionary GroupInfoDict = new Dictionary(); 22 | object groupLocker = new object(); 23 | object memStoreLocker = new object(); 24 | object writeLocker = new object(); 25 | SQLiteConnection connection; 26 | public SimpleDatabaseConnection(SQLiteConnection sqliteConnection) 27 | { 28 | connection = sqliteConnection; 29 | init(); 30 | } 31 | public SimpleDatabaseConnection(string databasePath) 32 | { 33 | connection = new SQLiteConnection(databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.FullMutex | SQLiteOpenFlags.Create, true); 34 | connection.ExecuteScalar("PRAGMA journal_mode=WAL"); 35 | init(); 36 | } 37 | 38 | void init() 39 | { 40 | #if iOS 41 | Foundation.NSNotificationCenter.DefaultCenter.AddObserver((Foundation.NSString)"UIApplicationDidReceiveMemoryWarningNotification",delegate{ 42 | ClearMemory(); 43 | }); 44 | #endif 45 | } 46 | 47 | public void MakeClassInstant(GroupInfo info) 48 | { 49 | MakeClassInstant(typeof(T), info); 50 | } 51 | 52 | public void MakeClassInstant() 53 | { 54 | var t = typeof(T); 55 | MakeClassInstant(t); 56 | } 57 | 58 | public void MakeClassInstant(Type type) 59 | { 60 | MakeClassInstant(type, null); 61 | 62 | } 63 | 64 | public void MakeClassInstant(Type type, GroupInfo info) 65 | { 66 | if (info == null) 67 | info = GetGroupInfo(type); 68 | SetGroups(type, info); 69 | FillGroups(type, info); 70 | if (Groups[new Tuple(type, info.ToString())].Count() == 0) 71 | SetGroups(type, info); 72 | 73 | } 74 | 75 | public GroupInfo GetGroupInfo() 76 | { 77 | return GetGroupInfo(typeof(T)); 78 | } 79 | 80 | public GroupInfo GetGroupInfo(Type type) 81 | { 82 | if (GroupInfoDict.ContainsKey(type)) 83 | return GroupInfoDict[type]; 84 | bool groupDesc = false; 85 | var groupBy = GetGroupByProperty(type, out groupDesc); 86 | bool desc = false; 87 | var orderBy = GetOrderByProperty(type, out desc); 88 | var groupInfo = new GroupInfo(); 89 | if (groupBy != null) 90 | { 91 | groupInfo.GroupBy = groupBy.Name; 92 | groupInfo.GroupOrderByDesc = groupDesc; 93 | } 94 | if (orderBy != null) 95 | { 96 | groupInfo.OrderBy = orderBy.Name; 97 | groupInfo.OrderByDesc = desc; 98 | } 99 | GroupInfoDict.Add(type, groupInfo); 100 | return groupInfo; 101 | } 102 | 103 | private void SetGroups(Type type, GroupInfo groupInfo) 104 | { 105 | List groups = CreateGroupInfo(type, groupInfo); 106 | 107 | var tuple = new Tuple(type, groupInfo.ToString()); 108 | lock (groupLocker) 109 | { 110 | if (Groups.ContainsKey(tuple)) 111 | Groups[tuple] = groups; 112 | else 113 | Groups.Add(tuple, groups); 114 | } 115 | 116 | } 117 | 118 | private List CreateGroupInfo(Type type, GroupInfo groupInfo) 119 | { 120 | List groups; 121 | if (string.IsNullOrEmpty(groupInfo.GroupBy)) 122 | groups = new List() { new SimpleDatabaseGroup { GroupString = "" } }; 123 | else 124 | { 125 | var query = $"select distinct {groupInfo.GroupBy} as GroupString from {groupInfo.FromString(GetTableName(type))} {groupInfo.FilterString(true)} {groupInfo.OrderByString(true)} {groupInfo.LimitString()}"; 126 | var queryInfo = groupInfo.ConvertSqlFromNamed(query); 127 | groups = connection.Query(queryInfo.Item1, queryInfo.Item2.ToArray()).ToList(); 128 | } 129 | 130 | for (int i = 0; i < groups.Count(); i++) 131 | { 132 | var group = groups[i]; 133 | group.ClassName = type.Name; 134 | group.Filter = groupInfo.Filter ?? ""; 135 | group.GroupBy = groupInfo.GroupBy ?? ""; 136 | group.OrderBy = groupInfo.OrderBy ?? ""; 137 | group.Order = i; 138 | string rowQuery; 139 | if (string.IsNullOrEmpty(groupInfo.GroupBy)) 140 | rowQuery = $"select count(*) from {groupInfo.FromString(GetTableName(type))} {groupInfo.FilterString(true)}"; 141 | else 142 | rowQuery = $"select count(*) from {groupInfo.FromString(GetTableName(type))} where {groupInfo.GroupBy} = @GroupByParam {groupInfo.FilterString(false)}"; 143 | var queryInfo = groupInfo.ConvertSqlFromNamed(rowQuery, new Dictionary { { "@GroupByParam", group.GroupString } }); 144 | group.RowCount = connection.ExecuteScalar(queryInfo.Item1, queryInfo.Item2); 145 | //} 146 | if (groupInfo.Limit > 0) 147 | group.RowCount = Math.Min(group.RowCount, groupInfo.Limit); 148 | } 149 | return groups; 150 | } 151 | string GetTableName(Type type) 152 | { 153 | var tableInfo = GetTableMapping(connection, type); 154 | return tableInfo.TableName; 155 | } 156 | public void UpdateInstant(GroupInfo info) 157 | { 158 | UpdateInstant(typeof(T), info); 159 | } 160 | 161 | public void UpdateInstant() 162 | { 163 | UpdateInstant(typeof(T)); 164 | } 165 | 166 | public void UpdateInstant(Type type) 167 | { 168 | UpdateInstant(type, null); 169 | } 170 | 171 | public void UpdateInstant(Type type, GroupInfo info) 172 | { 173 | if (info == null) 174 | info = GetGroupInfo(type); 175 | var tuple = new Tuple(type, info.ToString()); 176 | lock (memStoreLocker) 177 | { 178 | if (MemoryStore.ContainsKey(tuple)) 179 | { 180 | MemoryStore[tuple] = new Dictionary>(); 181 | } 182 | } 183 | FillGroups(type, info); 184 | 185 | } 186 | 187 | public void ClearMemory() 188 | { 189 | lock (memStoreLocker) 190 | { 191 | 192 | ObjectsDict.Clear(); 193 | ClearMemoryStore(); 194 | cacheQueue.Clear(); 195 | } 196 | } 197 | public void ClearMemoryStore() 198 | { 199 | lock (memStoreLocker) 200 | { 201 | MemoryStore.Clear(); 202 | lock (groupLocker) 203 | { 204 | Groups.Clear(); 205 | GroupInfoDict.Clear(); 206 | } 207 | } 208 | } 209 | public void ClearMemory() 210 | { 211 | var t = typeof(T); 212 | ClearMemory(t); 213 | 214 | } 215 | 216 | public void ClearMemory(params Type[] types) 217 | { 218 | lock (memStoreLocker) 219 | { 220 | var toRemove = MemoryStore.Where(x => types.Contains(x.Key.Item1)).ToArray(); 221 | foreach (var item in toRemove) 222 | { 223 | MemoryStore.Remove(item.Key); 224 | } 225 | } 226 | lock (groupLocker) 227 | { 228 | Groups.Clear(); 229 | } 230 | } 231 | 232 | public void ClearMemory(GroupInfo groupInfo) 233 | { 234 | var t = typeof(T); 235 | ClearMemory(t, groupInfo); 236 | } 237 | public void ClearMemory(Type type, GroupInfo groupInfo) 238 | { 239 | var tuple = new Tuple(type, groupInfo.ToString()); 240 | lock (memStoreLocker) 241 | { 242 | MemoryStore.Remove(tuple); 243 | } 244 | lock (groupLocker) 245 | { 246 | Groups.Clear(); 247 | } 248 | } 249 | 250 | public string SectionHeader(int section) 251 | { 252 | return SectionHeader(GetGroupInfo(typeof(T)), section); 253 | } 254 | 255 | public string SectionHeader(GroupInfo info, int section) 256 | { 257 | if (info == null) 258 | info = GetGroupInfo(); 259 | 260 | lock (groupLocker) 261 | { 262 | var t = typeof(T); 263 | var tuple = new Tuple(t, info.ToString()); 264 | if (!Groups.ContainsKey(tuple) || Groups[tuple].Count <= section) 265 | FillGroups(t, info); 266 | try 267 | { 268 | return Groups[tuple][section].GroupString; 269 | } 270 | catch (Exception ex) 271 | { 272 | return ""; 273 | } 274 | } 275 | } 276 | 277 | public string[] QuickJump() 278 | { 279 | return QuickJump(GetGroupInfo()); 280 | } 281 | 282 | public string[] QuickJump(GroupInfo info) 283 | { 284 | if (info == null) 285 | info = GetGroupInfo(); 286 | lock (groupLocker) 287 | { 288 | var t = typeof(T); 289 | var tuple = new Tuple(t, info.ToString()); 290 | if (!Groups.ContainsKey(tuple)) 291 | FillGroups(t, info); 292 | var groups = Groups[tuple]; 293 | var strings = groups.Select(x => string.IsNullOrEmpty(x.GroupString) ? "" : x.GroupString[0].ToString()).ToArray(); 294 | return strings; 295 | } 296 | } 297 | 298 | public int NumberOfSections() 299 | { 300 | return NumberOfSections(GetGroupInfo()); 301 | } 302 | 303 | public int NumberOfSections(GroupInfo info) 304 | { 305 | if (info == null) 306 | info = GetGroupInfo(); 307 | lock (groupLocker) 308 | { 309 | var t = typeof(T); 310 | var tuple = new Tuple(t, info.ToString()); 311 | if (!Groups.ContainsKey(tuple)) 312 | FillGroups(t, info); 313 | return Groups[tuple].Count; 314 | } 315 | } 316 | 317 | public int RowsInSection(int section) 318 | { 319 | return RowsInSection(GetGroupInfo(), section); 320 | } 321 | 322 | public int RowsInSection(GroupInfo info, int section) 323 | { 324 | if (info == null) 325 | info = GetGroupInfo(); 326 | lock (groupLocker) 327 | { 328 | var group = GetGroup(info, section); 329 | return group.RowCount; 330 | } 331 | } 332 | 333 | private SimpleDatabaseGroup GetGroup(int section) 334 | { 335 | return GetGroup(GetGroupInfo(), section); 336 | } 337 | 338 | private SimpleDatabaseGroup GetGroup(GroupInfo info, int section) 339 | { 340 | return GetGroup(typeof(T), info, section); 341 | 342 | } 343 | 344 | private SimpleDatabaseGroup GetGroup(Type t, GroupInfo info, int section) 345 | { 346 | 347 | var tuple = new Tuple(t, info.ToString()); 348 | List group = null; 349 | int count = 0; 350 | while ((group == null || group.Count <= section) && count < 5) 351 | { 352 | if (count > 0) 353 | Debug.WriteLine("Trying to fill groups: {0}", count); 354 | lock (groupLocker) 355 | { 356 | Groups.TryGetValue(tuple, out group); 357 | } 358 | if (group == null) 359 | { 360 | FillGroups(t, info); 361 | } 362 | 363 | count++; 364 | } 365 | if (group == null || section >= group.Count) 366 | return new SimpleDatabaseGroup(); 367 | return group[section]; 368 | 369 | } 370 | 371 | private void FillGroups(Type t, GroupInfo info) 372 | { 373 | List groups; 374 | groups = CreateGroupInfo(t, info); 375 | lock (groupLocker) 376 | { 377 | var tuple = new Tuple(t, info.ToString()); 378 | Groups[tuple] = groups; 379 | } 380 | 381 | } 382 | 383 | public T ObjectForRow(int section, int row) where T : new() 384 | { 385 | return ObjectForRow(GetGroupInfo(typeof(T)), section, row); 386 | } 387 | 388 | public T ObjectForRow(GroupInfo info, int section, int row) where T : new() 389 | { 390 | if (info == null) 391 | info = GetGroupInfo(); 392 | lock (memStoreLocker) 393 | { 394 | var type = typeof(T); 395 | var tuple = new Tuple(type, info.ToString()); 396 | if (MemoryStore.ContainsKey(tuple)) 397 | { 398 | var groups = MemoryStore[tuple]; 399 | if (groups.ContainsKey(section)) 400 | { 401 | var g = groups[section]; 402 | if (g.ContainsKey(row)) 403 | return (T)groups[section][row]; 404 | } 405 | } 406 | 407 | Precache(info, section); 408 | return getObject(info, section, row); 409 | } 410 | } 411 | 412 | public T GetObject(object primaryKey) where T : new() 413 | { 414 | try 415 | { 416 | var type = typeof(T); 417 | if (!ObjectsDict.ContainsKey(type)) 418 | ObjectsDict[type] = new Dictionary(); 419 | if (ObjectsDict[type].ContainsKey(primaryKey)) 420 | return (T)ObjectsDict[type][primaryKey]; 421 | //Debug.WriteLine("object not in objectsdict"); 422 | var pk = GetTableMapping(connection, type); 423 | var query = $"select * from {pk.TableName} where {pk.PK.Name} = ? "; 424 | 425 | T item = connection.Query(query, primaryKey).FirstOrDefault(); 426 | 427 | return item != null ? GetIfCached(item) : item; 428 | } 429 | catch (Exception ex) 430 | { 431 | Debug.WriteLine(ex); 432 | return default(T); 433 | } 434 | 435 | } 436 | 437 | private T getObject(GroupInfo info, int section, int row) where T : new() 438 | { 439 | try 440 | { 441 | T item; 442 | var t = typeof(T); 443 | var group = GetGroup(info, section); 444 | 445 | string query; 446 | if (string.IsNullOrEmpty(info.GroupBy)) 447 | query = $"select * from {info.FromString(GetTableName(t))} {info.FilterString(true)} {info.OrderByString(true)} LIMIT {row}, 1"; 448 | else 449 | query = $"select * from {info.FromString(GetTableName(t))} where {info.GroupBy} = @GroupByParam {info.FilterString(false)} {info.OrderByString(true)} LIMIT @LimitParam , 1"; 450 | var queryInfo = info.ConvertSqlFromNamed(query, new Dictionary { 451 | {"@GroupByParam",group.GroupString}, 452 | {"@LimitParam", row } 453 | }); 454 | item = connection.Query(queryInfo.Item1, queryInfo.Item2).FirstOrDefault(); 455 | 456 | if (item == null) 457 | return new T(); 458 | 459 | var tuple = new Tuple(t, info.ToString()); 460 | lock (memStoreLocker) 461 | { 462 | if (!MemoryStore.ContainsKey(tuple)) 463 | MemoryStore.Add(tuple, new Dictionary>()); 464 | var groups = MemoryStore[tuple]; 465 | if (!groups.ContainsKey(section)) 466 | groups.Add(section, new Dictionary()); 467 | if (!groups[section].ContainsKey(row)) 468 | groups[section].Add(row, item); 469 | else 470 | groups[section][row] = item; 471 | return GetIfCached(item); 472 | } 473 | } 474 | catch (Exception ex) 475 | { 476 | Debug.WriteLine(ex); 477 | return default(T); 478 | } 479 | 480 | } 481 | public void AddObjectToDict(object item, Type t) 482 | { 483 | lock (groupLocker) 484 | { 485 | var primaryKey = GetTableMapping(connection, t); 486 | if (primaryKey == null) 487 | return; 488 | object pk = primaryKey.PK.GetValue(item); 489 | if (!ObjectsDict.ContainsKey(t)) 490 | ObjectsDict.Add(t, new Dictionary()); 491 | ObjectsDict[t][pk] = item; 492 | // if (!Objects.ContainsKey (t)) 493 | // Objects.Add (t, new List ()); 494 | // if (!Objects [t].Contains (item)) 495 | // Objects [t].Add (item); 496 | } 497 | } 498 | 499 | public void AddObjectToDict(object item) 500 | { 501 | AddObjectToDict(item, item.GetType()); 502 | } 503 | public void RemoveObjectFromDict(object item) 504 | { 505 | RemoveObjectFromDict(item, item.GetType()); 506 | } 507 | 508 | public void RemoveObjectFromDict(object item, Type t) 509 | { 510 | lock (groupLocker) 511 | { 512 | var primaryKey = GetTableMapping(connection, t); 513 | if (primaryKey == null) 514 | return; 515 | object pk = primaryKey.PK.GetValue(item); 516 | if (ObjectsDict.ContainsKey(t)) 517 | ObjectsDict[t].Remove(pk); 518 | } 519 | } 520 | 521 | T GetIfCached(T item) 522 | { 523 | lock (groupLocker) 524 | { 525 | var t = typeof(T); 526 | var primaryKey = GetTableMapping(connection, t); 527 | if (primaryKey == null) 528 | return item; 529 | var pk = primaryKey.PK.GetValue(item); 530 | 531 | if (!ObjectsDict.ContainsKey(t)) 532 | ObjectsDict.Add(t, new Dictionary()); 533 | object oldItem; 534 | if (ObjectsDict[t].TryGetValue(pk, out oldItem)) 535 | { 536 | return (T)oldItem; 537 | } 538 | ObjectsDict[t][pk] = item; 539 | return item; 540 | } 541 | } 542 | 543 | public int GetObjectCount() 544 | { 545 | return GetObjectCount(null); 546 | } 547 | 548 | public int GetObjectCount(GroupInfo info) 549 | { 550 | if (info == null) 551 | info = GetGroupInfo(); 552 | var filterString = info.FilterString(true); 553 | var t = typeof(T); 554 | string query = $"Select count(*) from {info.FromString(GetTableName(t))} {filterString}"; 555 | var queryInfo = info.ConvertSqlFromNamed(query); 556 | int count = connection.ExecuteScalar(queryInfo.Item1, queryInfo.Item2); 557 | 558 | if (info.Limit > 0) 559 | return Math.Min(info.Limit, count); 560 | return count; 561 | } 562 | public int GetDistinctObjectCount(string column) 563 | { 564 | return GetDistinctObjectCount(null, column); 565 | } 566 | 567 | public int GetDistinctObjectCount(GroupInfo info, string column) 568 | { 569 | if (info == null) 570 | info = GetGroupInfo(); 571 | var filterString = info.FilterString(true); 572 | var t = typeof(T); 573 | string query = $"Select distinct count({column}) from {info.FromString(GetTableName(t))} {filterString} {info.LimitString()}"; 574 | var queryInfo = info.ConvertSqlFromNamed(query); 575 | int count = connection.ExecuteScalar(queryInfo.Item1, queryInfo.Item2); 576 | 577 | if (info.Limit > 0) 578 | return Math.Min(info.Limit, count); 579 | return count; 580 | } 581 | 582 | 583 | public T GetObjectByIndex(int index, GroupInfo info = null) where T : new() 584 | { 585 | T item; 586 | var t = typeof(T); 587 | if (info == null) 588 | info = GetGroupInfo(); 589 | var filterString = info.FilterString(true); 590 | var query = $"select * from {info.FromString(GetTableName(t))} {filterString} {info.OrderByString(true)} LIMIT {index}, 1"; 591 | var queryInfo = info.ConvertSqlFromNamed(query); 592 | item = connection.Query(queryInfo.Item1, queryInfo.Item2).FirstOrDefault(); 593 | 594 | if (item == null) 595 | return default(T); 596 | return GetIfCached(item); 597 | } 598 | public List GetObjects(GroupInfo info) where T : new() 599 | { 600 | if (info == null) 601 | info = GetGroupInfo(); 602 | var filterString = info.FilterString(true); 603 | var t = typeof(T); 604 | string query = $"Select * from {info.FromString(GetTableName(t))} {filterString} {info.LimitString()}"; 605 | var queryInfo = info.ConvertSqlFromNamed(query); 606 | return connection.Query(queryInfo.Item1, queryInfo.Item2).ToList(); 607 | 608 | } 609 | 610 | public void Precache() where T : new() 611 | { 612 | Precache(GetGroupInfo(typeof(T))); 613 | } 614 | 615 | public void Precache(GroupInfo info) where T : new() 616 | { 617 | return; 618 | if (info == null) 619 | info = GetGroupInfo(); 620 | var type = typeof(T); 621 | var tuple = new Tuple(type, info.ToString()); 622 | FillGroups(type, info); 623 | lock (groupLocker) 624 | { 625 | if (Groups[tuple].Count() == 0) 626 | SetGroups(type, info); 627 | 628 | foreach (var group in Groups[tuple]) 629 | { 630 | if (group.Loaded) 631 | continue; 632 | cacheQueue.AddLast(delegate 633 | { 634 | LoadItemsForGroup(group); 635 | }); 636 | } 637 | } 638 | StartQueue(); 639 | 640 | } 641 | 642 | public void Precache(int section) where T : new() 643 | { 644 | Precache(GetGroupInfo(typeof(T)), section); 645 | } 646 | 647 | public void Precache(GroupInfo info, int section) where T : new() 648 | { 649 | try 650 | { 651 | if (info == null) 652 | info = GetGroupInfo(); 653 | var type = typeof(T); 654 | var group = GetGroup(type, info, section); 655 | cacheQueue.AddFirst(delegate 656 | { 657 | LoadItemsForGroup(group); 658 | }); 659 | StartQueue(); 660 | } 661 | catch (Exception ex) 662 | { 663 | Debug.WriteLine(ex); 664 | } 665 | } 666 | 667 | private void LoadItemsForGroup(SimpleDatabaseGroup group) where T : new() 668 | { 669 | try 670 | { 671 | if (group.Loaded) 672 | return; 673 | Debug.WriteLine("Loading items for group"); 674 | var type = typeof(T); 675 | string query = $"select * from {group.FromString(type.Name)} where {group.GroupBy} = @GroupByParam {group.FilterString(false)} {group.OrderByString(true)} LIMIT @LimitParam , 50"; 676 | List items; 677 | int current = 0; 678 | bool hasMore = true; 679 | while (hasMore) 680 | { 681 | 682 | if (string.IsNullOrEmpty(group.GroupBy)) 683 | query = $"select * from {group.FromString(type.Name)} {group.FilterString(true)} {group.OrderByString(true)} LIMIT {current}, 50"; 684 | var queryInfo = group.ConvertSqlFromNamed(query, new Dictionary { 685 | {"@GroupByParam",group.GroupString}, 686 | {"@LimitParam", current } 687 | }); 688 | items = connection.Query(queryInfo.Item1, queryInfo.Item2).ToList(); 689 | 690 | { 691 | Dictionary memoryGroup; 692 | lock (memStoreLocker) 693 | { 694 | var tuple = new Tuple(type, group.ToString()); 695 | if (!MemoryStore.ContainsKey(tuple)) 696 | { 697 | MemoryStore.Add(tuple, new Dictionary>()); 698 | } 699 | 700 | if (!MemoryStore[tuple].ContainsKey(group.Order)) 701 | try 702 | { 703 | MemoryStore[tuple].Add(group.Order, new Dictionary()); 704 | } 705 | catch (Exception ex) 706 | { 707 | Debug.WriteLine(ex); 708 | } 709 | memoryGroup = MemoryStore[tuple][group.Order]; 710 | } 711 | for (int i = 0; i < items.Count; i++) 712 | { 713 | lock (groupLocker) 714 | { 715 | if (memoryGroup.ContainsKey(i + current)) 716 | memoryGroup[i + current] = items[i]; 717 | else 718 | memoryGroup.Add(i + current, items[i]); 719 | } 720 | GetIfCached(items[i]); 721 | 722 | } 723 | 724 | } 725 | current += items.Count; 726 | if (current == group.RowCount) 727 | hasMore = false; 728 | } 729 | Debug.WriteLine("group loaded"); 730 | group.Loaded = true; 731 | } 732 | catch (Exception ex) 733 | { 734 | Debug.WriteLine(ex); 735 | } 736 | } 737 | 738 | LinkedList cacheQueue = new LinkedList(); 739 | object locker = new object(); 740 | bool queueIsRunning = false; 741 | 742 | private void StartQueue() 743 | { 744 | return; 745 | lock (locker) 746 | { 747 | if (queueIsRunning) 748 | return; 749 | if (cacheQueue.Count == 0) 750 | return; 751 | queueIsRunning = true; 752 | } 753 | Task.Run(() => runQueue()); 754 | } 755 | 756 | void runQueue() 757 | { 758 | Action action; 759 | lock (locker) 760 | { 761 | if (cacheQueue.Count == 0) 762 | { 763 | queueIsRunning = false; 764 | return; 765 | } 766 | 767 | 768 | try 769 | { 770 | //Task.Factory.StartNew (delegate { 771 | action = cacheQueue.First(); 772 | cacheQueue.Remove(action); 773 | } 774 | catch (Exception ex) 775 | { 776 | Debug.WriteLine(ex); 777 | runQueue(); 778 | return; 779 | } 780 | } 781 | if (action != null) 782 | action(); 783 | //}).ContinueWith (delegate { 784 | runQueue(); 785 | } 786 | 787 | 788 | static Dictionary> groupByProperties = new Dictionary>(); 789 | static internal PropertyInfo GetGroupByProperty(Type type, out bool desc) 790 | { 791 | Tuple property; 792 | if (groupByProperties.TryGetValue(type, out property)) 793 | { 794 | desc = property.Item2; 795 | return property.Item1; 796 | } 797 | foreach (var prop in type.GetProperties()) 798 | { 799 | var attribtues = prop.GetCustomAttributes(false); 800 | var visibleAtt = attribtues.Where(x => x is GroupByAttribute).FirstOrDefault() as GroupByAttribute; 801 | if (visibleAtt != null) 802 | { 803 | desc = visibleAtt.Descending; 804 | groupByProperties[type] = new Tuple(prop, desc); 805 | return prop; 806 | } 807 | } 808 | desc = false; 809 | return null; 810 | } 811 | 812 | 813 | static Dictionary> orderByProperties = new Dictionary>(); 814 | internal static PropertyInfo GetOrderByProperty(Type type, out bool desc) 815 | { 816 | Tuple property; 817 | if (orderByProperties.TryGetValue(type, out property)) 818 | { 819 | desc = property.Item2; 820 | return property.Item1; 821 | } 822 | foreach (var prop in type.GetProperties()) 823 | { 824 | var attribtues = prop.GetCustomAttributes(false); 825 | var visibleAtt = attribtues.Where(x => x is OrderByAttribute).FirstOrDefault() as OrderByAttribute; 826 | if (visibleAtt != null) 827 | { 828 | desc = visibleAtt.Descending; 829 | orderByProperties[type] = new Tuple(prop, desc); 830 | return prop; 831 | } 832 | } 833 | desc = false; 834 | return null; 835 | } 836 | 837 | static Dictionary cachedTableMappings = new Dictionary(); 838 | static TableMapping GetTableMapping(SQLiteConnection connection, Type type) 839 | { 840 | TableMapping property; 841 | if (!cachedTableMappings.TryGetValue(type, out property)) 842 | cachedTableMappings[type] = property = connection.GetMapping(type); 843 | return property; 844 | 845 | } 846 | 847 | 848 | #region sqlite 849 | 850 | public int InsertAll(System.Collections.IEnumerable objects) 851 | { 852 | var c = 0; 853 | var types = new HashSet(); 854 | lock (writeLocker) 855 | { 856 | connection.RunInTransaction(() => 857 | { 858 | foreach (var item in objects) 859 | { 860 | var i = connection.Insert(item); 861 | if (i > 0) 862 | { 863 | AddObjectToDict(item); 864 | types.Add(item.GetType()); 865 | c += i; 866 | } 867 | 868 | } 869 | }); 870 | } 871 | if (c > 0) 872 | ClearMemory(types.ToArray()); 873 | return c; 874 | } 875 | 876 | /// 877 | /// Inserts all specified objects. 878 | /// 879 | /// 880 | /// An of the objects to insert. 881 | /// 882 | /// 883 | /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ... 884 | /// 885 | /// 886 | /// The number of rows added to the table. 887 | /// 888 | public int InsertAll(System.Collections.IEnumerable objects, string extra) 889 | { 890 | var c = 0; 891 | var types = new HashSet(); 892 | lock (writeLocker) 893 | { 894 | connection.RunInTransaction(() => 895 | { 896 | foreach (var item in objects) 897 | { 898 | var i = connection.Insert(item, extra); 899 | if (i > 0) 900 | { 901 | AddObjectToDict(item); 902 | types.Add(item.GetType()); 903 | c += i; 904 | } 905 | } 906 | }); 907 | } 908 | if (c > 0) 909 | ClearMemory(types.ToArray()); 910 | return c; 911 | } 912 | 913 | public Task InsertAllAsync(System.Collections.IEnumerable objects) 914 | { 915 | return Task.Run(() => connection.InsertAll(objects)); 916 | } 917 | 918 | public Task InsertAllAsync(System.Collections.IEnumerable objects, string extra) 919 | { 920 | return Task.Run(() => connection.InsertAll(objects, extra)); 921 | } 922 | 923 | public AsyncTableQuery TablesAsync() 924 | where T : new() 925 | { 926 | return new AsyncTableQuery(connection.Table()); 927 | } 928 | 929 | public Task InsertAsync(object item) 930 | { 931 | return Task.Run(() => Insert(item)); 932 | } 933 | public Task InsertAsync(object item, string extra) 934 | { 935 | return Task.Run(() => Insert(item, extra)); 936 | } 937 | 938 | 939 | /// 940 | /// Inserts all specified objects. 941 | /// 942 | /// 943 | /// An of the objects to insert. 944 | /// 945 | /// 946 | /// The type of object to insert. 947 | /// 948 | /// 949 | /// The number of rows added to the table. 950 | /// 951 | public int InsertAll(System.Collections.IEnumerable objects, Type objType) 952 | { 953 | var c = 0; 954 | lock (writeLocker) 955 | { 956 | connection.RunInTransaction(() => 957 | { 958 | foreach (var item in objects) 959 | { 960 | var i = connection.Insert(item, objType); 961 | if (i > 0) 962 | { 963 | AddObjectToDict(item); 964 | c += i; 965 | } 966 | } 967 | }); 968 | } 969 | if (c > 0) 970 | ClearMemory(objType); 971 | return c; 972 | } 973 | 974 | 975 | /// 976 | /// Inserts the given object and retrieves its 977 | /// auto incremented primary key if it has one. 978 | /// 979 | /// 980 | /// The object to insert. 981 | /// 982 | /// 983 | /// The number of rows added to the table. 984 | /// 985 | public int Insert(object obj) 986 | { 987 | var c = connection.Insert(obj); 988 | if (c > 0) 989 | { 990 | ClearMemory(obj.GetType()); 991 | AddObjectToDict(obj); 992 | } 993 | return c; 994 | } 995 | 996 | /// 997 | /// Inserts the given object and retrieves its 998 | /// auto incremented primary key if it has one. 999 | /// If a UNIQUE constraint violation occurs with 1000 | /// some pre-existing object, this function deletes 1001 | /// the old object. 1002 | /// 1003 | /// 1004 | /// The object to insert. 1005 | /// 1006 | /// 1007 | /// The number of rows modified. 1008 | /// 1009 | public int InsertOrReplace(object obj) 1010 | { 1011 | var c = connection.InsertOrReplace(obj); 1012 | if (c > 0) 1013 | { 1014 | ClearMemory(obj.GetType()); 1015 | AddObjectToDict(obj); 1016 | } 1017 | return c; 1018 | } 1019 | 1020 | /// 1021 | /// Inserts the given object and retrieves its 1022 | /// auto incremented primary key if it has one. 1023 | /// 1024 | /// 1025 | /// The object to insert. 1026 | /// 1027 | /// 1028 | /// The type of object to insert. 1029 | /// 1030 | /// 1031 | /// The number of rows added to the table. 1032 | /// 1033 | public int Insert(object obj, Type objType) 1034 | { 1035 | var c = connection.Insert(obj, objType); 1036 | if (c > 0) 1037 | { 1038 | ClearMemory(objType); 1039 | AddObjectToDict(obj); 1040 | } 1041 | return c; 1042 | } 1043 | 1044 | /// 1045 | /// Inserts the given object and retrieves its 1046 | /// auto incremented primary key if it has one. 1047 | /// If a UNIQUE constraint violation occurs with 1048 | /// some pre-existing object, this function deletes 1049 | /// the old object. 1050 | /// 1051 | /// 1052 | /// The object to insert. 1053 | /// 1054 | /// 1055 | /// The type of object to insert. 1056 | /// 1057 | /// 1058 | /// The number of rows modified. 1059 | /// 1060 | public int InsertOrReplace(object obj, Type objType) 1061 | { 1062 | var c = connection.InsertOrReplace(obj, objType); 1063 | if (c > 0) 1064 | { 1065 | ClearMemory(objType); 1066 | AddObjectToDict(obj); 1067 | } 1068 | return c; 1069 | } 1070 | 1071 | public int InsertOrReplaceAll(System.Collections.IEnumerable objects) 1072 | { 1073 | var c = 0; 1074 | var types = new HashSet(); 1075 | lock (writeLocker) 1076 | { 1077 | connection.RunInTransaction(() => 1078 | { 1079 | foreach (var item in objects) 1080 | { 1081 | var i = connection.InsertOrReplace(item); 1082 | if (i > 0) 1083 | { 1084 | AddObjectToDict(item); 1085 | types.Add(item.GetType()); 1086 | c += i; 1087 | } 1088 | } 1089 | }); 1090 | } 1091 | if (c > 0) 1092 | ClearMemory(types.ToArray()); 1093 | return c; 1094 | 1095 | } 1096 | 1097 | /// 1098 | /// Inserts all specified objects. 1099 | /// 1100 | /// 1101 | /// An of the objects to insert. 1102 | /// 1103 | /// 1104 | /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ... 1105 | /// 1106 | /// 1107 | /// The number of rows added to the table. 1108 | /// 1109 | public int InsertOrReplaceAll(System.Collections.IEnumerable objects, Type objType) 1110 | { 1111 | var c = 0; 1112 | lock (writeLocker) 1113 | { 1114 | connection.RunInTransaction(() => 1115 | { 1116 | foreach (var item in objects) 1117 | { 1118 | var i = connection.InsertOrReplace(item, objType); 1119 | if (i > 0) 1120 | { 1121 | AddObjectToDict(item); 1122 | c += i; 1123 | } 1124 | } 1125 | }); 1126 | } 1127 | if (c > 0) 1128 | ClearMemory(objType); 1129 | return c; 1130 | } 1131 | 1132 | public void RunInTransaction(Action action) 1133 | { 1134 | lock (writeLocker) 1135 | { 1136 | connection.RunInTransaction(() => action(connection)); 1137 | } 1138 | } 1139 | 1140 | /// 1141 | /// Inserts the given object and retrieves its 1142 | /// auto incremented primary key if it has one. 1143 | /// 1144 | /// 1145 | /// The object to insert. 1146 | /// 1147 | /// 1148 | /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ... 1149 | /// 1150 | /// 1151 | /// The number of rows added to the table. 1152 | /// 1153 | public int Insert(object obj, string extra) 1154 | { 1155 | var c = connection.Insert(obj, extra); 1156 | if (c > 0) 1157 | { 1158 | ClearMemory(obj.GetType()); 1159 | AddObjectToDict(obj); 1160 | } 1161 | return c; 1162 | } 1163 | 1164 | /// 1165 | /// Inserts the given object and retrieves its 1166 | /// auto incremented primary key if it has one. 1167 | /// 1168 | /// 1169 | /// The object to insert. 1170 | /// 1171 | /// 1172 | /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ... 1173 | /// 1174 | /// 1175 | /// The type of object to insert. 1176 | /// 1177 | /// 1178 | /// The number of rows added to the table. 1179 | /// 1180 | //public int Insert (object obj, string extra, Type objType) 1181 | //{ 1182 | // return connection.Insert (obj,extra,objType); 1183 | //} 1184 | 1185 | public int Execute(string query, params object[] args) 1186 | { 1187 | return connection.Execute(query, args); 1188 | } 1189 | 1190 | public Task ExecuteAsync(string query, params object[] args) 1191 | { 1192 | return Task.Run(() => connection.Execute(query, args)); 1193 | } 1194 | 1195 | public List Query(string query, params object[] args) where T : new() 1196 | { 1197 | return connection.Query(query, args); 1198 | 1199 | } 1200 | 1201 | public Task> QueryAsync(string query, params object[] args) where T : new() 1202 | { 1203 | return Task.Run(() => connection.Query(query, args)); 1204 | 1205 | } 1206 | 1207 | public int Delete(object objectToDelete) 1208 | { 1209 | var c = connection.Delete(objectToDelete); 1210 | if (c > 0) 1211 | { 1212 | ClearMemory(objectToDelete.GetType()); 1213 | RemoveObjectFromDict(objectToDelete); 1214 | } 1215 | return c; 1216 | } 1217 | 1218 | public int DeleteAll(System.Collections.IEnumerable objects) 1219 | { 1220 | var c = 0; 1221 | var types = new HashSet(); 1222 | lock (writeLocker) 1223 | { 1224 | connection.RunInTransaction(() => 1225 | { 1226 | foreach (var item in objects) 1227 | { 1228 | var i = connection.Delete(item); 1229 | if (i > 0) 1230 | { 1231 | RemoveObjectFromDict(item); 1232 | types.Add(item.GetType()); 1233 | c += i; 1234 | } 1235 | } 1236 | }); 1237 | } 1238 | if (c > 0) 1239 | ClearMemory(types.ToArray()); 1240 | return c; 1241 | } 1242 | 1243 | 1244 | public int DeleteAll(System.Collections.IEnumerable objects, Type type) 1245 | { 1246 | var c = 0; 1247 | lock (writeLocker) 1248 | { 1249 | connection.RunInTransaction(() => 1250 | { 1251 | foreach (var item in objects) 1252 | { 1253 | var i = connection.Delete(item, type); 1254 | if (i > 0) 1255 | { 1256 | RemoveObjectFromDict(item); 1257 | c += i; 1258 | } 1259 | } 1260 | }); 1261 | } 1262 | if (c > 0) 1263 | ClearMemory(type); 1264 | return c; 1265 | } 1266 | 1267 | public int Update(object obj) 1268 | { 1269 | var c = connection.Update(obj); 1270 | if (c > 0) 1271 | { 1272 | ClearMemory(obj.GetType()); 1273 | AddObjectToDict(obj); 1274 | } 1275 | return c; 1276 | } 1277 | 1278 | 1279 | public Task UpdateAsync(object obj) 1280 | { 1281 | return Task.Run(() => 1282 | { 1283 | AddObjectToDict(obj); 1284 | return connection.Update(obj); 1285 | }); 1286 | } 1287 | 1288 | public int UpdateAll(System.Collections.IEnumerable objects) 1289 | { 1290 | var c = 0; 1291 | var types = new HashSet(); 1292 | lock (writeLocker) 1293 | { 1294 | connection.RunInTransaction(() => 1295 | { 1296 | foreach (var item in objects) 1297 | { 1298 | var i = connection.Update(item); 1299 | if (i > 0) 1300 | { 1301 | AddObjectToDict(item); 1302 | types.Add(item.GetType()); 1303 | c += i; 1304 | } 1305 | } 1306 | }); 1307 | } 1308 | if (c > 0) 1309 | ClearMemory(types.ToArray()); 1310 | return c; 1311 | } 1312 | 1313 | public Dictionary CreateTables(params Type[] types) 1314 | { 1315 | return connection.CreateTables(types); 1316 | } 1317 | public CreateTableResult CreateTable() where T : new() 1318 | { 1319 | return connection.CreateTable(); 1320 | } 1321 | public T ExecuteScalar(string query, params object[] args) where T : new() 1322 | { 1323 | return connection.ExecuteScalar(query, args); 1324 | } 1325 | 1326 | #endregion 1327 | public class AsyncTableQuery 1328 | where T : new() 1329 | { 1330 | TableQuery _innerQuery; 1331 | 1332 | public AsyncTableQuery(TableQuery innerQuery) 1333 | { 1334 | _innerQuery = innerQuery; 1335 | } 1336 | 1337 | public AsyncTableQuery Where(Expression> predExpr) 1338 | { 1339 | return new AsyncTableQuery(_innerQuery.Where(predExpr)); 1340 | } 1341 | 1342 | public AsyncTableQuery Skip(int n) 1343 | { 1344 | return new AsyncTableQuery(_innerQuery.Skip(n)); 1345 | } 1346 | 1347 | public AsyncTableQuery Take(int n) 1348 | { 1349 | return new AsyncTableQuery(_innerQuery.Take(n)); 1350 | } 1351 | 1352 | public AsyncTableQuery OrderBy(Expression> orderExpr) 1353 | { 1354 | return new AsyncTableQuery(_innerQuery.OrderBy(orderExpr)); 1355 | } 1356 | 1357 | public AsyncTableQuery OrderByDescending(Expression> orderExpr) 1358 | { 1359 | return new AsyncTableQuery(_innerQuery.OrderByDescending(orderExpr)); 1360 | } 1361 | 1362 | public Task> ToListAsync() 1363 | { 1364 | return Task.Factory.StartNew(() => 1365 | { 1366 | return _innerQuery.ToList(); 1367 | }); 1368 | } 1369 | 1370 | public Task CountAsync() 1371 | { 1372 | return Task.Factory.StartNew(() => 1373 | { 1374 | return _innerQuery.Count(); 1375 | }); 1376 | } 1377 | 1378 | public Task ElementAtAsync(int index) 1379 | { 1380 | return Task.Factory.StartNew(() => 1381 | { 1382 | return _innerQuery.ElementAt(index); 1383 | }); 1384 | } 1385 | 1386 | public Task FirstAsync() 1387 | { 1388 | return Task.Factory.StartNew(() => 1389 | { 1390 | return _innerQuery.First(); 1391 | }); 1392 | } 1393 | 1394 | public Task FirstOrDefaultAsync() 1395 | { 1396 | return Task.Factory.StartNew(() => 1397 | { 1398 | return _innerQuery.FirstOrDefault(); 1399 | }); 1400 | } 1401 | } 1402 | } 1403 | 1404 | [AttributeUsage(AttributeTargets.Property)] 1405 | public class GroupByAttribute : IndexedAttribute 1406 | { 1407 | 1408 | public bool Descending { get; set; } 1409 | public GroupByAttribute(bool descending = false) 1410 | { 1411 | Descending = descending; 1412 | } 1413 | } 1414 | 1415 | [AttributeUsage(AttributeTargets.Property)] 1416 | public class OrderByAttribute : IndexedAttribute 1417 | { 1418 | public bool Descending { get; set; } 1419 | public OrderByAttribute(bool descending = false) 1420 | { 1421 | Descending = descending; 1422 | } 1423 | } 1424 | } 1425 | 1426 | --------------------------------------------------------------------------------