mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-04-05 14:37:01 +00:00
Version 4.3.0. Changed FileDbCache folder structure. New UWP project files.
This commit is contained in:
parent
a7bb9e1ef4
commit
2f123886ff
31 changed files with 184 additions and 256 deletions
BIN
FileDbCache/FileDb/FileDb.dll
Normal file
BIN
FileDbCache/FileDb/FileDb.dll
Normal file
Binary file not shown.
1910
FileDbCache/FileDb/FileDb.xml
Normal file
1910
FileDbCache/FileDb/FileDb.xml
Normal file
File diff suppressed because it is too large
Load diff
BIN
FileDbCache/FileDb/FileDbPcl.dll
Normal file
BIN
FileDbCache/FileDb/FileDbPcl.dll
Normal file
Binary file not shown.
1854
FileDbCache/FileDb/FileDbPcl.xml
Normal file
1854
FileDbCache/FileDb/FileDbPcl.xml
Normal file
File diff suppressed because it is too large
Load diff
983
FileDbCache/FileDb/Help.html
Normal file
983
FileDbCache/FileDb/Help.html
Normal file
|
|
@ -0,0 +1,983 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Language" content="en-au">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<title>FileDb Overview</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<b>
|
||||
<p><font face="Arial" size="4" color="#0066CC">Overview</font></p>
|
||||
</b>
|
||||
<p><font face="Arial">FileDb is a simple database designed as a simple
|
||||
local database solution for Xamarin Cross-Platform phone (Android, IOS, WinPhone)
|
||||
or any .NET application. <u>FileDb is a No-SQL database</u>
|
||||
meant for use as a local data store for applications.
|
||||
Here are some important points about FileDb:</font></p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<font face="Arial">Stores one table per file, including its index</font></li>
|
||||
<li>
|
||||
<font face="Arial">Extremely Lightweight - less than 50K</font></li>
|
||||
<li>
|
||||
<font face="Arial">Supports field types Int, UInt, Bool, String, Byte,
|
||||
Float, Double and DateTime and also arrays of the same
|
||||
types</font></li>
|
||||
<li>
|
||||
<font face="Arial">Index supports a single Primary Key field (optional)</font></li>
|
||||
<li>
|
||||
<font face="Arial">Compiled versions for Windows Phone
|
||||
RT/PCL,
|
||||
.NET</font></li>
|
||||
<li>
|
||||
<font face="Arial">FileDb is VERY FAST</font></li>
|
||||
<li>
|
||||
<font face="Arial">FileDb is FREE to use in your applications</font></li>
|
||||
<li>
|
||||
<font face="Arial">Use with LINQ to Objects to achieve full relational
|
||||
capability</font></li>
|
||||
</ul>
|
||||
|
||||
<p><font face="Arial">FileDb was specifically designed to use only native
|
||||
.NET data types so there would no need to translate
|
||||
between database storage and the CLR data types.
|
||||
So you can just as easily read/write a String[] field as
|
||||
you would an Int field. Another feature is that a
|
||||
database file created on any .NET platform will work on
|
||||
any other. So you can create a database file on
|
||||
your Windows machine and it can be used in a Silverlight
|
||||
or Windows Phone app.</font></p>
|
||||
|
||||
<p><font color="#0066CC" face="Arial"><b>LINQ + FileDb gives you full
|
||||
relational database capability</b></font></p>
|
||||
<p><font face="Arial">Even though FileDb is a "flat-file" database, using
|
||||
LINQ it becomes fully relational! LINQ
|
||||
to Objects allows you to join Tables together just as
|
||||
you would do in SQL. All of the power of LINQ is
|
||||
available to you: Joins, Grouping, Sum - the lot.
|
||||
(See
|
||||
the examples below.)</font></p>
|
||||
<p><font face="Arial">FileDb also has a built-in
|
||||
query filter parser so you can write SQL-like
|
||||
filter expressions to make filtering data easy, like
|
||||
this:</font></p>
|
||||
<blockquote>
|
||||
<p><b>
|
||||
<font color="#663300" face="Courier New" size="2">
|
||||
string filter = "FirstName IN ('Cindy', 'John') AND Age > 32"</font></b></p>
|
||||
</blockquote>
|
||||
<p><font face="Arial">Use FileDb in your .NET and mobile
|
||||
applications where you need a searchable, updatable
|
||||
local database.</font><p>
|
||||
<b><font face="Arial" color="#0066CC">Use FileDb with Xamarin cross-platform app
|
||||
development</font></b><p><font face="Arial">Most phone apps only require a
|
||||
simple database. By purchasing a source code license you can compile
|
||||
FileDb into your IOS, Android and Windows Phone projects and use the same exact
|
||||
data layer code for them all. This is much easier than using Sqlite
|
||||
wrappers. With FileDb's built in encryption you can have everything you
|
||||
need to have a secure data layer.</font></p>
|
||||
<p>
|
||||
<font color="#0066CC" face="Arial"><b>FileDb Database Overview</b></font></p>
|
||||
|
||||
<p><font face="Arial">FileDb is a simple database designed for use on any .NET platform such as Windows
|
||||
Phone and Silverlight, but its also great for any .NET app where simple local
|
||||
database storage is needed. For example, instead of using XML config files you
|
||||
could use a FileDb database to store and retrieve application data much more
|
||||
efficiently. FileDb allows only a single table per database file, so when we
|
||||
talk about a FileDb database we really mean a single table with an index. The
|
||||
index is stored in the file with the data, and allows an optional Primary Key.</font></p>
|
||||
<p><font face="Arial">FileDb is NOT a relational database - it is NO-SLQ,
|
||||
meaning you can't directly issue SQL commands for querying, adding or updating. However,
|
||||
you CAN use LINQ with
|
||||
FileDb to get full relational query capabilities. And FileDb does include an Expression Parser which parses SQL-like filter
|
||||
expressions, which makes searching, updating and deleting very easy - see below for an example.</font></p>
|
||||
<p><font face="Arial"><u>And FileDb supports using powerful Regular Expressions for
|
||||
filtering.</u></font></p>
|
||||
<p><font face="Arial"><u>FileDb supports AES encryption</u> at the record level. This
|
||||
means the database schema is not encrypted (field names, etc.), but each record
|
||||
is entirely encrypted. Encryption is "all or nothing", meaning it expects that
|
||||
either all records are encrypted or all records are not encrypted. You turn
|
||||
encryption on by passing an encryption key when opening the database.</font></p>
|
||||
<u>
|
||||
<p dir="ltr"><font face="Arial">FileDb is thread-safe for multithreading environments, so it can be accessed from
|
||||
multiple threads at the same time without worrying about database corruption.</font></u></p>
|
||||
<p><font face="Arial">FileDb databases can only be opened by a single
|
||||
application. Any attempt to open the file when already open will fail.
|
||||
This makes sense since its meant for use by a single application at a time
|
||||
(FileDb is not meant as a multi-user database, such as SQL Server Express).<br>
|
||||
</font></p>
|
||||
<p><b><font color="#0066CC" face="Arial">FileDb Classes</font></b></p>
|
||||
<p><font face="Arial">The main FileDb classes are: <b>FileDb</b>, <b>Table</b>,
|
||||
<b>Field</b> and <b>Record</b>. </font> </p>
|
||||
<dir>
|
||||
<b>
|
||||
<li><font face="Arial">FileDb</font></b><font face="Arial">: Represents a database file. All database operations are
|
||||
initiated through this class.<br>
|
||||
</li>
|
||||
</font>
|
||||
<b>
|
||||
<li><font face="Arial">Table</font></b><font face="Arial">: Represents a two
|
||||
dimensional dataset returned from a query. A Table consists of <b>Fields </b>
|
||||
and <b>Records</b>.<br>
|
||||
</li>
|
||||
</font>
|
||||
<b>
|
||||
<li><font face="Arial">Field</font></b><font face="Arial">: Defines the
|
||||
properties of the table column, such as Name and DataType.<br>
|
||||
</li>
|
||||
</font>
|
||||
<b>
|
||||
<li><font face="Arial">Fields</font></b><font face="Arial">: A List of Field
|
||||
objects.<br>
|
||||
</font></li>
|
||||
<li>
|
||||
<font face="Arial">
|
||||
<b>
|
||||
Record</b>: A list of data objects represents a single row in a Table.
|
||||
Implements IEnumerable and the Data property which is used for DataBinding.<br>
|
||||
</li>
|
||||
</font>
|
||||
<b>
|
||||
<li><font face="Arial">Records</font></b><font face="Arial">: A List of
|
||||
Record objects.<br>
|
||||
</li>
|
||||
</font>
|
||||
<b>
|
||||
<li><font face="Arial">FieldValues</font></b><font face="Arial">: A simple
|
||||
Name/Value pair Dictionary. Use this class when adding and updating records.<br>
|
||||
</li>
|
||||
</font>
|
||||
<b>
|
||||
<li><font face="Arial">FilterExpression</font></b><font face="Arial">: Used
|
||||
to filter records for query, update and delete.<br>
|
||||
</li>
|
||||
</font>
|
||||
<b>
|
||||
<li><font face="Arial">FilterExpressionGroup</font></b><font face="Arial">:
|
||||
Used to create compound expressions by grouping FilterExpressions and
|
||||
FilterExpressionGroups.<br>
|
||||
</font></li>
|
||||
</dir>
|
||||
<p><b><font color="#0066CC" face="Arial">Database Fields</font></b></p>
|
||||
<p><font face="Arial">Fields (or Columns) can be of several common types:
|
||||
String, Int, UInt, Bool, Byte, Float, Double and DateTime, or can also be an array of any of
|
||||
these types.</font></p>
|
||||
<p><font face="Arial">Int Fields can be AutoIncrementing, and you can optionally
|
||||
specify one field to be Primary Key (it must be of type Int or String).</font></p>
|
||||
<p><font face="Arial">FileDb doesn't support the notion of NULL fields for the
|
||||
non-array type. Only array type fields can have NULL values. The non-array field
|
||||
values will always have a value, either zero or empty.</font></p>
|
||||
<p><font color="#0066CC" face="Arial"><b>FileDb Records</b></font></p>
|
||||
<p><font face="Arial">FileDb supports two methods of data retrieval. You can say the
|
||||
"default" way is with the built-in Record and Records classes. Think of
|
||||
Record as the .NET DataRow class, and think of Table as a DataTable. Table
|
||||
is a list of Records, and a Record holds the actual values. You access Field
|
||||
values using indexing just as you would a DataRow, like this:</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" width="100%" id="table25" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td>
|
||||
<font face="Courier New" size="2">FileDb employeesDb = new FileDb();<br>
|
||||
employeesDb.Open( Employees.fdb" );<br>
|
||||
<br>
|
||||
Table employees = employeesDb.SelectAllRecords();<br>
|
||||
Record record =
|
||||
employees[0];<br>
|
||||
int id = (int) record["EmployeeId"];<br>
|
||||
// or<br>
|
||||
id
|
||||
= (int) record[0];</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p><font face="Arial">To use a Table with LINQ, you do this:</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" width="100%" id="table26" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td>
|
||||
<font FACE="Courier New" size="2">var recs = from e in
|
||||
employees<br>
|
||||
where (string)
|
||||
e["FirstName"] == "John"<br>
|
||||
select e;</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p><font face="Arial">Notice we have to cast the record value to a string.
|
||||
This is because, just like with the DataRow, Record
|
||||
values are all type object.</font></p>
|
||||
<p><font color="#0066CC" face="Arial"><b>Records and Custom Objects</b></font></p>
|
||||
<p><font face="Arial">Records are great because they require no additional
|
||||
programming and they work with LINQ, albeit with some
|
||||
casting. But you can use your own custom classes
|
||||
if you want because FileDb has template (generic)
|
||||
overloads for each of the SelectRecords methods.
|
||||
You only need to create a class with public properties
|
||||
which match the names of the fields you want to use.
|
||||
Here's an example using the Employees table.</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" width="100%" id="table27" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td>
|
||||
<font FACE="Courier New" size="2">public class Employee<br>
|
||||
{<br>
|
||||
public int EmployeeID { get; set; }<br>
|
||||
public string LastName { get; set; }<br>
|
||||
|
||||
public string
|
||||
FirstName { get; set; }<br>
|
||||
|
||||
public string Title {
|
||||
get; set; }<br>
|
||||
|
||||
public string
|
||||
TitleOfCourtesy { get; set; }<br>
|
||||
|
||||
public DateTime
|
||||
BirthDate { get; set; }<br>
|
||||
|
||||
public DateTime
|
||||
HireDate { get; set; }<br>
|
||||
|
||||
public string Address
|
||||
{ get; set; }<br>
|
||||
|
||||
public string City {
|
||||
get; set; }<br>
|
||||
|
||||
public string Region {
|
||||
get; set; }<br>
|
||||
|
||||
public string
|
||||
PostalCode { get; set; }<br>
|
||||
|
||||
public string Country
|
||||
{ get; set; }<br>
|
||||
|
||||
public string
|
||||
HomePhone { get; set; }<br>
|
||||
|
||||
public string
|
||||
Extension { get; set; }<br>
|
||||
|
||||
public Byte[] Photo {
|
||||
get; set; }<br>
|
||||
|
||||
public string Notes {
|
||||
get; set; }<br>
|
||||
|
||||
public int ReportsTo {
|
||||
get; set; }<br>
|
||||
}</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p><font face="Arial">The templated SelectRecords versions return a IList<T>
|
||||
where T is your custom type.</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" width="100%" id="table28" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td>
|
||||
<font FACE="Courier New" size="2">
|
||||
IList<Employee>
|
||||
employees = employeesDb.SelectAllRecords<Employee>();<br>
|
||||
Employee employee
|
||||
= employees[0];<br>
|
||||
int id = Employee.EmployeeId;<br>
|
||||
<br>
|
||||
var emps = from e in
|
||||
employees<br>
|
||||
where e.FirstName
|
||||
== "John"<br>
|
||||
select e;</td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p><font face="Arial">As you can see, this is much cleaner code. And
|
||||
its actually more efficient since the Record class has
|
||||
more overhead because its not as simple.</font></p>
|
||||
<p><b><font color="#0066CC" face="Arial">Searching and Filtering</font></b></p>
|
||||
<p><font face="Arial">FileDb uses FilterExpressions and
|
||||
FilterExpressionGroups to filter records in queries and
|
||||
updates. We use FilterExpressions for simple queries
|
||||
which consist of a single field comparison (field =
|
||||
'value') and we use FilterExpressionGroups for compound
|
||||
expressions, where multiple expressions and grouping are
|
||||
required. You can add either FilterExpressions or
|
||||
FilterExpressionGroups to a FilterExpressionGroup, thus
|
||||
creating complex expresssions (FileDb processes
|
||||
FilterExpressionGroups recursively).</font></p>
|
||||
<p><font face="Arial">You can either create your own manually in code or
|
||||
use the built-in Expression Parser to create them for
|
||||
you. The Expression Parser recognizes standard SQL
|
||||
comparison operators. You can see it used in the
|
||||
examples below. It also recognizes REGEX, which
|
||||
uses Regular Expressions, and CONTAINS which uses <b>
|
||||
String.Contains</b>. See the section on
|
||||
Regular Expressions below for more info. Field names
|
||||
prefixed with ~ specifies no-case comparison (for
|
||||
strings only).</font></p>
|
||||
<p><font face="Arial">Each time you use () around an expression, a new
|
||||
FilterExpressionGroup will be created. The inner-most expressions are evaluated
|
||||
first, just as in SQL.</font></p>
|
||||
<b>
|
||||
<p><font color="#333333" face="Arial">Example 1: Create a FilterExpression</font></p>
|
||||
</b>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" width="100%" id="table29" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td>
|
||||
<font size="2" FACE="Courier New">// build an expression manually<br>
|
||||
FilterExpression searchExp = new FilterExpression( "LastName", "Peacock",
|
||||
Equality.Equal );<br>
|
||||
<br>
|
||||
// build the same expression using the
|
||||
parser<br>
|
||||
searchExp = FilterExpression.Parse( "LastName = 'Peacock'" ); <br>
|
||||
Table table = employeesDb.SelectRecords(
|
||||
searchExp,
|
||||
new string[] { "ID", "LastName" } );<br>
|
||||
<br>
|
||||
// Or you can simply pass the string filter
|
||||
directly - a FilterExpression will be
|
||||
created in the same way as above<br>
|
||||
<br>
|
||||
table = employeesDb.SelectRecords( "LastName
|
||||
= 'Peacock'", new string[] { "ID", "LastName"
|
||||
} );<br>
|
||||
<br>
|
||||
foreach( Record record in table )<br>
|
||||
{<br>
|
||||
foreach( object value in record )<br>
|
||||
{<br>
|
||||
|
||||
Debug.WriteLine( value );<br>
|
||||
|
||||
}<br>
|
||||
}</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<b>
|
||||
<p><font color="#333333" face="Arial"><br>
|
||||
Example 2: Create a FilterExpressionGroup</font></p>
|
||||
</b>
|
||||
<p><font face="Arial">This example creates two identical FilterExpressionGroups,
|
||||
one using the Expression Parser and the other with code.</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" width="100%" id="table30" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font size="2" FACE="Courier New">// For string fields there are
|
||||
2 ways to specify </font><font size="2" FACE="Courier New">no-case
|
||||
comparisons: you can prefix fieldnames with ~ or you can use ~= as
|
||||
demonstrated below<br>
|
||||
// The first form is needed when using the IN operator, eg.<br>
|
||||
FilterExpressionGroup filterExpGrp =
|
||||
FilterExpressionGroup.Parse( "(FirstName ~= 'andrew' OR ~FirstName = 'nancy')
|
||||
AND LastName = 'Fuller'" );<br>
|
||||
Table table = employeesDb.SelectRecords( filterExpGrp );<br>
|
||||
<br>
|
||||
// equivalent building it manually<br>
|
||||
var fname1Exp = new FilterExpression( "FirstName", "andrew", Equality.Equal,
|
||||
MatchType.IgnoreCase );<br>
|
||||
var fname2Exp = new FilterExpression( "FirstName", "nancy", Equality.Equal,
|
||||
MatchType.IgnoreCase );<br>
|
||||
var lnameExp = new FilterExpression( "LastName", "Fuller", Equality.Equal );
|
||||
// this constructor defaults to MatchType.UseCase<br>
|
||||
var fnamesGrp = new FilterExpressionGroup();<br>
|
||||
fnamesGrp.Add( BoolOp.Or, fname1Exp );<br>
|
||||
fnamesGrp.Add( BoolOp.Or, fname2Exp );<br>
|
||||
var allNamesGrp = new FilterExpressionGroup();<br>
|
||||
allNamesGrp.Add( BoolOp.And, lnameExp );<br>
|
||||
allNamesGrp.Add( BoolOp.And, fnamesGrp );<br>
|
||||
<br>
|
||||
table = employeesDb.SelectRecords( allNamesGrp );<br>
|
||||
<br>
|
||||
// or just pass the filter string directly<br>
|
||||
<br>
|
||||
table = employeesDb.SelectRecords( "(FirstName ~= 'andrew' OR ~FirstName = 'nancy')
|
||||
AND LastName = 'Fuller'" );<br>
|
||||
</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p><font face="Arial"><br>
|
||||
FileDb supports these comparison operators:</font></p>
|
||||
<font face="Arial" size="2">
|
||||
<font face="Courier New">
|
||||
<blockquote>
|
||||
<table border="0" id="table31" style="border-collapse: collapse" bgcolor="#FBEDBB" cellpadding="7">
|
||||
<tr>
|
||||
<font face="Courier New" size="2">
|
||||
<td><b>=</b></td>
|
||||
</font><font face="Arial" size="2">
|
||||
<td width="18"> </td>
|
||||
<font face="Courier New">
|
||||
<td><font face="Arial">Equality</font></td>
|
||||
</font></font>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>~<b>=</b></td>
|
||||
</font><font face="Arial">
|
||||
<td width="18"> </td>
|
||||
<font face="Courier New">
|
||||
<td>No-case equality - strings only</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><font face="Courier New"><b><></b></font></td>
|
||||
</font><font face="Arial">
|
||||
<td width="18"> </td>
|
||||
<font face="Courier New">
|
||||
<td><font face="Arial">Not Equal</font></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><font face="Courier New"><b>!=</b></font></td>
|
||||
</font><font face="Arial">
|
||||
<td width="18"> </td>
|
||||
<font face="Courier New">
|
||||
<td><font face="Arial">Not Equal (same as <>)</font></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><font face="Courier New"><b>>=</b></font></td>
|
||||
</font><font face="Arial">
|
||||
<td width="18"> </td>
|
||||
<font face="Courier New">
|
||||
<td><font face="Arial">Greater than or Equal</font></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><font face="Courier New"><b><=</b></font></td>
|
||||
</font><font face="Arial">
|
||||
<td width="18"> </td>
|
||||
<font face="Courier New">
|
||||
<td><font face="Arial">Less than or Equal</font></td>
|
||||
</tr>
|
||||
</font>
|
||||
<font face="Courier New" size="2">
|
||||
<tr>
|
||||
<td><b><font face="Courier New">REGEX</font></b></td>
|
||||
</font>
|
||||
</font>
|
||||
<font face="Arial" size="2">
|
||||
<font face="Arial">
|
||||
<td width="18"> </td>
|
||||
<td><font face="Arial">Use Regular Expression</font></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<font face="Arial" size="2">
|
||||
<td><b><font face="Courier New">CONTAINS</font></b></td>
|
||||
<font face="Arial">
|
||||
<td width="18"> </td>
|
||||
<td><font face="Arial">Use the .NET String's Contains method</font></td>
|
||||
</font>
|
||||
</font>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b><font face="Courier New">IN</font></b></td>
|
||||
<td width="18"> </td>
|
||||
<td><font face="Arial">Creates a HashSet of values to use like SQL
|
||||
IN operator</font></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<font face="Arial" size="2">
|
||||
<td><b><font face="Courier New">NOT</font></b></td>
|
||||
<font face="Arial">
|
||||
<td width="18"> </td>
|
||||
<td><font face="Arial">Logical Negation, e.g NOT CONTAINS</font></td>
|
||||
</font>
|
||||
</font>
|
||||
</tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
</font>
|
||||
</font>
|
||||
</font></font></font></font></font>
|
||||
<font face="Arial" size="2">
|
||||
<p> </p>
|
||||
</font>
|
||||
<p><b><font face="Arial" color="#0066CC">REGEX - </font>
|
||||
<font color="#0066CC" face="Arial" size="3">Regular Expressions in searches and filtering</font></b></p>
|
||||
<p><font face="Arial" size="3">FileDb supports using Regular Expressions. You can use any RegEx supported by
|
||||
.NET. The Expression Parser supports MatchType.RegEx using the REGEX operator.
|
||||
Internally, FileDb uses FilterExpressions to evaluate fields. You don't
|
||||
need to use them because you can pass in filter strings and they'll be parsed
|
||||
into FilterExpressions/FilterExpressionGroups for you. This is just to
|
||||
show you how can create them manually if you want to. In
|
||||
the example below, both FilterExpressionGroups are identical.</font></p>
|
||||
<font FACE="Courier New" size="2">
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" id="table32" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font size="2" FACE="Courier New">// Using the Expression Parser<br>
|
||||
<br>
|
||||
// You can use brackets around fieldnames if there are spaces in the
|
||||
name<br>
|
||||
FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse( "(~FirstName = 'steven' OR [FirstName]
|
||||
REGEX 'NANCY') AND LastName = 'Fuller'" );<br>
|
||||
Table table = employeesDb.SelectRecords( filterExpGrp );<br>
|
||||
<br>
|
||||
// we can manually build the same FilterExpressionGroup<br>
|
||||
var fname1Exp = FilterExpression.Parse( "~FirstName = steven" );<br>
|
||||
var fname2Exp = new FilterExpression( "FirstName", "NANCY", Equality.Regex );<br>
|
||||
var lnameExp = new FilterExpression( "LastName", "Fuller", Equality.Equal );<br>
|
||||
var fnamesGrp = new FilterExpressionGroup();<br>
|
||||
fnamesGrp.Add( BoolOp.Or, fname1Exp );<br>
|
||||
fnamesGrp.Add( BoolOp.Or, fname2Exp );<br>
|
||||
var allNamesGrp = new FilterExpressionGroup();<br>
|
||||
allNamesGrp.Add( BoolOp.And, lnameExp );<br>
|
||||
allNamesGrp.Add( BoolOp.And, fnamesGrp );<br>
|
||||
<br>
|
||||
table = employeesDb.SelectRecords( allNamesGrp );</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
</font>
|
||||
<font face="Arial" size="2">
|
||||
<p> </p>
|
||||
</font>
|
||||
<p><b><font color="#0066CC" face="Arial" size="3">Using CONTAINS in searches and filtering</font></b></p>
|
||||
<p><font face="Arial" size="3">FileDb supports using Regular Expressions. You can use any RegEx supported by
|
||||
.NET. The Expression Parser supports MatchType.RegEx using the REGEX operator.
|
||||
Internally, FileDb uses FilterExpressions to evaluate fields. You don't
|
||||
need to use them because you can pass in filter strings and they'll be parsed
|
||||
into FilterExpressions/FilterExpressionGroups for you. This is just to
|
||||
show you how can create them manually if you want to. In
|
||||
the example below, both FilterExpressionGroups are identical.</font></p>
|
||||
<font FACE="Courier New" size="2">
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" id="table42" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font size="2" FACE="Courier New">// You can use brackets around fieldnames if there are spaces in the
|
||||
name<br>
|
||||
FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse( "(~FirstName = 'steven' OR [FirstName]
|
||||
CONTAINS 'NANC') AND LastName = 'Fuller'" );<br>
|
||||
Table table = employeesDb.SelectRecords( filterExpGrp );<br>
|
||||
<br>
|
||||
// we can manually build the same FilterExpressionGroup<br>
|
||||
var fname1Exp = FilterExpression.Parse( "~FirstName = steven" );<br>
|
||||
var fname2Exp = new FilterExpression( "FirstName", "NANCY", Equality.Contains );<br>
|
||||
var lnameExp = new FilterExpression( "LastName", "Fuller", Equality.Equal );<br>
|
||||
var fnamesGrp = new FilterExpressionGroup();<br>
|
||||
fnamesGrp.Add( BoolOp.Or, fname1Exp );<br>
|
||||
fnamesGrp.Add( BoolOp.Or, fname2Exp );<br>
|
||||
var allNamesGrp = new FilterExpressionGroup();<br>
|
||||
allNamesGrp.Add( BoolOp.And, lnameExp );<br>
|
||||
allNamesGrp.Add( BoolOp.And, fnamesGrp );<br>
|
||||
<br>
|
||||
table = employeesDb.SelectRecords( allNamesGrp );</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
</font>
|
||||
<p> </p>
|
||||
<p><b><font color="#0066CC" face="Arial" size="3">Sort Ordering</font></b></p>
|
||||
<p><font face="Arial" size="3">Query methods allow for sorting the results by fields. To
|
||||
get a reverse sort, prefix the sort field list with !. To get a no-case sort,
|
||||
prefix with ~. To get both reverse and no-case sort, use both ! and ~.</font></p>
|
||||
<p><font face="Arial" size="3">Example:</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New" size="2">
|
||||
<table border="0" id="table33" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td>
|
||||
<font FACE="Courier New" size="2">
|
||||
Table table = employeesDb.SelectAllRecords( new string[] { "ID", "Firstname", "LastName",
|
||||
"Age" }, false, new string[] { "~LastName", "~FirstName", "!Age" } );</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p><font color="#0066CC" face="Arial" size="3"><b>Selecting a Table from a Table</b></font></p>
|
||||
<p><font face="Arial" size="3">Another very powerful feature of FileDb is the ability to select a Table from
|
||||
another Table. This would allow you to be able to select data from a Table
|
||||
after the database file has been closed, for example.</font></p>
|
||||
<p><font face="Arial" size="3">Example:</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New" size="2">
|
||||
<table border="0" id="table34" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td>
|
||||
<font FACE="Courier New" size="2">
|
||||
customersDb.Open( path + "Customers.fdb" );<br>
|
||||
<br>
|
||||
// select all fields and records from the database table<br>
|
||||
Table customers = customersDb.SelectAllRecords();<br>
|
||||
<br>
|
||||
Table subCusts = customers.SelectRecords( "CustomerID <> 'ALFKI'",<br>
|
||||
new string[] { "CustomerID", "CompanyName", "City" }, new string[] { "~City", "~CompanyName"
|
||||
} );</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p><b><font color="#0066CC" face="Arial" size="3">Encryption</font></b></p>
|
||||
<p><font face="Arial" size="3">Using encryption with FileDb is simple. You only need to
|
||||
specify a string key when you open the database. After that everything is
|
||||
automatic. The only caveat is you must set a key before you add any records.
|
||||
Once a single record has been added without a key set you cannot later add
|
||||
records with a key. Its all or nothing. Likewise, you cannot add records with
|
||||
encryption and later add records without. You must set the encryption key
|
||||
each time you add any records.</font></p>
|
||||
<p><b><font color="#0066CC" face="Arial" size="3">Persisting Tables</font></b></p>
|
||||
<p><font face="Arial" size="3">You can easily save a Table as a new database using
|
||||
Table.SaveToDb. This method creates a new database file using the Fields
|
||||
in the Table then populates it using the Records in the Table. For
|
||||
example, you can select subsets of your data, save it as a new database and send
|
||||
it over the Internet.</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" id="table35" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font FACE="Courier New" size="2">Table table = employeesDb.SelectAllRecords( new string[] { "ID", "Firstname", "LastName"
|
||||
} );<br>
|
||||
table.SaveToDb( "Names.fdb" );</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p><font face="Arial" size="3">You can also save a Table to a database from the
|
||||
FileDb Explorer. Just right-click on the Grid to show
|
||||
the context menu and select the "Create database from
|
||||
Table..." menu item.<br>
|
||||
</font></p>
|
||||
<p><font color="#0066CC" face="Arial" size="3"><b>Using LINQ to Objects with FileDb</b></font></p>
|
||||
<p><font face="Arial" size="3">Microsoft has done an amazing job with LINQ.
|
||||
They have invested a huge amount of time, effort and $
|
||||
in this technology which allows you to query just about
|
||||
any kind of data in a SQL-like way. We use LINQ
|
||||
with FileDb to join Tables as we would using SQL.
|
||||
The difference is that instead of doing it all in a
|
||||
single step with SQL, we must do it in two steps.
|
||||
First we select the data Tables from the database files
|
||||
then we use LINQ to join them together.</font></p>
|
||||
<p><font face="Arial" size="3">LINQ to Objects produces a list of anonymous types as
|
||||
its result set. This is good because we get
|
||||
strongly typed data objects which we can easily use in WPF/Silverlight apps.</font></p>
|
||||
<p><font face="Arial" size="3">Here is an example of doing a simple select using
|
||||
LINQ:</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" id="table36" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font face="Courier New" size="2">//
|
||||
Using the IN operator. Notice the ~
|
||||
prefix on the LastName field. This is how
|
||||
<br>
|
||||
// you can specify case insensitive searches<br>
|
||||
<br>
|
||||
Table
|
||||
employees = employeesDb.SelectRecords( "~LastName
|
||||
IN ('Fuller', 'Peacock')" );<br>
|
||||
<br>
|
||||
var query =
|
||||
from record in employees<br>
|
||||
select new<br>
|
||||
{<br>
|
||||
ID = record["EmployeeId"],<br>
|
||||
Name = record["FirstName"] + " " + record["LastName"],<br>
|
||||
Title = record["Title"]<br>
|
||||
};<br>
|
||||
<br>
|
||||
foreach( var rec in query )<br>
|
||||
{<br>
|
||||
Debug.WriteLine( rec.ToString() );<br>
|
||||
}</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
<p><font face="Arial" size="3">The only thing LINQ did for us in this example was
|
||||
gave us a typed list of anonymous objects. Here's
|
||||
the same thing but with custom objects:</font></p>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" id="table37" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font size="2">IList<Employee></font><font face="Courier New" size="2"> employees
|
||||
= employeesDb.SelectRecords<Employee>(
|
||||
"~LastName
|
||||
IN ('Fuller', 'Peacock')" );<br>
|
||||
<br>
|
||||
var query =<br>
|
||||
from e in employees<br>
|
||||
select e;<br>
|
||||
<br>
|
||||
foreach( var emp in query )<br>
|
||||
{<br>
|
||||
Debug.WriteLine( emp.ToString() );<br>
|
||||
}</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
</blockquote>
|
||||
<p dir="ltr"><font face="Arial" size="3">Now lets tap into LINQ's real power to join tables together
|
||||
like a SQL inner join. Notice in the following example we use the </font>
|
||||
<font face="Courier New" size="2">FilterExpression.CreateInExpressionFromTable</font><font face="Arial"><font size="3">
|
||||
method. We do this to get only the records we are going to need with LINQ.
|
||||
So using FileDb with LINQ is a two step process. You first select the
|
||||
records you will need then use them in the LINQ query. If your database
|
||||
files are large, you can filter the records like this. Otherwise you can
|
||||
just select all records.</font></p>
|
||||
<blockquote>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" id="table38" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font face="Courier New" size="2">FileDb customersDb = new FileDb(),<br>
|
||||
ordersDb = new FileDb(),<br>
|
||||
orderDetailsDb = new FileDb(),<br>
|
||||
productsDb = new FileDb();<br>
|
||||
<br>
|
||||
customersDb.Open( "Customers.fdb" );<br>
|
||||
ordersDb.Open( "Orders.fdb" );<br>
|
||||
orderDetailsDb.Open( "OrderDetails.fdb" );<br>
|
||||
productsDb.Open( "Products.fdb" );<br>
|
||||
<br>
|
||||
// get our target Customer records<br>
|
||||
// Note that we should select only fields we
|
||||
need from each table, but to keep the code<br>
|
||||
// simple for this example we just pass in null for the field
|
||||
list<br>
|
||||
<br>
|
||||
FilterExpression filterExp =
|
||||
FilterExpression.Parse( "CustomerID IN( 'ALFKI',
|
||||
'BONAP' )" );<br>
|
||||
FileDbNs.Table customers =
|
||||
customersDb.SelectRecords( filterExp );<br>
|
||||
<br>
|
||||
// now get only Order records for the target
|
||||
Customer records<br>
|
||||
// CreateInExpressionFromTable will create
|
||||
an IN FilterExpression, which uses a HashSet
|
||||
<br>
|
||||
// for high efficiency when filtering
|
||||
records<br>
|
||||
<br>
|
||||
filterExp =
|
||||
FilterExpression.CreateInExpressionFromTable(
|
||||
"CustomerID", customers, "CustomerID" );<br>
|
||||
FileDbNs.Table orders =
|
||||
ordersDb.SelectRecords( filterExp );<br>
|
||||
<br>
|
||||
// now get only OrderDetails records for the
|
||||
target Order records<br>
|
||||
<br>
|
||||
filterExp =
|
||||
FilterExpression.CreateInExpressionFromTable(
|
||||
"OrderID", orders, "OrderID" );<br>
|
||||
FileDbNs.Table orderDetails =
|
||||
orderDetailsDb.SelectRecords( filterExp );<br>
|
||||
<br>
|
||||
// now get only Product records for the
|
||||
target OrderDetails records<br>
|
||||
<br>
|
||||
filterExp =
|
||||
FilterExpression.CreateInExpressionFromTable(
|
||||
"ProductID", orderDetails, "ProductID" );<br>
|
||||
FileDbNs.Table products =
|
||||
productsDb.SelectRecords( filterExp );<br>
|
||||
<br>
|
||||
// now we're ready to do the join<br>
|
||||
<br>
|
||||
var query =<br>
|
||||
from custRec in customers<br>
|
||||
join orderRec in orders on custRec["CustomerID"]
|
||||
equals orderRec["CustomerID"]<br>
|
||||
join orderDetailRec in orderDetails on
|
||||
orderRec["OrderID"] equals
|
||||
orderDetailRec["OrderID"]<br>
|
||||
join productRec in products on
|
||||
orderDetailRec["ProductID"] equals
|
||||
productRec["ProductID"]<br>
|
||||
select new<br>
|
||||
{<br>
|
||||
ID = custRec["CustomerID"],<br>
|
||||
CompanyName = custRec["CompanyName"],<br>
|
||||
OrderID = orderRec["OrderID"],<br>
|
||||
OrderDate = orderRec["OrderDate"],<br>
|
||||
ProductName = productRec["ProductName"],<br>
|
||||
UnitPrice = orderDetailRec["UnitPrice"],<br>
|
||||
Quantity = orderDetailRec["Quantity"]<br>
|
||||
};<br>
|
||||
<br>
|
||||
foreach( var rec in query )<br>
|
||||
{<br>
|
||||
Debug.WriteLine( rec.ToString() );<br>
|
||||
}</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</font>
|
||||
<p><font face="Arial" size="3">Here's the same thing again using custom objects:</font></p>
|
||||
<font FACE="Courier New">
|
||||
<table border="0" id="table39" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font face="Courier New" size="2">// get our target Customer records<br>
|
||||
<br>
|
||||
FilterExpression filterExp =
|
||||
FilterExpression.Parse( "CustomerID IN( 'ALFKI',
|
||||
'BONAP' )" );<br>
|
||||
IList<Customer> customers =
|
||||
customersDb.SelectRecords<Customer>( filterExp );<br>
|
||||
<br>
|
||||
filterExp =
|
||||
FilterExpression.CreateInExpressionFromTable<Customer>(
|
||||
"CustomerID", customers, "CustomerID" );<br>
|
||||
IList<Order> orders =
|
||||
ordersDb.SelectRecords<Order>( filterExp );<br>
|
||||
<br>
|
||||
// now get only OrderDetails records for the
|
||||
target Order records<br>
|
||||
<br>
|
||||
filterExp =
|
||||
FilterExpression.CreateInExpressionFromTable<Order>(
|
||||
"OrderID", orders, "OrderID" );<br>
|
||||
IList<OrderDetail> orderDetails =
|
||||
orderDetailsDb.SelectRecords<OrderDetail>( filterExp );<br>
|
||||
<br>
|
||||
// now get only Product records for the
|
||||
target OrderDetails records<br>
|
||||
<br>
|
||||
filterExp =
|
||||
FilterExpression.CreateInExpressionFromTable<OrderDetail>(
|
||||
"ProductID", orderDetails, "ProductID" );<br>
|
||||
IList<Product> products =
|
||||
productsDb.SelectRecords<Product>(( filterExp );<br>
|
||||
<br>
|
||||
// now we're ready to do the join<br>
|
||||
<br>
|
||||
var query =<br>
|
||||
from custRec in customers<br>
|
||||
join orderRec in orders on custRec.CustomerID
|
||||
equals orderRec.CustomerID<br>
|
||||
join orderDetailRec in orderDetails on
|
||||
orderRec.OrderID equals orderDetailRec.OrderID<br>
|
||||
join productRec in products on
|
||||
orderDetailRec.ProductID equals productRec.ProductID<br>
|
||||
select new<br>
|
||||
{<br>
|
||||
ID = custRec.CustomerID,<br>
|
||||
CompanyName = custRec.CompanyName,<br>
|
||||
OrderID = orderRec.OrderID,<br>
|
||||
OrderDate = orderRec.OrderDate,<br>
|
||||
ProductName = productRec.ProductName,<br>
|
||||
UnitPrice = orderDetailRec.UnitPrice,<br>
|
||||
Quantity = orderDetailRec.Quantity<br>
|
||||
};<br>
|
||||
<br>
|
||||
foreach( var rec in query )<br>
|
||||
{<br>
|
||||
Debug.WriteLine( rec.ToString() );<br>
|
||||
}</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
<p> </p>
|
||||
</font>
|
||||
<p><b>
|
||||
<font color="#0066CC" face="Arial" size="3">Creating a Database</font></p>
|
||||
</b>
|
||||
<p><font face="Arial" size="3">You create your database programmatically by defining
|
||||
Fields and adding them to an array then calling FileDb.Create, similar to below.
|
||||
Notice we set the ID field to be AutoIncrementing and PrimaryKey. This code
|
||||
creates a database with every type of field.</font></p>
|
||||
<font FACE="Courier New">
|
||||
<blockquote>
|
||||
<table border="0" width="100%" id="table40" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td><font size="2" FACE="Courier New">Field field;<br>
|
||||
var fieldLst = new List<Field>( 20 );<br>
|
||||
field = new Field( "ID", DataType.Int );<br>
|
||||
field.AutoIncStart = 0;<br>
|
||||
field.IsPrimaryKey = true;<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "FirstName", DataType.String );<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "LastName", DataType.String );<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "BirthDate", DataType.DateTime );<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "IsCitizen", DataType.Bool );<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "DoubleField", DataType.Double );<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "ByteField", DataType.Byte );<br>
|
||||
fields.Add( field );<br>
|
||||
<br>
|
||||
// array types<br>
|
||||
field = new Field( "StringArrayField", DataType.String );<br>
|
||||
field.IsArray = true;<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "ByteArrayField", DataType.Byte );<br>
|
||||
field.IsArray = true;<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "IntArrayField", DataType.Int );<br>
|
||||
field.IsArray = true;<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "DoubleArrayField", DataType.Double );<br>
|
||||
field.IsArray = true;<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "DateTimeArrayField", DataType.DateTime );<br>
|
||||
field.IsArray = true;<br>
|
||||
fields.Add( field );<br>
|
||||
field = new Field( "BoolArray", DataType.Bool );<br>
|
||||
field.IsArray = true;<br>
|
||||
fields.Add( field );</font><font size="2"><br>
|
||||
<br>
|
||||
var myDb = new FileDb();</font><font size="2" FACE="Courier New"><br>
|
||||
myDb.Create( "MyDatabase.fdb", fieldLst.ToArray() );</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
</font>
|
||||
<p><b><font color="#0066CC" face="Arial" size="3"><br>
|
||||
Adding Records</font></b></p>
|
||||
<p><font face="Arial" size="3">You add records to a database by creating a FieldValues
|
||||
object and adding field values. You do not need to represent every field of the
|
||||
database. Fields that are missing will be initialized to the default value (zero
|
||||
for numeric types, DateTime.MinValue,
|
||||
empty for String and NULL for array types).</font></p>
|
||||
<font FACE="Courier New">
|
||||
<blockquote>
|
||||
<table border="0" width="100%" id="table41" bgcolor="#FBEDBB" cellpadding="10">
|
||||
<tr>
|
||||
<td>
|
||||
<font size="2" FACE="Courier New">var record = new FieldValues();<br>
|
||||
record.Add( "FirstName", "Nancy" );<br>
|
||||
record.Add( "LastName", "Davolio" );<br>
|
||||
record.Add( "BirthDate", new DateTime( 1968, 12, 8 ) );<br>
|
||||
record.Add( "IsCitizen", true );<br>
|
||||
record.Add( "Double", 1.23 );<br>
|
||||
record.Add( "Byte", 1 );<br>
|
||||
record.Add( "StringArray", new string[] { "s1", "s2", "s3" } );<br>
|
||||
record.Add( "ByteArray", new Byte[] { 1, 2, 3, 4 } );<br>
|
||||
record.Add( "IntArray", new int[] { 100, 200, 300, 400 } );<br>
|
||||
record.Add( "DoubleArray", new double[] { 1.2, 2.4, 3.6, 4.8 } );<br>
|
||||
record.Add( "DateTimeArray", new DateTime[] { DateTime.Now, DateTime.Now,
|
||||
DateTime.Now, DateTime.Now } );<br>
|
||||
record.Add( "BoolArray", new bool[] { true, false, true, false } );<br>
|
||||
<br>
|
||||
myDb.AddRecord( record );</font></td>
|
||||
</tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
<p> </p>
|
||||
</font>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
86
FileDbCache/UWP/FileDbCache.UWP.csproj
Normal file
86
FileDbCache/UWP/FileDbCache.UWP.csproj
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{BEEB142A-5FA3-468D-810A-32A4A5BD6D5D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl.Caching</RootNamespace>
|
||||
<AssemblyName>FileDbCache.UWP</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.10240.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileDbCache.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Properties\FileDbCache.UWP.rd.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.0.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MapControl\UWP\MapControl.UWP.csproj">
|
||||
<Project>{9545f73c-9c35-4cf6-baae-19a0baebd344}</Project>
|
||||
<Name>MapControl.UWP</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FileDbPcl, Version=6.1.0.0, Culture=neutral, PublicKeyToken=ba3f58a0e60cd01d, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\FileDb\FileDbPcl.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
260
FileDbCache/UWP/FileDbCache.cs
Normal file
260
FileDbCache/UWP/FileDbCache.cs
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI.Xaml;
|
||||
using FileDbNs;
|
||||
|
||||
namespace MapControl.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// IImageCache implementation based on FileDb, a free and simple No-SQL database by EzTools Software.
|
||||
/// See http://www.eztools-software.com/tools/filedb/.
|
||||
/// </summary>
|
||||
public sealed class FileDbCache : IImageCache, IDisposable
|
||||
{
|
||||
private const string keyField = "Key";
|
||||
private const string valueField = "Value";
|
||||
private const string expiresField = "Expires";
|
||||
|
||||
private readonly FileDb fileDb = new FileDb();
|
||||
private readonly StorageFolder folder;
|
||||
private readonly string fileName;
|
||||
|
||||
public FileDbCache(StorageFolder folder, string fileName = "TileCache.fdb", bool autoFlush = true, int autoCleanThreshold = -1)
|
||||
{
|
||||
if (folder == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter folder must not be null.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
throw new ArgumentNullException("The parameter fileName must not be null.");
|
||||
}
|
||||
|
||||
this.folder = folder;
|
||||
this.fileName = fileName;
|
||||
|
||||
fileDb.AutoFlush = autoFlush;
|
||||
fileDb.AutoCleanThreshold = autoCleanThreshold;
|
||||
|
||||
Application.Current.Resuming += async (s, e) => await Open();
|
||||
Application.Current.Suspending += (s, e) => Close();
|
||||
|
||||
var task = Open();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public void Clean()
|
||||
{
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
int deleted = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThan));
|
||||
|
||||
if (deleted > 0)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: Deleted {0} expired items", deleted);
|
||||
fileDb.Clean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task<ImageCacheItem> GetAsync(string key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (!fileDb.IsOpen)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Task.Run(() => Get(key));
|
||||
}
|
||||
|
||||
public Task SetAsync(string key, IBuffer buffer, DateTime expiration)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter buffer must not be null.");
|
||||
}
|
||||
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
var bytes = buffer.ToArray();
|
||||
var ok = AddOrUpdateRecord(key, bytes, expiration);
|
||||
|
||||
if (!ok && (await RepairDatabase()))
|
||||
{
|
||||
AddOrUpdateRecord(key, bytes, expiration);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task Open()
|
||||
{
|
||||
if (!fileDb.IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
var file = await folder.GetFileAsync(fileName);
|
||||
var stream = await file.OpenAsync(FileAccessMode.ReadWrite);
|
||||
|
||||
fileDb.Open(stream.AsStream());
|
||||
Debug.WriteLine("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, file.Path);
|
||||
|
||||
Clean();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await CreateDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
fileDb.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreateDatabase()
|
||||
{
|
||||
Close();
|
||||
|
||||
var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
|
||||
var stream = await file.OpenAsync(FileAccessMode.ReadWrite);
|
||||
|
||||
fileDb.Create(stream.AsStream(), new Field[]
|
||||
{
|
||||
new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true },
|
||||
new Field(valueField, DataTypeEnum.Byte) { IsArray = true },
|
||||
new Field(expiresField, DataTypeEnum.DateTime)
|
||||
});
|
||||
|
||||
Debug.WriteLine("FileDbCache: Created database " + file.Path);
|
||||
}
|
||||
|
||||
private async Task<bool> RepairDatabase()
|
||||
{
|
||||
try
|
||||
{
|
||||
fileDb.Reindex();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.Reindex(): " + ex.Message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await CreateDatabase();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: Creating database {0}: {1}", Path.Combine(folder.Path, fileName), ex.Message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private ImageCacheItem Get(string key)
|
||||
{
|
||||
var fields = new string[] { valueField, expiresField };
|
||||
|
||||
try
|
||||
{
|
||||
var record = fileDb.GetRecordByKey(key, fields, false);
|
||||
|
||||
if (record != null)
|
||||
{
|
||||
return new ImageCacheItem
|
||||
{
|
||||
Buffer = ((byte[])record[0]).AsBuffer(),
|
||||
Expiration = (DateTime)record[1]
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool AddOrUpdateRecord(string key, byte[] value, DateTime expiration)
|
||||
{
|
||||
var fieldValues = new FieldValues(3);
|
||||
fieldValues.Add(valueField, value);
|
||||
fieldValues.Add(expiresField, expiration);
|
||||
|
||||
bool recordExists;
|
||||
|
||||
try
|
||||
{
|
||||
recordExists = fileDb.GetRecordByKey(key, new string[0], false) != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recordExists)
|
||||
{
|
||||
try
|
||||
{
|
||||
fileDb.UpdateRecordByKey(key, fieldValues);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.UpdateRecordByKey(\"{0}\"): {1}", key, ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
fieldValues.Add(keyField, key);
|
||||
fileDb.AddRecord(fieldValues);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.AddRecord(\"{0}\"): {1}", key, ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Debug.WriteLine("FileDbCache: Writing \"{0}\", Expires {1}", key, expiration.ToLocalTime());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
FileDbCache/UWP/Properties/AssemblyInfo.cs
Normal file
14
FileDbCache/UWP/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("XAML Map Control FileDbCache (UWP)")]
|
||||
[assembly: AssemblyDescription("IImageCache implementation based on EzTools FileDb")]
|
||||
[assembly: AssemblyProduct("XAML Map Control")]
|
||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("4.3.0")]
|
||||
[assembly: AssemblyFileVersion("4.3.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
5
FileDbCache/UWP/Properties/FileDbCache.UWP.rd.xml
Normal file
5
FileDbCache/UWP/Properties/FileDbCache.UWP.rd.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Library Name="FileDbCache.UWP">
|
||||
</Library>
|
||||
</Directives>
|
||||
63
FileDbCache/WPF/FileDbCache.WPF.csproj
Normal file
63
FileDbCache/WPF/FileDbCache.WPF.csproj
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{AD1CB53E-7AA4-4EC0-B901-B4E0E2665133}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MapControl.Caching</RootNamespace>
|
||||
<AssemblyName>FileDbCache.WPF</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FileDb">
|
||||
<HintPath>..\FileDb\FileDb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileDbCache.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
405
FileDbCache/WPF/FileDbCache.cs
Normal file
405
FileDbCache/WPF/FileDbCache.cs
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2017 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using FileDbNs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Caching;
|
||||
|
||||
namespace MapControl.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// ObjectCache implementation based on FileDb, a free and simple No-SQL database by EzTools Software.
|
||||
/// See http://www.eztools-software.com/tools/filedb/.
|
||||
/// </summary>
|
||||
public sealed class FileDbCache : ObjectCache, IDisposable
|
||||
{
|
||||
private const string keyField = "Key";
|
||||
private const string valueField = "Value";
|
||||
private const string expiresField = "Expires";
|
||||
|
||||
private readonly FileDb fileDb = new FileDb();
|
||||
private readonly string dbPath;
|
||||
|
||||
public FileDbCache(string path, bool autoFlush = true, int autoCleanThreshold = -1)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentException("The parameter path must not be null or empty.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Path.GetExtension(path)))
|
||||
{
|
||||
path = Path.Combine(path, "TileCache.fdb");
|
||||
}
|
||||
|
||||
dbPath = path;
|
||||
|
||||
fileDb.AutoFlush = autoFlush;
|
||||
fileDb.AutoCleanThreshold = autoCleanThreshold;
|
||||
|
||||
try
|
||||
{
|
||||
fileDb.Open(dbPath, false);
|
||||
|
||||
Debug.WriteLine("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, dbPath);
|
||||
|
||||
Clean();
|
||||
}
|
||||
catch
|
||||
{
|
||||
CreateDatabase();
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.ProcessExit += (s, e) => Close();
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
public override DefaultCacheCapabilities DefaultCacheCapabilities
|
||||
{
|
||||
get { return DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations; }
|
||||
}
|
||||
|
||||
public override object this[string key]
|
||||
{
|
||||
get { return Get(key); }
|
||||
set { Set(key, value, null); }
|
||||
}
|
||||
|
||||
protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
throw new NotSupportedException("FileDbCache does not support the ability to enumerate items.");
|
||||
}
|
||||
|
||||
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(IEnumerable<string> keys, string regionName = null)
|
||||
{
|
||||
throw new NotSupportedException("FileDbCache does not support the ability to create change monitors.");
|
||||
}
|
||||
|
||||
public override long GetCount(string regionName = null)
|
||||
{
|
||||
if (regionName != null)
|
||||
{
|
||||
throw new NotSupportedException("The parameter regionName must be null.");
|
||||
}
|
||||
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
return fileDb.NumRecords;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.NumRecords: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override bool Contains(string key, string regionName = null)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (regionName != null)
|
||||
{
|
||||
throw new NotSupportedException("The parameter regionName must be null.");
|
||||
}
|
||||
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
return fileDb.GetRecordByKey(key, new string[0], false) != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override object Get(string key, string regionName = null)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (regionName != null)
|
||||
{
|
||||
throw new NotSupportedException("The parameter regionName must be null.");
|
||||
}
|
||||
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
var record = fileDb.GetRecordByKey(key, new string[] { valueField }, false);
|
||||
|
||||
if (record != null)
|
||||
{
|
||||
return record[0];
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override CacheItem GetCacheItem(string key, string regionName = null)
|
||||
{
|
||||
var value = Get(key, regionName);
|
||||
|
||||
return value != null ? new CacheItem(key, value) : null;
|
||||
}
|
||||
|
||||
public override IDictionary<string, object> GetValues(IEnumerable<string> keys, string regionName = null)
|
||||
{
|
||||
return keys.ToDictionary(key => key, key => Get(key, regionName));
|
||||
}
|
||||
|
||||
public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter value must not be null.");
|
||||
}
|
||||
|
||||
if (policy == null)
|
||||
{
|
||||
throw new ArgumentNullException("The parameter policy must not be null.");
|
||||
}
|
||||
|
||||
if (regionName != null)
|
||||
{
|
||||
throw new NotSupportedException("The parameter regionName must be null.");
|
||||
}
|
||||
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
var expiration = DateTime.MaxValue;
|
||||
|
||||
if (policy.AbsoluteExpiration != InfiniteAbsoluteExpiration)
|
||||
{
|
||||
expiration = policy.AbsoluteExpiration.DateTime;
|
||||
}
|
||||
else if (policy.SlidingExpiration != NoSlidingExpiration)
|
||||
{
|
||||
expiration = DateTime.UtcNow + policy.SlidingExpiration;
|
||||
}
|
||||
|
||||
if (!AddOrUpdateRecord(key, value, expiration) && RepairDatabase())
|
||||
{
|
||||
AddOrUpdateRecord(key, value, expiration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
|
||||
{
|
||||
Set(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
|
||||
}
|
||||
|
||||
public override void Set(CacheItem item, CacheItemPolicy policy)
|
||||
{
|
||||
Set(item.Key, item.Value, policy, item.RegionName);
|
||||
}
|
||||
|
||||
public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
|
||||
{
|
||||
var oldValue = Get(key, regionName);
|
||||
|
||||
Set(key, value, policy);
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
|
||||
{
|
||||
return AddOrGetExisting(key, value, new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }, regionName);
|
||||
}
|
||||
|
||||
public override CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
|
||||
{
|
||||
var oldItem = GetCacheItem(item.Key, item.RegionName);
|
||||
|
||||
Set(item, policy);
|
||||
|
||||
return oldItem;
|
||||
}
|
||||
|
||||
public override object Remove(string key, string regionName = null)
|
||||
{
|
||||
var oldValue = Get(key, regionName);
|
||||
|
||||
if (oldValue != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
fileDb.DeleteRecordByKey(key);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.DeleteRecordByKey(\"{0}\"): {1}", key, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
fileDb.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void Clean()
|
||||
{
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
int deleted = fileDb.DeleteRecords(new FilterExpression(expiresField, DateTime.UtcNow, ComparisonOperatorEnum.LessThan));
|
||||
|
||||
if (deleted > 0)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: Deleted {0} expired items", deleted);
|
||||
fileDb.Clean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
fileDb.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateDatabase()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (File.Exists(dbPath))
|
||||
{
|
||||
File.Delete(dbPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(dbPath));
|
||||
}
|
||||
|
||||
fileDb.Create(dbPath, new Field[]
|
||||
{
|
||||
new Field(keyField, DataTypeEnum.String) { IsPrimaryKey = true },
|
||||
new Field(valueField, DataTypeEnum.Byte) { IsArray = true },
|
||||
new Field(expiresField, DataTypeEnum.DateTime)
|
||||
});
|
||||
|
||||
Debug.WriteLine("FileDbCache: Created database " + dbPath);
|
||||
}
|
||||
|
||||
private bool RepairDatabase()
|
||||
{
|
||||
try
|
||||
{
|
||||
fileDb.Reindex();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.Reindex(): " + ex.Message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CreateDatabase();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: Failed creating database {0}: {1}", dbPath, ex.Message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool AddOrUpdateRecord(string key, object value, DateTime expiration)
|
||||
{
|
||||
var fieldValues = new FieldValues(3);
|
||||
fieldValues.Add(valueField, value);
|
||||
fieldValues.Add(expiresField, expiration);
|
||||
|
||||
bool recordExists;
|
||||
|
||||
try
|
||||
{
|
||||
recordExists = fileDb.GetRecordByKey(key, new string[0], false) != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.GetRecordByKey(\"{0}\"): {1}", key, ex.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recordExists)
|
||||
{
|
||||
try
|
||||
{
|
||||
fileDb.UpdateRecordByKey(key, fieldValues);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.UpdateRecordByKey(\"{0}\"): {1}", key, ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
fieldValues.Add(keyField, key);
|
||||
fileDb.AddRecord(fieldValues);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: FileDb.AddRecord(\"{0}\"): {1}", key, ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Debug.WriteLine("FileDbCache: Writing \"{0}\", Expires {1}", key, expiration.ToLocalTime());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
FileDbCache/WPF/Properties/AssemblyInfo.cs
Normal file
14
FileDbCache/WPF/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("XAML Map Control FileDbCache (WPF)")]
|
||||
[assembly: AssemblyDescription("ObjectCache implementation based on EzTools FileDb")]
|
||||
[assembly: AssemblyProduct("XAML Map Control")]
|
||||
[assembly: AssemblyCompany("Clemens Fischer")]
|
||||
[assembly: AssemblyCopyright("© 2017 Clemens Fischer")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyVersion("4.3.0")]
|
||||
[assembly: AssemblyFileVersion("4.3.0")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
Loading…
Add table
Add a link
Reference in a new issue