mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2025-12-06 07:12:04 +01:00
Version 4.12.2 Updated FileDbCache and MapImages
This commit is contained in:
parent
85c54f075c
commit
a860c8d341
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
|
@ -1,983 +0,0 @@
|
|||
<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>
|
||||
|
|
@ -45,6 +45,9 @@
|
|||
<EmbeddedResource Include="Properties\FileDbCache.UWP.rd.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FileDb.Standard">
|
||||
<Version>7.4.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.2.8</Version>
|
||||
</PackageReference>
|
||||
|
|
@ -60,12 +63,6 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ namespace MapControl.Caching
|
|||
private const string expiresField = "Expires";
|
||||
|
||||
private readonly FileDb fileDb = new FileDb();
|
||||
private readonly StorageFolder folder;
|
||||
private readonly string fileName;
|
||||
private readonly string dbPath;
|
||||
|
||||
public FileDbCache(StorageFolder folder, string fileName = "TileCache.fdb", bool autoFlush = true, int autoCleanThreshold = -1)
|
||||
{
|
||||
|
|
@ -40,16 +39,15 @@ namespace MapControl.Caching
|
|||
throw new ArgumentNullException("The parameter fileName must not be null.");
|
||||
}
|
||||
|
||||
this.folder = folder;
|
||||
this.fileName = fileName;
|
||||
dbPath = Path.Combine(folder.Path, "TileCache.fdb");
|
||||
|
||||
fileDb.AutoFlush = autoFlush;
|
||||
fileDb.AutoCleanThreshold = autoCleanThreshold;
|
||||
|
||||
Application.Current.Resuming += async (s, e) => await Open();
|
||||
Application.Current.Resuming += (s, e) => Open();
|
||||
Application.Current.Suspending += (s, e) => Close();
|
||||
|
||||
var task = Open();
|
||||
Open();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -78,12 +76,7 @@ namespace MapControl.Caching
|
|||
throw new ArgumentNullException("The parameter key must not be null.");
|
||||
}
|
||||
|
||||
if (!fileDb.IsOpen)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Task.Run(() => Get(key));
|
||||
return fileDb.IsOpen ? Task.Run(() => Get(key)) : null;
|
||||
}
|
||||
|
||||
public Task SetAsync(string key, IBuffer buffer, DateTime expiration)
|
||||
|
|
@ -98,14 +91,14 @@ namespace MapControl.Caching
|
|||
throw new ArgumentNullException("The parameter buffer must not be null.");
|
||||
}
|
||||
|
||||
return Task.Run(async () =>
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (fileDb.IsOpen)
|
||||
{
|
||||
var bytes = buffer.ToArray();
|
||||
var ok = AddOrUpdateRecord(key, bytes, expiration);
|
||||
|
||||
if (!ok && (await RepairDatabase()))
|
||||
if (!ok && RepairDatabase())
|
||||
{
|
||||
AddOrUpdateRecord(key, bytes, expiration);
|
||||
}
|
||||
|
|
@ -113,23 +106,20 @@ namespace MapControl.Caching
|
|||
});
|
||||
}
|
||||
|
||||
private async Task Open()
|
||||
private void 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);
|
||||
fileDb.Open(dbPath);
|
||||
Debug.WriteLine("FileDbCache: Opened database with {0} cached items in {1}", fileDb.NumRecords, dbPath);
|
||||
|
||||
Clean();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await CreateDatabase();
|
||||
CreateDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -142,24 +132,21 @@ namespace MapControl.Caching
|
|||
}
|
||||
}
|
||||
|
||||
private async Task CreateDatabase()
|
||||
private void CreateDatabase()
|
||||
{
|
||||
Close();
|
||||
|
||||
var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
|
||||
var stream = await file.OpenAsync(FileAccessMode.ReadWrite);
|
||||
|
||||
fileDb.Create(stream.AsStream(), new Field[]
|
||||
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 " + file.Path);
|
||||
Debug.WriteLine("FileDbCache: Created database " + dbPath);
|
||||
}
|
||||
|
||||
private async Task<bool> RepairDatabase()
|
||||
private bool RepairDatabase()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -173,12 +160,12 @@ namespace MapControl.Caching
|
|||
|
||||
try
|
||||
{
|
||||
await CreateDatabase();
|
||||
CreateDatabase();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("FileDbCache: Creating database {0}: {1}", Path.Combine(folder.Path, fileName), ex.Message);
|
||||
Debug.WriteLine("FileDbCache: Creating database {0}: {1}", dbPath, ex.Message);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@
|
|||
<AssemblyOriginatorKeyFile>..\..\MapControl.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="FileDb">
|
||||
<HintPath>..\FileDb\FileDb.dll</HintPath>
|
||||
<Reference Include="FileDb.Net, Version=7.3.0.0, Culture=neutral, PublicKeyToken=ba3f58a0e60cd01d, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\FileDb.Net.7.4.3\lib\net45\FileDb.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
|
|
@ -51,6 +51,7 @@
|
|||
<None Include="..\..\MapControl.snk">
|
||||
<Link>MapControl.snk</Link>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
|
|
|||
4
FileDbCache/WPF/packages.config
Normal file
4
FileDbCache/WPF/packages.config
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FileDb.Net" version="7.4.3" targetFramework="net48" />
|
||||
</packages>
|
||||
|
|
@ -24,21 +24,21 @@ namespace MapControl
|
|||
public static int MaxLoadTasks { get; set; } = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum expiration time for cached tile images. The default value is one hour.
|
||||
/// Default expiration time for cached tile images. Used when no expiration time
|
||||
/// was transmitted on download. The default value is one day.
|
||||
/// </summary>
|
||||
public static TimeSpan MinCacheExpiration { get; set; } = TimeSpan.FromHours(1);
|
||||
public static TimeSpan DefaultCacheExpiration { get; set; } = TimeSpan.FromDays(1);
|
||||
|
||||
/// <summary>
|
||||
/// Minimum expiration time for cached tile images. The default value is one day.
|
||||
/// </summary>
|
||||
public static TimeSpan MinCacheExpiration { get; set; } = TimeSpan.FromDays(1);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum expiration time for cached tile images. The default value is one week.
|
||||
/// </summary>
|
||||
public static TimeSpan MaxCacheExpiration { get; set; } = TimeSpan.FromDays(7);
|
||||
|
||||
/// <summary>
|
||||
/// Default expiration time for cached tile images. Used when no expiration time
|
||||
/// was transmitted on download. The default value is one day.
|
||||
/// </summary>
|
||||
public static TimeSpan DefaultCacheExpiration { get; set; } = TimeSpan.FromDays(1);
|
||||
|
||||
/// <summary>
|
||||
/// Format string for creating cache keys from the sourceName argument passed to LoadTilesAsync,
|
||||
/// the ZoomLevel, XIndex, and Y properties of a Tile, and the image file extension.
|
||||
|
|
@ -68,19 +68,29 @@ namespace MapControl
|
|||
/// Loads all pending tiles from the tiles collection.
|
||||
/// If tileSource.UriFormat starts with "http" and sourceName is a non-empty string,
|
||||
/// tile images will be cached in the TileImageLoader's Cache (if it's not null).
|
||||
/// The method is async void because it implements void ITileImageLoader.LoadTilesAsync
|
||||
/// and is not awaited when it is called in MapTileLayer.UpdateTiles().
|
||||
/// </summary>
|
||||
public void LoadTilesAsync(IEnumerable<Tile> tiles, TileSource tileSource, string sourceName)
|
||||
public async void LoadTilesAsync(IEnumerable<Tile> tiles, TileSource tileSource, string sourceName)
|
||||
{
|
||||
tileQueue.Clear();
|
||||
|
||||
if (tileSource != null)
|
||||
{
|
||||
SetLoadTileImageFunction(tileSource, sourceName);
|
||||
|
||||
tiles = tiles.Where(tile => tile.Pending);
|
||||
|
||||
if (tiles.Any())
|
||||
if (tiles.Any() && tileSource != null)
|
||||
{
|
||||
if (Cache != null &&
|
||||
tileSource.UriFormat != null &&
|
||||
tileSource.UriFormat.StartsWith("http") &&
|
||||
!string.IsNullOrEmpty(sourceName))
|
||||
{
|
||||
loadTileImage = tile => LoadCachedTileImageAsync(tile, tileSource, sourceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadTileImage = tile => LoadTileImageAsync(tile, tileSource);
|
||||
}
|
||||
|
||||
tileQueue.Enqueue(tiles);
|
||||
|
||||
var newTasks = Math.Min(tileQueue.Count, MaxLoadTasks) - taskCount;
|
||||
|
|
@ -89,8 +99,7 @@ namespace MapControl
|
|||
{
|
||||
Interlocked.Add(ref taskCount, newTasks);
|
||||
|
||||
Task.WhenAll(Enumerable.Range(0, newTasks).Select(n => LoadTilesFromQueueAsync())); // not awaited
|
||||
}
|
||||
await Task.WhenAll(Enumerable.Range(0, newTasks).Select(n => LoadTilesFromQueueAsync())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -116,21 +125,6 @@ namespace MapControl
|
|||
Interlocked.Decrement(ref taskCount);
|
||||
}
|
||||
|
||||
private void SetLoadTileImageFunction(TileSource tileSource, string sourceName)
|
||||
{
|
||||
if (Cache != null &&
|
||||
tileSource.UriFormat != null &&
|
||||
tileSource.UriFormat.StartsWith("http") &&
|
||||
!string.IsNullOrEmpty(sourceName))
|
||||
{
|
||||
loadTileImage = tile => LoadCachedTileImageAsync(tile, tileSource, sourceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadTileImage = tile => LoadTileImageAsync(tile, tileSource);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task LoadCachedTileImageAsync(Tile tile, TileSource tileSource, string sourceName)
|
||||
{
|
||||
var uri = tileSource.GetUri(tile.XIndex, tile.Y, tile.ZoomLevel);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
|
@ -120,48 +119,6 @@ namespace MapControl.Images
|
|||
}
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<ImageOverlay>> ReadGroundOverlaysFromArchiveAsync(string archiveFile)
|
||||
{
|
||||
using (var archive = ZipFile.OpenRead(archiveFile))
|
||||
{
|
||||
var docEntry = archive.GetEntry("doc.kml")
|
||||
?? archive.Entries.FirstOrDefault(e => e.Name.EndsWith(".kml"));
|
||||
|
||||
if (docEntry == null)
|
||||
{
|
||||
throw new ArgumentException("No KML entry found in " + archiveFile);
|
||||
}
|
||||
|
||||
var kmlDocument = new XmlDocument();
|
||||
|
||||
using (var docStream = docEntry.Open())
|
||||
{
|
||||
kmlDocument.Load(docStream);
|
||||
}
|
||||
|
||||
var imageOverlays = await Task.Run(() => ReadGroundOverlays(kmlDocument).ToList());
|
||||
|
||||
foreach (var imageOverlay in imageOverlays)
|
||||
{
|
||||
var imageEntry = archive.GetEntry(imageOverlay.ImagePath);
|
||||
|
||||
if (imageEntry != null)
|
||||
{
|
||||
using (var zipStream = imageEntry.Open())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await zipStream.CopyToAsync(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
imageOverlay.ImageSource = await ImageLoader.LoadImageAsync(memoryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imageOverlays;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ImageOverlay> ReadGroundOverlays(XmlDocument kmlDocument)
|
||||
{
|
||||
foreach (XmlElement groundOverlayElement in kmlDocument.GetElementsByTagName("GroundOverlay"))
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
|
@ -41,5 +42,47 @@ namespace MapControl.Images
|
|||
|
||||
return imageOverlays;
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<ImageOverlay>> ReadGroundOverlaysFromArchiveAsync(string archiveFile)
|
||||
{
|
||||
using (var archive = ZipFile.OpenRead(archiveFile))
|
||||
{
|
||||
var docEntry = archive.GetEntry("doc.kml")
|
||||
?? archive.Entries.FirstOrDefault(e => e.Name.EndsWith(".kml"));
|
||||
|
||||
if (docEntry == null)
|
||||
{
|
||||
throw new ArgumentException("No KML entry found in " + archiveFile);
|
||||
}
|
||||
|
||||
var kmlDocument = new XmlDocument();
|
||||
|
||||
using (var docStream = docEntry.Open())
|
||||
{
|
||||
kmlDocument.Load(docStream);
|
||||
}
|
||||
|
||||
var imageOverlays = await Task.Run(() => ReadGroundOverlays(kmlDocument).ToList());
|
||||
|
||||
foreach (var imageOverlay in imageOverlays)
|
||||
{
|
||||
var imageEntry = archive.GetEntry(imageOverlay.ImagePath);
|
||||
|
||||
if (imageEntry != null)
|
||||
{
|
||||
using (var zipStream = imageEntry.Open())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await zipStream.CopyToAsync(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
imageOverlay.ImageSource = await ImageLoader.LoadImageAsync(memoryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imageOverlays;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
// © 2019 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
|
@ -32,5 +34,50 @@ namespace MapControl.Images
|
|||
return imageOverlays;
|
||||
});
|
||||
}
|
||||
|
||||
private static Task<List<ImageOverlay>> ReadGroundOverlaysFromArchiveAsync(string archiveFile)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
using (var archive = ZipFile.OpenRead(archiveFile))
|
||||
{
|
||||
var docEntry = archive.GetEntry("doc.kml")
|
||||
?? archive.Entries.FirstOrDefault(e => e.Name.EndsWith(".kml"));
|
||||
|
||||
if (docEntry == null)
|
||||
{
|
||||
throw new ArgumentException("No KML entry found in " + archiveFile);
|
||||
}
|
||||
|
||||
var kmlDocument = new XmlDocument();
|
||||
|
||||
using (var docStream = docEntry.Open())
|
||||
{
|
||||
kmlDocument.Load(docStream);
|
||||
}
|
||||
|
||||
var imageOverlays = ReadGroundOverlays(kmlDocument).ToList();
|
||||
|
||||
foreach (var imageOverlay in imageOverlays)
|
||||
{
|
||||
var imageEntry = archive.GetEntry(imageOverlay.ImagePath);
|
||||
|
||||
if (imageEntry != null)
|
||||
{
|
||||
using (var zipStream = imageEntry.Open())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
zipStream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
imageOverlay.ImageSource = ImageLoader.LoadImage(memoryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imageOverlays;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue