예제 #1
0
        /// <summary>
        /// Writes the file with isolatedStorageSchema information.
        /// </summary>
        /// <param name="stream">Stream to which to write</param>
        /// <param name="uri">Scope uri to be written</param>
        /// <param name="scope">Scope Name</param>
        /// <param name="storageSchema">Schema to be written</param>
        private void WriteSchemaFile(Stream stream,
                                     Uri uri,
                                     string scope,
                                     OfflineSchema storageSchema)
        {
            // Write data as text, so create the stream reader.
            using (var writer = new StreamWriter(stream))
            {
                // Write the text version of the Uri.
                writer.WriteLine(uri.AbsoluteUri);
                writer.WriteLine(scope);

                // Get the list of types as strings and sort to make comparison
                // faster when reading.
                List <string> types = (from type in storageSchema.Collections
                                       select type.FullName).ToList();
                types.Sort();

                // Write the types.
                foreach (string type in types)
                {
                    writer.WriteLine(type);
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Constructor for the WinEightStorageOfflineContext
        /// </summary>
        /// <param name="schema">The WinEightStorageSchema that specifies the set of the collections for the c.</param>
        /// <param name="scopeName">The scope name used to identify the scope on the service.</param>
        /// <param name="cachePath">Path in isolated storage where the data will be stored.</param>
        /// <param name="uri">Uri of the scope.  Used to intialize the CacheController.</param>
        /// <remarks>
        /// If the Uri specified is different from the one that is stored in the cache path, the
        /// Load method will throw an InvalidOperationException.
        /// </remarks>
        public WinEightContext(OfflineSchema schema, string scopeName, string cachePath,
                               Uri uri)
        {
            if (schema == null)
            {
                throw new ArgumentNullException("OfflineSchema");
            }

            if (string.IsNullOrEmpty(scopeName))
            {
                throw new ArgumentNullException("scopeName");
            }

            if (string.IsNullOrEmpty(cachePath))
            {
                throw new ArgumentNullException("cachePath");
            }

            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            this.isDisposed     = false;
            this.schema         = schema;
            this.scopeUri       = uri;
            this.scopeName      = scopeName;
            this.cachePath      = cachePath;
            this.StorageHandler = new StorageHandler(schema, cachePath);
            this.saveSyncLock   = new AutoResetLock();
            this.CreateCacheController();
        }
예제 #3
0
        /// <summary>
        /// Get total number of changes of sqlite database
        /// </summary>
        /// <param name="schema">All Tables</param>
        /// <param name="lastModifiedDate">Changes since this date</param>
        internal long GetChangeCount(OfflineSchema schema, DateTime lastModifiedDate)
        {
            long totalCount = 0;

            using (SQLiteDatabaseConnection connection = SQLitePCL.pretty.SQLite3.Open(localFilePath))
            {
                try
                {
                    foreach (var ty in schema.Collections)
                    {
                        // Get mapping from my type
                        var map = manager.GetMapping(ty);

                        // Create query to select changes
                        var querySelect = SQLiteConstants.SelectChangeCount;

                        querySelect = String.Format(querySelect, map.TableName);

                        var count = connection.Query(querySelect, P(lastModifiedDate)).SelectScalarInt().First();

                        Debug.WriteLine($"Table {map.TableName} has {count} changes");

                        totalCount += count;
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    throw;
                }
            }
            Debug.WriteLine($"Total change count: {totalCount}");

            return(totalCount);
        }
예제 #4
0
        /// <summary>
        /// Constructor for the SQLiteContext
        /// </summary>
        /// <param name="schema">The OfflineSchema that specifies the set of the collections.</param>
        /// <param name="scopeName">The scope name used to identify the scope on the service.</param>
        /// <param name="databasePath">Name of the database used to store entities.</param>
        /// <param name="uri">Uri of the scope.  Used to intialize the CacheController.</param>
        /// <param name="cookieContainer"></param>
        /// <param name="uploadBatchSize">If set, determines that the upload should be batched and the maximum number of rows to send in one batch (row count - not KB!)</param>
        /// <remarks>
        /// If the Uri specified is different from the one that is stored in the cache path, the
        /// Load method will throw an InvalidOperationException.
        /// 1/11/2015 Added an optional parameter to allow setting cookies
        /// </remarks>
        public SQLiteContext(OfflineSchema schema, string scopeName, string databasePath, Uri uri, CookieContainer cookieContainer = null, int uploadBatchSize = -1)
        {
            if (schema == null)
            {
                throw new ArgumentNullException("schema");
            }

            if (string.IsNullOrEmpty(scopeName))
            {
                throw new ArgumentNullException("scopeName");
            }

            if (string.IsNullOrEmpty(databasePath))
            {
                throw new ArgumentNullException("databasePath");
            }

            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            this.isDisposed = false;
            this.schema     = schema;
            this.scopeUri   = uri;
            this.scopeName  = scopeName;
            // set cookiecontainer
            this.cookieContainer = cookieContainer;
            this.uploadBatchSize = uploadBatchSize;
            this.databaseName    = databasePath;

            bool   isPath    = databasePath.Contains('/') || databasePath.Contains('\\');
            string localPath = databaseName;

            // allow to explicitly specify full path - relevant e.g. for unittests
            if (isPath)
            {
                this.databaseName = Path.GetFileName(databaseName);
            }
            else
            {
#if (WINDOWS_PHONE || NETFX_CORE)
                localPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, databaseName);
#elif (__ANDROID__)
                localPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), databaseName);
#elif (NET)
                localPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), databaseName);
#elif (__IOS__)
                // we need to put in /Library/ on iOS5.1 to meet Apple's iCloud terms
                // (they don't want non-user-generated data in Documents)
                string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                // Documents folder
                string libraryPath = Path.Combine(documentsPath, "..", "Library");
                localPath = Path.Combine(libraryPath, databaseName);
#endif
            }

            this.Manager = new SQLiteManager(schema, localPath);
            this.CreateCacheController();
        }
예제 #5
0
        /// <summary>
        /// Ctor
        /// </summary>
        public SQLiteManager(OfflineSchema schema, String localfilePath)
        {
            this.schema = schema;

            this.localFilePath = localfilePath;

            this.sqliteHelper = new SQLiteHelper(this.localFilePath, this);
        }
예제 #6
0
        /// <summary>
        /// Constructor which initializes the data.
        /// </summary>
        /// <param name="schema">Schema for the data</param>
        public CacheData(OfflineSchema schema)
        {
            Collections   = new Dictionary <Type, OfflineCollection>();
            SyncConflicts = new List <SyncConflict>();
            SyncErrors    = new List <SyncError>();

            CreateCollections(schema);
        }
예제 #7
0
        /// <summary>
        /// Constructor which initializes the data.
        /// </summary>
        /// <param name="schema">Schema for the data</param>
        public CacheData(OfflineSchema schema)
        {
            Collections = new Dictionary<Type, OfflineCollection>();
            SyncConflicts = new List<SyncConflict>();
            SyncErrors = new List<SyncError>();

            CreateCollections(schema);
        }
예제 #8
0
        /// <summary>
        /// Get total number of changes of sqlite database
        /// </summary>
        /// <param name="schema">All Tables</param>
        /// <param name="lastModifiedDate">Changes since this date</param>
        internal long GetChangeCount(OfflineSchema schema, DateTime lastModifiedDate)
        {
            long totalCount = 0;

            using (SQLiteConnection connection = new SQLiteConnection(localFilePath))
            {
                try
                {
                    foreach (var ty in schema.Collections)
                    {
                        // Get mapping from my type
                        var map = manager.GetMapping(ty);

                        // Create query to select changes
                        var querySelect = SQLiteConstants.SelectChangeCount;

                        querySelect = String.Format(querySelect, map.TableName);


                        // Prepare command
                        using (var stmt = connection.Prepare(querySelect))
                        {
                            try
                            {
                                // Set Values
                                BindParameter(stmt, 1, lastModifiedDate);

                                stmt.Step();

                                var count = stmt.GetInteger(0);

                                Debug.WriteLine($"Table {map.TableName} has {count} changes");

                                totalCount += count;
                            }
                            finally
                            {
                                stmt.Reset();
                                stmt.ClearBindings();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    throw;
                }
            }
            Debug.WriteLine($"Total change count: {totalCount}");

            return(totalCount);
        }
예제 #9
0
        /// <summary>
        /// Creates the collections for the types in the schema
        /// </summary>
        /// <param name="schema">Schema for which to create collections</param>
        private void CreateCollections(OfflineSchema schema)
        {
            Type collectionType = typeof(OfflineCollection <,>);

            foreach (Type t in schema.Collections)
            {
                // Create the generic type for the type in the collection.
                Type objectType = typeof(Object);
                Type generic    = collectionType.MakeGenericType(objectType, t);
                OfflineCollection collection = (OfflineCollection)Activator.CreateInstance(generic);
                Collections[t] = collection;
            }
        }
예제 #10
0
        /// <summary>
        /// Constructor for the SQLiteContext
        /// </summary>
        /// <param name="schema">The OfflineSchema that specifies the set of the collections.</param>
        /// <param name="scopeName">The scope name used to identify the scope on the service.</param>
        /// <param name="datbaseName">Name of the database used to store entities.</param>
        /// <param name="uri">Uri of the scope.  Used to intialize the CacheController.</param>
        /// <remarks>
        /// If the Uri specified is different from the one that is stored in the cache path, the
        /// Load method will throw an InvalidOperationException.
        /// </remarks>
        public SQLiteContext(OfflineSchema schema, string scopeName, string datbaseName, Uri uri)
        {
            if (schema == null)
            {
                throw new ArgumentNullException("OfflineSchema");
            }

            if (string.IsNullOrEmpty(scopeName))
            {
                throw new ArgumentNullException("scopeName");
            }

            if (string.IsNullOrEmpty(datbaseName))
            {
                throw new ArgumentNullException("cachePath");
            }

            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            this.isDisposed   = false;
            this.schema       = schema;
            this.scopeUri     = uri;
            this.scopeName    = scopeName;
            this.databaseName = datbaseName;

#if (WINDOWS_PHONE || NETFX_CORE)
            var localPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, databaseName);
#elif (__ANDROID__)
            var localPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), databaseName);
#elif (__IOS__)
            // we need to put in /Library/ on iOS5.1 to meet Apple's iCloud terms
            // (they don't want non-user-generated data in Documents)
            string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
            string libraryPath   = Path.Combine(documentsPath, "..", "Library");
            var    localPath     = Path.Combine(libraryPath, databaseName);
#endif

            this.Manager = new SQLiteManager(schema, localPath);
            this.CreateCacheController();
        }
예제 #11
0
        /// <summary>
        /// Method that verifies a previously cached isolatedStorageSchema and uri (if they exist) with the current isolatedStorageSchema and uri.
        /// </summary>
        /// <param name="offlineSchema">Schema to verify</param>
        /// <param name="uri">Uri to verify</param>
        /// <param name="scope">The scope name that the client will be accessing on the service</param>
        private async Task CheckSchemaAndUriAsync(OfflineSchema offlineSchema, Uri uri, string scope, CancellationToken cancellationToken, IProgress<SyncProgressEvent> progress)
        {
            await Task.Run(() =>
            {

                DateTime durationStartDate = DateTime.Now;

                this.Configuration = this.Manager.ReadConfiguration(scope);

                if (progress != null)
                    progress.Report(new SyncProgressEvent(SyncStage.ReadingConfiguration, DateTime.Now.Subtract(durationStartDate)));

                if (this.Configuration != null)
                {

                    // Verify the scope uri.
                    if (this.Configuration.ServiceUri.AbsoluteUri != uri.AbsoluteUri)
                        throw new ArgumentException(
                            "Specified uri does not match uri previously used for the specified database");

                    // Verify the types.
                    List<Type> userTypes = offlineSchema.Collections.ToList();

                    // Sort by name (the class Type isn't sortable)
                    userTypes.Sort((x, y) => String.Compare(x.FullName, y.FullName, StringComparison.Ordinal));

                    if (userTypes.Count != this.Configuration.Types.Count)
                        throw new ArgumentException(
                            "Specified offlineSchema does not match database Offline schema previously used for cache path");

                    // Fix
                   this.Configuration.Types.Sort((x, y) => String.Compare(x, y, StringComparison.Ordinal));

                    if (userTypes.Where(
                        (t, i) => t.FullName != this.Configuration.Types[i]).Any())
                        throw new ArgumentException(
                            "Specified offlineSchema does not match database Offline schema previously used for cache path");

                }
                else
                {
                    bool existScope = this.Manager.ScopeTableExist();

                    if (!existScope)
                    {
                        durationStartDate = DateTime.Now;

                        this.Manager.CreateScopeTable();

                        if (progress != null)
                            progress.Report(new SyncProgressEvent(SyncStage.CreatingScope, DateTime.Now.Subtract(durationStartDate)));
                    }

                    // Get the list of types as strings and sort to make comparison
                    // faster when reading.
                    List<string> types = (from type in offlineSchema.Collections
                                          select type.FullName).ToList();
                    types.Sort();

                    // Create the initial configuration
                    this.Configuration = new SQLiteConfiguration
                    {
                        AnchorBlob = null,
                        LastSyncDate = new DateTime(1900, 01, 01),
                        ScopeName = scope,
                        ServiceUri = uri,
                        Types = types
                    };

                    durationStartDate = DateTime.Now;

                    this.Manager.SaveConfiguration(this.Configuration);

                    if (progress != null)
                        progress.Report(new SyncProgressEvent(SyncStage.ApplyingConfiguration, DateTime.Now.Subtract(durationStartDate)));
                }

                // Try to save tables if not exists
                if (schema == null || schema.Collections == null || schema.Collections.Count == 0)
                    return;

                durationStartDate = DateTime.Now;
                foreach (var table in schema.Collections.Where(table => table.Name != SQLiteConstants.ScopeInfo))
                {
                    this.Manager.CreateTable(table);
                }

                if (progress != null)
                    progress.Report(new SyncProgressEvent(SyncStage.CheckingTables, DateTime.Now.Subtract(durationStartDate)));


            });
        }
예제 #12
0
        /// <summary>
        /// Get all changes fro SQLite Database
        /// </summary>
        /// <param name="schema">All Tables</param>
        /// <param name="lastModifiedDate">Changes since this date</param>
        internal IEnumerable <SQLiteOfflineEntity> GetChanges(OfflineSchema schema, DateTime lastModifiedDate)
        {
            List <SQLiteOfflineEntity> lstChanges = new List <SQLiteOfflineEntity>();

            using (SQLiteConnection connection = new SQLiteConnection(localFilePath))
            {
                try
                {
                    foreach (var ty in schema.Collections)
                    {
                        // Get mapping from my type
                        var map = manager.GetMapping(ty);

                        // Create query to select changes
                        var querySelect = SQLiteConstants.SelectChanges;

                        var columnsDcl = new List <String>();
                        var columnsPK  = new List <String>();


                        // Foreach columns, create the tsql command to execute
                        foreach (var c in map.Columns)
                        {
                            if (!c.IsPK)
                            {
                                columnsDcl.Add("[s].[" + c.Name + "]");
                            }

                            // If it's the PK, add it from Tracking (because of deleted items not in real table
                            if (c.IsPK)
                            {
                                columnsDcl.Add("[t].[" + c.Name + "]");
                                columnsPK.Add("[s].[" + c.Name + "] = [t].[" + c.Name + "]");
                            }
                        }

                        var decl = string.Join(",\n", columnsDcl.ToArray());
                        var pk   = string.Join(" \nAND ", columnsPK.ToArray());
                        querySelect = String.Format(querySelect, map.TableName, pk, decl);

                        // Prepare command
                        using (var stmt = connection.Prepare(querySelect))
                        {
                            try
                            {
                                // Set Values
                                BindParameter(stmt, 1, lastModifiedDate);

                                // Get mapping form the statement
                                var cols = new TableMapping.Column[map.Columns.Length];

                                // Foreach column, get the property in my object
                                for (int i = 0; i < cols.Length; i++)
                                {
                                    var name = stmt.ColumnName(i);
                                    var c    = map.FindColumn(name);
                                    if (c != null)
                                    {
                                        cols[i] = map.FindColumn(name);
                                    }
                                }

                                // While row is available
                                //while (await stmt.StepAsync().AsTask().ConfigureAwait(false))
                                while (stmt.Step() == SQLiteResult.ROW)
                                {
                                    // Create the object
                                    SQLiteOfflineEntity obj = (SQLiteOfflineEntity)Activator.CreateInstance(map.MappedType);

                                    for (int i = 0; i < cols.Length; i++)
                                    {
                                        if (cols[i] == null)
                                        {
                                            continue;
                                        }

                                        // Read the column
                                        var val = ReadCol(stmt, i, cols[i].ColumnType);

                                        // Set the value
                                        cols[i].SetValue(obj, val);
                                    }

                                    // Read the Oem Properties
                                    var newIndex = map.Columns.Count();

                                    obj.ServiceMetadata = new OfflineEntityMetadata();

                                    obj.ServiceMetadata.IsTombstone = (Boolean)ReadCol(stmt, newIndex, typeof(Boolean));
                                    obj.ServiceMetadata.Id          = (String)ReadCol(stmt, newIndex + 1, typeof(String));
                                    obj.ServiceMetadata.ETag        = (String)ReadCol(stmt, newIndex + 2, typeof(String));
                                    String absoluteUri = (String)ReadCol(stmt, newIndex + 3, typeof(String));
                                    obj.ServiceMetadata.EditUri = String.IsNullOrEmpty(absoluteUri) ? null : new Uri(absoluteUri);

                                    lstChanges.Add(obj);
                                }
                            }
                            catch (Exception ex)
                            {
                                Debug.WriteLine(ex.Message);
                                throw;
                            }
                            finally
                            {
                                stmt.Reset();
                                stmt.ClearBindings();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    throw;
                }
            }
            return(lstChanges);
        }
예제 #13
0
        /// <summary>
        /// Constructor for the SQLiteContext
        /// </summary>
        /// <param name="schema">The OfflineSchema that specifies the set of the collections.</param>
        /// <param name="scopeName">The scope name used to identify the scope on the service.</param>
        /// <param name="datbaseName">Name of the database used to store entities.</param>
        /// <param name="uri">Uri of the scope.  Used to intialize the CacheController.</param>
        /// <remarks>
        /// If the Uri specified is different from the one that is stored in the cache path, the
        /// Load method will throw an InvalidOperationException.
        /// </remarks>
        public SQLiteContext(OfflineSchema schema, string scopeName, string datbaseName, Uri uri)
        {
            if (schema == null)
                throw new ArgumentNullException("OfflineSchema");

            if (string.IsNullOrEmpty(scopeName))
                throw new ArgumentNullException("scopeName");

            if (string.IsNullOrEmpty(datbaseName))
                throw new ArgumentNullException("cachePath");

            if (uri == null)
                throw new ArgumentNullException("uri");

            this.isDisposed = false;
            this.schema = schema;
            this.scopeUri = uri;
            this.scopeName = scopeName;
            this.databaseName = datbaseName;

#if ( WINDOWS_PHONE || NETFX_CORE) 
            var localPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, databaseName);
#elif (__ANDROID__)
            var localPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), databaseName);
#elif (__IOS__)
            // we need to put in /Library/ on iOS5.1 to meet Apple's iCloud terms
            // (they don't want non-user-generated data in Documents)
            string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
            string libraryPath = Path.Combine(documentsPath, "..", "Library");
            var localPath = Path.Combine(libraryPath, databaseName);
#endif

            this.Manager = new SQLiteManager(schema, localPath);
            this.CreateCacheController();
        }
예제 #14
0
        /// <summary>
        /// Method that verifies a previously cached isolatedStorageSchema and uri (if they exist) with the current isolatedStorageSchema and uri.
        /// </summary>
        /// <param name="path">Cache path for the c</param>
        /// <param name="isolatedStorageSchema">Schema to verify</param>
        /// <param name="uri">Uri to verify</param>
        /// <param name="scope">The scope name that the client will be accessing on the service</param>
        private async Task CheckSchemaAndUri(string path, OfflineSchema isolatedStorageSchema, Uri uri,
                                             string scope)
        {
            // Get the isolated storage file for the application.
            StorageFolder isoFolder = ApplicationData.Current.LocalFolder;

            bool cachePathExist = await isoFolder.FolderExistsAsync(path);

            if (!cachePathExist)
            {
                await isoFolder.CreateFolderAsync(path);
            }

            StorageFolder cacheFolder = await isoFolder.GetFolderAsync(path);

            // Generate the path to the scope info file.
            string infoPath = Constants.SCOPE_INFO;

            bool fileExist = await cacheFolder.FileExistsAsync(infoPath);

            // If the file exists, read it, otherwise, everything is fine.
            if (fileExist)
            {
                // Open the scope file.
                using (Stream stream = await cacheFolder.OpenStreamForReadAsync(infoPath))
                {
                    Stream readStream = stream;

                    try
                    {
                        List <string> fileTypes;
                        string        fileUri;
                        string        fileScopeName;

                        // Read the file types and uri from the file.
                        ReadSchemaAndUri(readStream, out fileUri, out fileScopeName, out fileTypes);

                        // Verify the scope uri.
                        if (fileUri != uri.AbsoluteUri)
                        {
                            throw new ArgumentException(
                                      "Specified uri does not match uri previously used for the specified cache path");
                        }

                        if (fileScopeName != scope)
                        {
                            throw new ArgumentException(
                                      "Specified scope name does not match scope name previously used for the specified cache path");
                        }

                        // Verify the types.
                        List <Type> userTypes = isolatedStorageSchema.Collections.ToList();

                        // Sort by name (the class Type isn't sortable)
                        userTypes.Sort((x, y) => String.Compare(x.FullName, y.FullName, StringComparison.Ordinal));

                        if (userTypes.Count != fileTypes.Count)
                        {
                            throw new ArgumentException(
                                      "Specified isolatedStorageSchema does not match isolatedStorageSchema previously used for cache path");
                        }

                        if (userTypes.Where((t, i) => t.FullName != fileTypes[i]).Any())
                        {
                            throw new ArgumentException(
                                      "Specified isolatedStorageSchema does not match isolatedStorageSchema previously used for cache path");
                        }
                    }
                    finally
                    {
                        readStream.Dispose();
                    }
                }
            }
            else
            {
                // If the file doesn't exist, write the new info.
                using (
                    Stream stream =
                        await cacheFolder.OpenStreamForWriteAsync(infoPath, CreationCollisionOption.ReplaceExisting))
                {
                    Stream writeStream = stream;

                    try
                    {
                        WriteSchemaFile(writeStream, uri, scope, isolatedStorageSchema);
                    }
                    finally
                    {
                        writeStream.Dispose();
                    }
                }
            }
        }
예제 #15
0
        /// <summary>
        /// Get all changes fro SQLite Database
        /// </summary>
        /// <param name="schema">All Tables</param>
        /// <param name="lastModifiedDate">Changes since this date</param>
        /// <param name="uploadBatchSize">Maximum number of rows to upload</param>
        internal IEnumerable <SQLiteOfflineEntity> GetChanges(OfflineSchema schema, DateTime lastModifiedDate, int uploadBatchSize)
        {
            List <SQLiteOfflineEntity> lstChanges = new List <SQLiteOfflineEntity>();

            using (SQLiteDatabaseConnection connection = SQLitePCL.pretty.SQLite3.Open(localFilePath))
            {
                try
                {
                    foreach (var ty in schema.Collections)
                    {
                        // Get mapping from my type
                        var map = manager.GetMapping(ty);

                        // Create query to select changes
                        var querySelect = SQLiteConstants.SelectChanges;

                        var columnsDcl = new List <String>();
                        var columnsPK  = new List <String>();


                        // Foreach columns, create the tsql command to execute
                        foreach (var c in map.Columns)
                        {
                            if (!c.IsPK)
                            {
                                columnsDcl.Add("[s].[" + c.Name + "]");
                            }

                            // If it's the PK, add it from Tracking (because of deleted items not in real table
                            if (c.IsPK)
                            {
                                columnsDcl.Add("[t].[" + c.Name + "]");
                                columnsPK.Add("[s].[" + c.Name + "] = [t].[" + c.Name + "]");
                            }
                        }

                        var decl = string.Join(",\n", columnsDcl.ToArray());
                        var pk   = string.Join(" \nAND ", columnsPK.ToArray());
                        querySelect = String.Format(querySelect, map.TableName, pk, decl);

                        // add limit if specified
                        if (uploadBatchSize > 0)
                        {
                            querySelect += $" LIMIT {uploadBatchSize}";
                        }

                        try
                        {
                            // Get mapping form the statement
                            var  cols     = new TableMapping.Column[map.Columns.Length];
                            bool firstRow = true;

                            // While row is available
                            foreach (var row in connection.Query(querySelect, P(lastModifiedDate)))
                            {
                                if (firstRow)
                                {
                                    // Foreach column, get the property in my object
                                    for (int i = 0; i < cols.Length; i++)
                                    {
                                        var name = row[i].ColumnInfo.Name;
                                        var c    = map.FindColumn(name);
                                        if (c != null)
                                        {
                                            cols[i] = map.FindColumn(name);
                                        }
                                    }

                                    firstRow = false;
                                }

                                // Create the object
                                SQLiteOfflineEntity obj = (SQLiteOfflineEntity)Activator.CreateInstance(map.MappedType);

                                for (int i = 0; i < cols.Length; i++)
                                {
                                    if (cols[i] == null)
                                    {
                                        continue;
                                    }

                                    // Read the column
                                    var val = ReadCol(row, i, cols[i].ColumnType);

                                    // Set the value
                                    cols[i].SetValue(obj, val);
                                }

                                // Read the Oem Properties
                                var newIndex = map.Columns.Count();

                                obj.ServiceMetadata = new OfflineEntityMetadata();

                                obj.ServiceMetadata.IsTombstone = (bool)ReadCol(row, newIndex, typeof(Boolean));
                                obj.ServiceMetadata.Id          = (String)ReadCol(row, newIndex + 1, typeof(String));
                                obj.ServiceMetadata.ETag        = (String)ReadCol(row, newIndex + 2, typeof(String));
                                String absoluteUri = (String)ReadCol(row, newIndex + 3, typeof(String));
                                obj.ServiceMetadata.EditUri = String.IsNullOrEmpty(absoluteUri) ? null : new Uri(absoluteUri);

                                lstChanges.Add(obj);
                            }
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.Message);
                            throw;
                        }

                        // if we are batching uploads and the upload rowcount has been reached, skip
                        if (uploadBatchSize > 0 && lstChanges.Count >= uploadBatchSize)
                        {
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    throw;
                }
            }

            // if we are batching uploads, limit the in-memory result set as well
            if (uploadBatchSize > 0)
            {
                return(lstChanges.Take(uploadBatchSize));
            }

            return(lstChanges);
        }
예제 #16
0
        /// <summary>
        /// Get all changes fro SQLite Database
        /// </summary>
        /// <param name="schema">All Tables</param>
        /// <param name="lastModifiedDate">Changes since this date</param>
        internal IEnumerable<SQLiteOfflineEntity> GetChanges(OfflineSchema schema, DateTime lastModifiedDate)
        {
            List<SQLiteOfflineEntity> lstChanges = new List<SQLiteOfflineEntity>();

            using (SQLiteConnection connection = new SQLiteConnection(localFilePath))
            {
                try
                {
                    foreach (var ty in schema.Collections)
                    {
                        // Get mapping from my type
                        var map = manager.GetMapping(ty);

                        // Create query to select changes 
                        var querySelect = SQLiteConstants.SelectChanges;

                        var columnsDcl = new List<String>();
                        string pkColumnName = string.Empty;

                        // Foreach columns, create the tsql command to execute
                        foreach (var c in map.Columns)
                        {
                            if (!c.IsPK)
                                columnsDcl.Add("[s].[" + c.Name + "]");

                            // If it's the PK, add it from Tracking (because of deleted items not in real table
                            if (c.IsPK)
                            {
                                columnsDcl.Add("[t].[" + c.Name + "]");
                                pkColumnName = c.Name;
                            }

                        }

                        var decl = string.Join(",\n", columnsDcl.ToArray());
                        querySelect = String.Format(querySelect, map.TableName, pkColumnName, decl);

                        // Prepare command
                        using (var stmt = connection.Prepare(querySelect))
                        {
                            try
                            {
                                // Set Values
                                BindParameter(stmt, 1, lastModifiedDate);

                                // Get mapping form the statement
                                var cols = new TableMapping.Column[map.Columns.Length];

                                // Foreach column, get the property in my object
                                for (int i = 0; i < cols.Length; i++)
                                {
                                    var name = stmt.ColumnName(i);
                                    var c = map.FindColumn(name);
                                    if (c != null)
                                        cols[i] = map.FindColumn(name);
                                }

                                // While row is available
                                //while (await stmt.StepAsync().AsTask().ConfigureAwait(false))
                                while (stmt.Step() == SQLiteResult.ROW)
                                {
                                    // Create the object
                                    SQLiteOfflineEntity obj = (SQLiteOfflineEntity)Activator.CreateInstance(map.MappedType);

                                    for (int i = 0; i < cols.Length; i++)
                                    {
                                        if (cols[i] == null)
                                            continue;

                                        // Read the column
                                        var val = ReadCol(stmt, i, cols[i].ColumnType);

                                        // Set the value
                                        cols[i].SetValue(obj, val);
                                    }

                                    // Read the Oem Properties
                                    var newIndex = map.Columns.Count();

                                    obj.ServiceMetadata = new OfflineEntityMetadata();

                                    obj.ServiceMetadata.IsTombstone = (Boolean)ReadCol(stmt, newIndex, typeof(Boolean));
                                    obj.ServiceMetadata.Id = (String)ReadCol(stmt, newIndex + 1, typeof(String));
                                    obj.ServiceMetadata.ETag = (String)ReadCol(stmt, newIndex + 2, typeof(String));
                                    String absoluteUri = (String)ReadCol(stmt, newIndex + 3, typeof(String));
                                    obj.ServiceMetadata.EditUri = String.IsNullOrEmpty(absoluteUri) ? null : new Uri(absoluteUri);

                                    lstChanges.Add(obj);
                                }
                            }
                            catch (Exception ex)
                            {
                                Debug.WriteLine(ex.Message);
                                throw;
                            }
                            finally
                            {
                                stmt.Reset();
                                stmt.ClearBindings();
                            }
                        }

                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    throw;
                }

            }
            return lstChanges;
        }
예제 #17
0
 /// <summary>
 /// Creates the collections for the types in the schema
 /// </summary>
 /// <param name="schema">Schema for which to create collections</param>
 private void CreateCollections(OfflineSchema schema)
 {
     Type collectionType = typeof(OfflineCollection<,>);
     foreach (Type t in schema.Collections)
     {
         // Create the generic type for the type in the collection.
         Type objectType = typeof(Object);
         Type generic = collectionType.MakeGenericType(objectType, t);
         OfflineCollection collection = (OfflineCollection)Activator.CreateInstance(generic);
         Collections[t] = collection;
     }
 }
예제 #18
0
        /// <summary>
        /// Method that verifies a previously cached isolatedStorageSchema and uri (if they exist) with the current isolatedStorageSchema and uri.
        /// </summary>
        /// <param name="offlineSchema">Schema to verify</param>
        /// <param name="uri">Uri to verify</param>
        /// <param name="scope">The scope name that the client will be accessing on the service</param>
        private async Task CheckSchemaAndUriAsync(OfflineSchema offlineSchema, Uri uri, string scope, CancellationToken cancellationToken, IProgress <SyncProgressEvent> progress)
        {
            await Task.Run(() =>
            {
                DateTime durationStartDate = DateTime.Now;

                this.Configuration = this.Manager.ReadConfiguration(scope);

                if (progress != null)
                {
                    progress.Report(new SyncProgressEvent(SyncStage.ReadingConfiguration, DateTime.Now.Subtract(durationStartDate)));
                }

                if (this.Configuration != null)
                {
                    // Verify the scope uri.
                    if (this.Configuration.ServiceUri.AbsoluteUri != uri.AbsoluteUri)
                    {
                        throw new ArgumentException(
                            "Specified uri does not match uri previously used for the specified database");
                    }

                    // Verify the types.
                    List <Type> userTypes = offlineSchema.Collections.ToList();

                    // Sort by name (the class Type isn't sortable)
                    userTypes.Sort((x, y) => String.Compare(x.FullName, y.FullName, StringComparison.Ordinal));

                    if (userTypes.Count != this.Configuration.Types.Count)
                    {
                        throw new ArgumentException(
                            "Specified offlineSchema does not match database Offline schema previously used for cache path");
                    }

                    // Fix
                    this.Configuration.Types.Sort((x, y) => String.Compare(x, y, StringComparison.Ordinal));

                    if (userTypes.Where(
                            (t, i) => t.FullName != this.Configuration.Types[i]).Any())
                    {
                        throw new ArgumentException(
                            "Specified offlineSchema does not match database Offline schema previously used for cache path");
                    }
                }
                else
                {
                    bool existScope = this.Manager.ScopeTableExist();

                    if (!existScope)
                    {
                        durationStartDate = DateTime.Now;

                        this.Manager.CreateScopeTable();

                        if (progress != null)
                        {
                            progress.Report(new SyncProgressEvent(SyncStage.CreatingScope, DateTime.Now.Subtract(durationStartDate)));
                        }
                    }

                    // Get the list of types as strings and sort to make comparison
                    // faster when reading.
                    List <string> types = (from type in offlineSchema.Collections
                                           select type.FullName).ToList();
                    types.Sort();

                    // Create the initial configuration
                    this.Configuration = new SQLiteConfiguration
                    {
                        AnchorBlob   = null,
                        LastSyncDate = new DateTime(1900, 01, 01),
                        ScopeName    = scope,
                        ServiceUri   = uri,
                        Types        = types
                    };

                    durationStartDate = DateTime.Now;

                    this.Manager.SaveConfiguration(this.Configuration);

                    if (progress != null)
                    {
                        progress.Report(new SyncProgressEvent(SyncStage.ApplyingConfiguration, DateTime.Now.Subtract(durationStartDate)));
                    }
                }

                // Try to save tables if not exists
                if (schema == null || schema.Collections == null || schema.Collections.Count == 0)
                {
                    return;
                }

                durationStartDate = DateTime.Now;
                foreach (var table in schema.Collections.Where(table => table.Name != SQLiteConstants.ScopeInfo))
                {
                    this.Manager.CreateTable(table);
                }

                if (progress != null)
                {
                    progress.Report(new SyncProgressEvent(SyncStage.CheckingTables, DateTime.Now.Subtract(durationStartDate)));
                }
            });
        }