Server to Client back to server synchronization - c#

I am developing a WPF application that interacts with localdb using the Entity Framework 6.0
So the past couple of weeks I've been attempting to get synchronization setup between the following database types:
Server: SQL Server 2008 Database
Client: localdb (SQL Express)
While I can get the structure of the database from the server transferred onto the client it does not bring over the relationships.
This somehow changes the way the ADO.NET Entity Data Model is generated.
When the Entity Data Model is generated for the server structure it generates the many-to-one-to-many relationships as a collection (many-to-many), but on the localdb that is generated (with no relationships generated) it keeps the table structure. This causes issues with my application. I can't fully migrate to an offline application if the synchronization doesn't work properly.
Server to Client (Initialization of client):
using System;
using System.IO;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServer;
namespace DatabaseSetup
{
class Program
{
static void Main()
{
try
{
CreateLocalDatabase();
ProvisionServer();
ProvisionClient();
Sync();
}
catch (Exception ex)
{
Console.WriteLine($"Exception thrown: {ex.Source}");
Console.WriteLine($"Exception thrown: {ex.Data}");
Console.WriteLine($"Exception thrown: {ex.Message}");
Console.ReadLine();
}
}
public static void CreateLocalDatabase()
{
var conn = new SqlConnection(LocalDb);
var command = new SqlCommand(#"CREATE DATABASE ********", conn);
try
{
conn.Open();
Console.WriteLine(command.ExecuteNonQuery() <= 0
? #"Creating '********' Database on '(localdb)\v11.0'"
: #"Database '********' already exists. Attempting to synchronize.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if(conn.State == ConnectionState.Open)
conn.Close();
}
}
public static void ProvisionServer()
{
Console.WriteLine(#"Attempting to provision server for synchronization...");
// connect to server database
var serverConnection = new SqlConnection(Server);
// define a new scope named ProductsScope
var scopeDescription = DatabaseScope(serverConnection);
// create a server scope provisioning object based on the ProductScope
var serverProvision = new SqlSyncScopeProvisioning(serverConnection, scopeDescription);
if(!serverProvision.ScopeExists("DatabaseScope"))
{ // skipping the creation of table since table already exists on server
serverProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);
// start the provisioning process
serverProvision.Apply();
Console.WriteLine(#"Provisioning complete.");
}
else
{
Console.WriteLine(#"Server already provisioned.");
}
}
public static void ProvisionClient()
{
Console.WriteLine(#"Attempting to provision client for synchronization...");
// create a connection to the SyncExpressDB database
var clientConnection = new SqlConnection(Client);
// create a connection to the SyncDB server database
var serverConnection = new SqlConnection(Server);
// get the description of ProductsScope from the SyncDB server database
var scopeDesc = DatabaseScope(serverConnection);
// create server provisioning object based on the ProductsScope
var clientProvision = new SqlSyncScopeProvisioning(clientConnection, scopeDesc);
if (!clientProvision.ScopeExists("DatabaseScope"))
{
// starts the provisioning process
clientProvision.Apply();
Console.WriteLine(#"Provisioning complete.");
}
else
{
Console.WriteLine(#"Client already provisioned.");
}
}
public static void Sync()
{
//Define conections
Console.WriteLine(#"Attempting to synchronize.");
var serverConnection = new SqlConnection(Server);
var clientConnection = new SqlConnection(Client);
//Create Sync Orchestrator
var syncOrchestrator = new SyncOrchestrator
{
Direction = SyncDirectionOrder.DownloadAndUpload,
LocalProvider = new SqlSyncProvider("DatabaseScope", clientConnection),
RemoteProvider = new SqlSyncProvider("DatabaseScope", serverConnection)
};
((SqlSyncProvider)syncOrchestrator.LocalProvider).ApplyChangeFailed += Program_ApplyChangeFailed;
var syncStats = syncOrchestrator.Synchronize();
Console.WriteLine("\n\nSynchronization complete:");
Console.WriteLine($"Start Time: {syncStats.SyncStartTime}");
Console.WriteLine($"Uploaded: {syncStats.UploadChangesTotal}");
Console.WriteLine($"Downloaded: {syncStats.DownloadChangesTotal}");
Console.WriteLine($"Time Elapsed: {syncStats.SyncEndTime}");
Console.Read();
}
private static DbSyncScopeDescription DatabaseScope(SqlConnection connection)
{
//Define scope
var scopeTables = new Collection<DbSyncTableDescription>();
foreach (var table in TableList)
{
scopeTables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable(table, connection));
}
var databaseScope = new DbSyncScopeDescription("DatabaseScope");
foreach(var table in scopeTables)
{
databaseScope.Tables.Add(table);
}
return databaseScope;
}
static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e)
{
// display conflict type
Console.WriteLine(e.Conflict.Type);
// display error message
Console.WriteLine(e.Error);
}
}
}
and this is the action I setup to happen when the user clicks a button on the application whenever they want to sync:
Synchronization between client and server:
using System;
using System.Collections.ObjectModel;
using System.Data.SqlClient;
using System.Windows;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServer;
using Outreach.Resources;
namespace WpfApplication
{
class DatabaseSynchronization
{
public static void Sync()
{
//Define conections
Console.WriteLine(#"Attempting to synchronize.");
var clientConnection = new SqlConnection(Constants.Client);
var serverConnection = new SqlConnection(Constants.Server);
//Create Sync Orchestrator
var syncOrchestrator = new SyncOrchestrator();
syncOrchestrator.LocalProvider = new SqlSyncProvider("DatabaseScope", clientConnection);
syncOrchestrator.RemoteProvider = new SqlSyncProvider("DatabaseScope", serverConnection);
syncOrchestrator.Direction = SyncDirectionOrder.UploadAndDownload;
((SqlSyncProvider)syncOrchestrator.LocalProvider).ApplyChangeFailed += Program_ApplyChangeFailed;
var syncStats = syncOrchestrator.Synchronize();
Console.WriteLine("\n\nSynchronization complete:");
Console.WriteLine($"Start Time: {syncStats.SyncStartTime}");
Console.WriteLine($"Uploaded: {syncStats.UploadChangesTotal}");
Console.WriteLine($"Downloaded: {syncStats.DownloadChangesTotal}");
Console.WriteLine($"Time Elapsed: {syncStats.SyncEndTime}");
Console.Read();
}
static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e)
{
// display conflict type
Console.WriteLine(e.Conflict.Type);
// display error message
Console.WriteLine(e.Error);
}
private static DbSyncScopeDescription DatabaseScope(SqlConnection connection)
{
//Define scope
var scopeTables = new Collection<DbSyncTableDescription>();
foreach(var table in Constants.MsoTableList)
{
scopeTables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable(table, connection));
}
var outreachScope = new DbSyncScopeDescription("DatabaseScope");
foreach(var table in scopeTables)
{
outreachScope.Tables.Add(table);
}
return outreachScope;
}
public static void ProvisionServer()
{
Console.WriteLine(#"Attempting to provision server for synchronization...");
// connect to server database
var serverConnection = new SqlConnection(Constants.Server);
// define a new scope named ProductsScope
var scopeDescription = DatabaseScope(serverConnection);
// create a server scope provisioning object based on the ProductScope
var serverProvision = new SqlSyncScopeProvisioning(serverConnection, scopeDescription);
if(!serverProvision.ScopeExists("DatabaseScope"))
{ // skipping the creation of table since table already exists on server
serverProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);
// start the provisioning process
serverProvision.Apply();
Console.WriteLine(#"Provisioning complete.");
}
else
{
Console.WriteLine(#"Server already provisioned.");
}
}
public static void ProvisionClient()
{
Console.WriteLine(#"Attempting to provision client for synchronization...");
// create a connection to the SyncExpressDB database
var clientConnection = new SqlConnection(Constants.Client);
// create a connection to the SyncDB server database
var serverConnection = new SqlConnection(Constants.Server);
// get the description of ProductsScope from the SyncDB server database
var scopeDesc = DatabaseScope(serverConnection);
// create server provisioning object based on the ProductsScope
var clientProvision = new SqlSyncScopeProvisioning(clientConnection, scopeDesc);
if(!clientProvision.ScopeExists("DatabaseScope"))
{
// starts the provisioning process
clientProvision.Apply();
Console.WriteLine(#"Provisioning complete.");
}
else
{
Console.WriteLine(#"Client already provisioned.");
}
}
}
}
Is there something I'm doing wrong?
Are there better options than Sync Framework?

Sync Framework don't do schema syncs. it provisions the tables just so you sync data. If you want to include the full server schema, you'll have to script it yourself and execute on the client. if you're only after synching the FKs as well, you can include that as part of the db sync table description when you provision.

Related

Is there a Method to read the Content of a Website?

I wanted to write a Program that reads an online Calendar, compares it with Names in a Database and uses this Data in some way. But if I use the WebClient, it reads the Source Code of the Website, not the Content. This is my Code:
using System;
using System.Diagnostics;
using System.Net;
using MySql.Data.MySqlClient;
namespace CalendarCrawler
{
class Program
{
static void KillTask(string Task)
{
Process[] Process = new Process[] { };
Process = Process.GetProcessesByName(Task);
foreach (Process Instance in Process)
{
Instance.Kill();
}
}
static String ReadContent(String Website)
{
WebClient web = new WebClient();
System.IO.Stream stream = web.OpenRead(Website);
using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
{
String text = reader.ReadToEnd();
return text;
}
}
static void Main(string[] args)
{
Console.WriteLine("Getting Connection ...");
var datasource = "localhost";//your server
var database = "database"; //your database name
var username = "username"; //username of server to connect
var password = "password"; //password
//your connection string
string connStr = $"Server={datasource};Database={database};Uid={username};Pwd={password}";
//create instanace of database connection
using (var conn = new MySqlConnection(connStr))
{
try
{
Console.WriteLine("Openning Connection ...");
//open connection
conn.Open();
Console.WriteLine("Connection successful!");
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
String Websitetext = ReadContent("http://www.esel.at/termine");
var stm = $"INSERT INTO content(Content) VALUES (#1);";
var cmd = new MySqlCommand(stm, conn);
cmd.Parameters.AddWithValue("#1", Websitetext);
cmd.ExecuteNonQuery();
Console.WriteLine(Websitetext);
KillTask("CalendarCrawler");
}
}
}
}
The Killtask Method is only to Clear it from the Background Processes, so there are no Problems with building a new Version.
I hope someone can help me.
There are effectively 2 different users of the web. People and computers. People like shiney things like buttons and tables (UI), computers prefer things like XML or JSON (API).
Your calendar web site has a UI, this is what you are currently seeing (both in a browser and when you 'download the code'). It probably has an API too and that is what you should be using in your program.
I've just had a quick look at esel.at and it doesn't appear to have a (public) API (but maybe that is because Google can't translate the page properly).

Database is locked: SQLite exception thrown on my android and ios xamarin app

The database was very stable until I did all the package and bundle signing and deploy the app to the app center for distributed testing, then I started getting all kind of errors from the SQLite database. I did some research and got to know that the database is not threading safe, but how can I make it thread safe, all my database connections and queries are through async.
This is my database connection
private static object collisionLock = new object();
//local SQLite database.
public SQLiteAsyncConnection Db;
private Uri url;
private MemberDataStore membersStore;
private AdvantageMemberDataStore advMembersStore;
private EventDataStore eventDataStore;
public ConnectionsUtil()
{
Db = DependencyService.Get<IDatabaseConnection>().DbConnection();
}
This is the code that does inserts and updates, I have three methods like the one below that access the database and many queries that uses the Db within the app
public async Task GetAdvantageMembersAsync(AdvantageMemberDataStore store)
{
await Db.QueryAsync<AdvantageMember>("DROP TABLE IF EXISTS AdvantageMember").ContinueWith(t =>
{
Db.CreateTableAsync<AdvantageMember>();
Console.WriteLine("AdvantageMember table created!");
});
url = new Uri("http://54.39.180.209/benefits");
Console.WriteLine("Gideon: Attempting to get advantage members.");
try
{
string json;
//Gideon: get json string containing advantage members.
using (WebClient sr = new WebClient())
{
Console.WriteLine("Gideon: Retrieving advantage Members....");
sr.DownloadStringCompleted += async (s, e) =>
{
Console.WriteLine("Gideon: Advantage Members Downloaded. Processing advantage Members....");
json = e.Result;
lock (collisionLock)
{
Db.InsertAllAsync(JsonConvert.DeserializeObject<IEnumerable<AdvantageMember>>(json)).ContinueWith(async t =>
{
await store.UpdateItemsAsync(Db.QueryAsync<AdvantageMember>("SELECT * FROM AdvantageMember GROUP BY Title").Result);
});
}
Console.WriteLine("Processing Members benefits for android");
advMembersStore = store;
};
await Task.Run(() => sr.DownloadStringAsync(url));
}
}
catch (Exception e)
{
Console.WriteLine("Gideon: An error occured while trying to dbect. ERROR: " + e);
}
}
**This is the code that specify the folder for the sqlite connection**
public class DatabaseConnection_iOS: Services.IDatabaseConnection
{
public SQLiteAsyncConnection DbConnection()
{
var dbName = "MyDb.db3";
string personalFolder =
Environment.
GetFolderPath(Environment.SpecialFolder.Personal);
string libraryFolder =
Path.Combine(personalFolder, "..", "Library");
var path = Path.Combine(libraryFolder, dbName);
return new SQLiteAsyncConnection(path);
}
}
In my case, I was put SaveContext inside foreach
var transaction = _context.Database.BeginTransaction();
try
{
foreach (var item in obj)
{
_context.EntrevistaNacionals.Add(item);
await _context.SaveChangesAsync(); //--wrong
}
transaction.Commit();
return true;
}

Items are not added in dynamodb using aws toolkit for .net

I have created a profile in aws toolkit for .net and I am creating a lambda function by uploading the aws function.
The function when tested in aws console does not throw any error. However , the data is not added in dynamodb table.
Here is the code snippet :
public void FunctionHandler(DynamoDBEvent dynamoEvent, ILambdaContext context1)
{
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
var context = new DynamoDBContext(client);
Table awsnet = Table.LoadTable(client, "bookmaster");
context1.Logger.LogLine("In method : Function Handler : start");
CreateBookItem(bookmaster);
}
private static void CreateBookItem(Table tblName)
{
var client = new AmazonDynamoDBClient();
Console.WriteLine("\n*** Executing CreateBookItem() ***");
string sampleBookId = "3";
var doc = new Document();
doc["strid"] = sampleBookId;
tblName.PutItemAsync(doc);
}
Also , all the examples are using "tblName.PutItem(doc)" , but it is unavailable. SO I have used "tblName.PutItemAsync(doc)". The log lines are printed in the aws console , but data is not added in table.
I was able to solve the above issue with the following code :
public void FunctionHandler(DynamoDBEvent dynamoEvent, ILambdaContext context1)
{
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
var context = new DynamoDBContext(client);
Table bookmaster = Table.LoadTable(client, "bookmaster");
context1.Logger.LogLine("In method : Function Handler : start");
string result = PutDataAsync(bookmaster, context1).Result;
context1.Logger.LogLine("Result = " + result);
}
private static async Task<string> PutDataAsync(Table table , ILambdaContext context1)
{
try
{
string sampleBookId = "3";
var doc = new Document();
doc["strid"] = sampleBookId;
Document x = await table.PutItemAsync(doc);
context1.Logger.LogLine("In method after put item async");
return "success";
}
catch(Exception ex)
{
context1.Logger.LogLine("In method after put item async catch block");
return "failed";
}
}

table mapping unable error in SQlite connection in windows application

When i connect my application with SQLite database. The following exception throw.
An exception of type 'SQLite.SQLiteException' occurred in
Scrap_Book.Windows.exe but was not handled in user code
Additional information: no such table: CONTENT
I created the table with the help of SqLite manager an adon of mozila firefox.
Please help me how I can solve this problem.
var dbpath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "MYSCRAPBOOK.sqlite");
using (var db = new SQLite.SQLiteConnection(dbpath))
db.Insert(new CONTENT()
{
STICKERS=snodesticker
});
db.Commit();
db.Dispose();
db.Close();
var line = new MessageDialog("Records Inserted");
await line.ShowAsync();
According to message from your exception you haven't created CONTENT table.
So, I guess you need to create table before inserting data.
public static string DB_NAME = "ContactsManager.sqlite";
public static string DB_PATH = Path.Combine(Path.Combine(ApplicationData.Current.LocalFolder.Path, DB_NAME));//DataBase Name
private async Task<bool> CheckFileExistsAsync(string fileName)
{
try
{
var store = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
return true;
}
catch
{
}
return false;
}
if (!(await CheckFileExistsAsync(DB_NAME)))
{
using (var db = new SQLiteConnection(DB_PATH))
{
db.CreateTable<CONTENT>();
}
}

create new record in db

Using asp.net mvc project I want to create a new ClientAccountAccess record.
private static void SetAccessCode(string guidCode)
{
using (EPOSEntities db = new EPOSEntities())
{
ClientAccountAccess client = new ClientAccountAccess();
client.GUID = guidCode;
db.SaveChanges();
}
}
//have also tried
//ClientAccountAccess client = new ClientAccountAccess()
//{
// GUID = guidCode
//};
//db.SaveChanges();
Wont seem to save a new record?
You need to add this record to context:
private static void SetAccessCode(string guidCode)
{
using (EPOSEntities db = new EPOSEntities())
{
ClientAccountAccess client = new ClientAccountAccess();
client.GUID = guidCode;
// ClientAccountAccess is name of your DBSet in context. It might be different
db.ClientAccountAccess.AddObject(client);
db.SaveChanges();
}
}
Here is more samples of basic CRUD operations - http://www.dotnetcurry.com/showarticle.aspx?ID=619

Categories