Last active
December 31, 2015 23:59
-
-
Save yallie/8063411 to your computer and use it in GitHub Desktop.
Zyan+EF6+SqlCe4 concurrent data access example. Application source code (make sure you create a configuration file).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Zyan + EF6 concurrent data access example. | |
// Written by Alexey Yakovlev <yallie@yandex.ru> | |
// Compile this code using: | |
// | |
// csc eftest3.cs /r:Zyan.Communication.dll /r:EntityFramework.dll /r:EntityFramework.SqlServerCompact.dll /r:System.ComponentModel.DataAnnotations.dll | |
// | |
// First run — starts server. | |
// Second run — starts client. | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel.DataAnnotations; | |
using System.Data.Entity; | |
using System.Linq; | |
using System.Net.Sockets; | |
using System.Threading; | |
using System.Transactions; | |
using Zyan.Communication; | |
using Zyan.Communication.Protocols.Tcp; | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
try | |
{ | |
RunServer(); | |
} | |
catch (SocketException) // server already running | |
{ | |
RunClient(); | |
} | |
} | |
// shared code --------------- | |
interface IUserService | |
{ | |
void AddUser(string name); | |
IEnumerable<string> GetUsers(); | |
} | |
// server code --------------- | |
class User | |
{ | |
[Key] | |
public int ID { get; set; } | |
public string Name { get; set; } | |
} | |
class UserContext : DbContext | |
{ | |
public DbSet<User> Users { get; set; } | |
} | |
class UserService : IUserService | |
{ | |
public void AddUser(string name) | |
{ | |
Console.WriteLine("Adding user: {0}", name); | |
var user = new User { Name = name }; | |
userContext.Users.Add(user); | |
userContext.SaveChanges(); | |
if (name == "Dave") | |
{ | |
// even if SaveChanges is already called, the transaction will be rolled back due to exception | |
throw new InvalidOperationException("I'm sorry Dave, I'm afraid I can't do that."); | |
} | |
} | |
public IEnumerable<string> GetUsers() | |
{ | |
var query = | |
from u in userContext.Users | |
orderby u.Name | |
select u.Name; | |
return query.ToArray(); | |
} | |
} | |
static void RunServer() | |
{ | |
using (var zyanHost = new ZyanComponentHost("EntityFrameworkServer", 8195)) | |
{ | |
zyanHost.RegisterComponent<IUserService, UserService>(); | |
zyanHost.BeforeInvoke += ZyanHost_BeforeInvoke; | |
zyanHost.AfterInvoke += ZyanHost_AfterInvoke; | |
zyanHost.InvokeCanceled += ZyanHost_InvokeCanceled; | |
Console.WriteLine("Trying to access the database..."); | |
using (var ctx = new UserContext()) | |
{ | |
var dummy = ctx.Users.Count(); | |
} | |
Console.WriteLine("Server is ready. Press ENTER to stop."); | |
Console.WriteLine(); | |
Console.WriteLine("Run the program again to test the concurrent database access."); | |
Console.ReadLine(); | |
} | |
} | |
[ThreadStatic] | |
static UserContext userContext; | |
[ThreadStatic] | |
static TransactionScope transactionScope; | |
static void ZyanHost_BeforeInvoke(object sender, BeforeInvokeEventArgs args) | |
{ | |
var options = new TransactionOptions | |
{ | |
IsolationLevel = IsolationLevel.ReadCommitted, | |
Timeout = TimeSpan.Zero | |
}; | |
// begin a new transaction and create a data context | |
transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, options); | |
userContext = new UserContext(); | |
} | |
static void ZyanHost_AfterInvoke(object sender, AfterInvokeEventArgs args) | |
{ | |
// remote call was successful, commit the transaction | |
if (transactionScope != null) | |
{ | |
transactionScope.Complete(); | |
transactionScope.Dispose(); | |
transactionScope = null; | |
} | |
if (userContext != null) | |
{ | |
userContext.Dispose(); | |
userContext = null; | |
} | |
} | |
static void ZyanHost_InvokeCanceled(object sender, InvokeCanceledEventArgs args) | |
{ | |
// remote call failed, rollback the transaction | |
if (transactionScope != null) | |
{ | |
transactionScope.Dispose(); | |
transactionScope = null; | |
} | |
if (userContext != null) | |
{ | |
userContext.Dispose(); | |
userContext = null; | |
} | |
} | |
// client code --------------- | |
static void RunClient() | |
{ | |
var serverUrl = "tcp://localhost:8195/EntityFrameworkServer"; | |
Console.WriteLine("Connecting to {0}...", serverUrl); | |
Console.WriteLine(); | |
using (var zyanConnection = new ZyanConnection(serverUrl)) | |
{ | |
var proxy = zyanConnection.CreateProxy<IUserService>(); | |
// inserting data concurrently | |
Console.WriteLine("Spawning several threads to create multiple users..."); | |
var userNames = new[] { "Bob", "Alice", "Dave", "Erwin", "Charles", "Roger", "Steve" }; | |
for (var i = 0; i < 20; i++) | |
{ | |
var index = i; | |
ThreadPool.QueueUserWorkItem(x => | |
{ | |
var name = userNames[index % userNames.Length]; | |
Console.WriteLine("Adding {0}...", name); | |
try | |
{ | |
proxy.AddUser(name); | |
} | |
catch (Exception ex) | |
{ | |
Console.WriteLine("Error inserting {0}. {1}", name, ex.InnerException.Message); | |
} | |
}); | |
} | |
Console.WriteLine("Wait a minute, then press ENTER to continue."); | |
Console.ReadLine(); | |
// selecting data | |
var users = proxy.GetUsers(); | |
Console.WriteLine("{0} users in the database: {1}", users.Count(), string.Join(", ", users)); | |
Console.WriteLine(); | |
Console.WriteLine("Notice there are no Daves."); | |
Console.ReadLine(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment