I have been working on a device which is sending some data to an Azure IoT hub
The device is doing this on two different locations in the code. On one side it works perfectly and I can connect to the Hub via Connection String and transport type MQTT_WebSocket_Only.
public static class Mqtt2IoTNew
{
private static string _DeviceConnectionString = Properties.Settings.Default.MqttUri;
private static TransportType _TransportType = TransportType.Mqtt_WebSocket_Only;
public static void Send(object argEntry, bool argIsList)
{
var deviceClient = DeviceClient.CreateFromConnectionString(_DeviceConnectionString, _TransportType);
deviceClient.ReceiveAsync(TimeSpan.FromSeconds(2)).Wait();
var message = new Message(deviceClient, argEntry, argIsList);
message.RunAsync().GetAwaiter().GetResult();
}
}
internal class Message
{
private DeviceClient _DeviceClient;
private readonly string _Message;
public Message(DeviceClient argDeviceClient, object argEntry, bool isList)
{
_DeviceClient = argDeviceClient;
StringBuilder stb = new StringBuilder();
if (isList)
{
foreach (var entity in (List<object>) argEntry)
{
stb.Append("<entity>").Append(JsonConvert.SerializeObject(entity)).Append("</entity>\n");
}
}
else
{
stb.Append(JsonConvert.SerializeObject(argEntry));
}
_Message = stb.ToString();
}
public async Task RunAsync()
{
await SendEvent().ConfigureAwait(false);
}
private async Task SendEvent()
{
Microsoft.Azure.Devices.Client.Message eventMessage = new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(_Message));
await _DeviceClient.SendEventAsync(eventMessage).ConfigureAwait(false);
}
}
//Call of method that does not work
protected override void DoOnCompleted(IRepository argRepository)
{
if (_CurrentlySendingTreadId.HasValue)
{
if (_CurrentlySendingTreadId.Value == Thread.CurrentThread.ManagedThreadId)
{
return;
}
}
TaskFactoryProvider.GetFactory().StartNew(()=>SendBatchProtocols());
}
public bool SendBatchProtocols()
{
using (var repository = RepositoryProviderHolder.RepositoryProvider.GetRepository(Constants.CONTAINERCONTRACT_PRODUCTIONREPOSITORY))
{
IQueryable<BatchProtocol> batchProtocolQuery = repository.GetQuery<BatchProtocol>().OrderBy(bp => bp.InternalNoInteger);
batchProtocolQuery = batchProtocolQuery.Where(bp => !bp.IsArchived).Take(1);
if (!batchProtocolQuery.Any()) return false;
var batchProtocols = batchProtocolQuery.ToList();
IsBatchProtocolSend = false;
try
{
foreach (var bps in batchProtocols)
{
Mqtt2IoTNew.Send(bps,false);
}
IsBatchProtocolSend = true;
}
catch (Exception ex)
{
throw;
}
}
return IsBatchProtocolSend;
}
//Call of Method that does work
private void AddEntitiesAndSaveChanges(IEnumerable argEntities)
{
if (argEntities == null)
{
return;
}
lock (_UnderlyingRepositoryAccessLockObject)
{
#region Log2DornerIoT
if (Properties.Settings.Default.Log2DornerIoT)
{
List<object> entities = new List<object>();
int i = 0;
foreach (var entity in argEntities)
{
if (i < 100)
{
entities.Add(entity);
i++;
}
else
{
try
{
Mqtt2IoTNew.Send(entities, true);
}
catch (Exception e)
{
throw;
}
entities.Clear();
i = 0;
}
}
}
}
on the other part of the code, I am only colling the same class to use to send method in the same way but here I get an exception which says "TLS authentication error" and the inner exception "Unable to connect to the remote server", "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel".
But: I never used any kind of authorization not in the first part which works perfectly neither in the second.
I would be very happy if someone could help me. I have found nothing so fare regarding this issue.
Thanks for your time.
Michael
I found the reason why it didn't work. There was a Persmissice Certificate Policy applied that blocked the certificate at one side of the project. I disabled it and now it works perfectly fine.
Thanks for the help anyway.
Related
I need help with creating a windows service using Threading and asynchronous HttpWebRequest calls. I have created a few C# windows services before but never using threading. Also, I seem to be getting hung up with the async calls using HttpWebRequest. I have googled this as well as looking on this site. I could not find anything that helped. This is mainly because I could not seem to get what was presented in other questions to work in my specific example.
Please keep in mind that I may be overlapping things based on my lack of knowledge in this specific area as well as through trying to figure it out.
The main flow of this is to get a list of urls during onStart. Typically this list would be retrieved from a _facade.GetUrls call. Then, at each time interval call scanSites. A request is made to each url and then I save the results to the database in _facade.SaveUrlResponse.
My problems is it seems as if I am caught in an endless loop when I debug it. I am not exactly sure how/where to do this. Thanks in advance.
Here is what I have:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.ServiceProcess;
using System.Threading;
using URLValidation.BusinessManager.Facade;
using URLValidation.BusinessManager.Model;
namespace URLValidation
{
partial class URLValidation : ServiceBase
{
#region " class variables "
private System.Timers.Timer _timer;
private List<UrlModel> _url_List = null;
private Facade _facade;
private Thread _t;
private int _x;
#endregion
public URLValidation()
{
_facade = new Facade();
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try
{
_url_List = new List<UrlModel>
{
new UrlModel(address: "http://www.google.com", addressID: 1),
new UrlModel(address: "http://www.microsoft.com", addressID: 2),
new UrlModel(address: "http://www.stackoverflow.com", addressID: 3)
};
resetTimer();
GC.KeepAlive(_timer);
}
catch (Exception ex)
{
throw ex;
}
}
private void resetTimer()
{
try
{
_timer = new System.Timers.Timer();
_timer.Interval = 10000;//1800000; //30 minutes
_timer.Start();
_timer.Enabled = true;
_timer.Elapsed += scanSites;
}
catch (Exception ex)
{
throw ex;
}
}
private void scanSites(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Stop();
_x = 0;
_t = new Thread(new ThreadStart(scanSites));
_t.IsBackground = true;
_t.Start();
}
private void scanSites()
{
try
{
foreach (UrlModel url in _url_List)
{
_x += 1;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.Address);
request.Method = "HEAD";
RequestModel requestModel = new RequestModel(request, url);
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(saveUrlResponse), requestModel);
ThreadPool.RegisterWaitForSingleObject
(
result.AsyncWaitHandle,
new WaitOrTimerCallback(ScanTimeoutCallback),
requestModel,
(30 * 1000), // 30 second timeout
true
);
}
}
catch (Exception ex)
{
throw ex;
}
}
private void saveUrlResponse(IAsyncResult result)
{
//grab the custom state object
RequestModel requestModel = (RequestModel)result.AsyncState;
HttpWebRequest request = (HttpWebRequest)requestModel.Request;
//get the Response
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
// process the response...
ResponseModel responseModel = new ResponseModel(request, response, requestModel.UrlModel.AddressID);
_facade.SaveUrlResponse(responseModel);
}
private void ScanTimeoutCallback(object requestModel, bool timedOut)
{
if (timedOut)
{
RequestModel reqState = (RequestModel)requestModel;
if (reqState != null)
reqState.Request.Abort();
}
if (_x == _url_List.Count)
{
resetTimer();
}
}
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
}
}
}
Okay, I think I am getting somewhere. I have moved my code to a console app. I am able to get the results saved to the database by using either GetResponse (sync) and BeginGetResponse (async). From what I can tell I believe this is a good solution. Can somebody verify this and let me know if you foresee any problems once this is moved to a Windows Service. Here is the new code
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
namespace ConsoleApplication2
{
static class Program
{
private static List<UrlModel> _url_List = null;
private static Object _acctLock = new object();
private static Facade _facade = new Facade();
static void Main(string[] args)
{
_url_List = new List<UrlModel>
{
new UrlModel(address: "http://www.microsoft.com", addressID: 1),
new UrlModel(address: "http://www.google.com", addressID: 2),
new UrlModel(address: "http://www.stackoverflow.com", addressID: 3)
};
lockThreadAndGetUrlStatus(_url_List);
Console.ReadLine();
}
static void lockThreadAndGetUrlStatus(List<UrlModel> _url_List)
{
Thread[] threads;
try
{
threads = new Thread[_url_List.Count];
Thread.CurrentThread.Name = "main";
int i = 0;
foreach (UrlModel url in _url_List)
{
//Thread t = new Thread(() => scanSites(url));
Thread t = new Thread(() => scanSitesWithAsync(url));
t.Name = i.ToString();
threads[i] = t;
i += 1;
}
for (i = 0; i < _url_List.Count; i++)
{
Console.WriteLine("Thread {0} Alive : {1}", threads[i].Name, threads[i].IsAlive);
threads[i].Start();
Console.WriteLine("Thread {0} Alive : {1}", threads[i].Name, threads[i].IsAlive);
}
Console.WriteLine("Current Priority : {0}", Thread.CurrentThread.Priority);
Console.WriteLine("Thread {0} Ending", Thread.CurrentThread.Name);
}
catch (Exception ex)
{
throw ex;
}
}
static void scanSitesWithAsync(UrlModel url)
{
try
{
lock (_acctLock)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.Address);
request.Method = "HEAD";
RequestModel requestModel = new RequestModel(request, url);
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(saveUrlResponseWithAsync), requestModel);
ThreadPool.RegisterWaitForSingleObject
(
result.AsyncWaitHandle,
new WaitOrTimerCallback(scanTimeoutCallback),
requestModel, 30000, true
);
}
}
catch (Exception ex)
{
throw ex;
}
}
static void saveUrlResponseWithAsync(IAsyncResult result)
{
try
{
RequestModel requestModel = (RequestModel)result.AsyncState;
HttpWebRequest request = (HttpWebRequest)requestModel.Request;
//get the Response
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
// process the response...
ResponseModel responseModel = new ResponseModel(requestModel.Request, response, requestModel.UrlModel.AddressID);
_facade.SaveUrlResponse(responseModel);
Console.WriteLine(response.StatusCode);
}
catch (Exception ex)
{
throw ex;
}
}
static void scanTimeoutCallback(object requestModel, bool timedOut)
{
try
{
if (timedOut)
{
RequestModel reqState = (RequestModel)requestModel;
if (reqState != null)
reqState.Request.Abort();
}
}
catch (Exception ex)
{
throw ex;
}
}
}
}
looks like you are trying to issue 3 async requests at the same time. By default, the HTTP/1.1 protocol only specifies 2 connections
I try to run Load Test with PhantomJS on Azure by guide from Microsoft.
https://blogs.msdn.microsoft.com/visualstudioalm/2014/11/16/using-selenium-with-cloud-based-load-testing/
Test opens page, makes login and stays on the site.
With ~20 users it works well. When I try with 30 and more users, it throws error:
Initialization method SeleniumWebLoadPoc.SelenumLoadWeb.SetupTest threw exception. OpenQA.Selenium.WebDriverException: OpenQA.Selenium.WebDriverException: Cannot start the driver service on http://localhost:1341/.
Code of my test:
[TestClass]
public class SelenumLoadWeb
{
private IWebDriver _driver;
public TestContext TestContext { get; set; }
private StringBuilder verificationErrors;
private string baseURL;
private bool acceptNextAlert = true;
[TestInitialize]
public void SetupTest()
{
_driver = new PhantomJSDriver();
baseURL = "https://***.com/";
verificationErrors = new StringBuilder();
}
[TestMethod]
[DeploymentItem("SeleniumWebLoadPoc\\OnlyLogin.csv", "SeleniumWebLoadPoc")]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\SeleniumWebLoadPoc\\OnlyLogin.csv", "OnlyLogin#csv", DataAccessMethod.Sequential)]
public void TheSelenumLoadWebTest()
{
var UserName = TestContext.DataRow["UserName"].ToString();
var PassWord = TestContext.DataRow["Pass"].ToString();
// OPEN LOGIN PAGE
TestContext.BeginTimer("SelenumLoadWeb_Navigate");
_driver.Navigate().GoToUrl(baseURL);
TestContext.EndTimer("SelenumLoadWeb_Navigate");
// LOGIN WITH USERNAME AND PASSWORD
_driver.FindElement(By.Id("txtUName")).Click();
_driver.FindElement(By.Id("txtUName")).Clear();
_driver.FindElement(By.Id("txtUName")).SendKeys(UserName);
_driver.FindElement(By.Id("txtPass")).Click();
_driver.FindElement(By.Id("txtPass")).Clear();
_driver.FindElement(By.Id("txtPass")).SendKeys(PassWord);
TestContext.BeginTimer("SelenumLoadWeb_Login");
_driver.FindElement(By.Id("btnOkLogin")).Click();
TestContext.EndTimer("SelenumLoadWeb_Login");
Thread.Sleep(60000);
}
[TestCleanup]
public void TeardownTest()
{
try
{
_driver.Quit();
}
catch (Exception)
{
// Ignore errors if unable to close the browser
}
Assert.AreEqual("", verificationErrors.ToString());
}
private bool IsElementPresent(By by)
{
try
{
_driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
private bool IsAlertPresent()
{
try
{
_driver.SwitchTo().Alert();
return true;
}
catch (NoAlertPresentException)
{
return false;
}
}
private string CloseAlertAndGetItsText()
{
try
{
IAlert alert = _driver.SwitchTo().Alert();
string alertText = alert.Text;
if (acceptNextAlert)
{
alert.Accept();
}
else
{
alert.Dismiss();
}
return alertText;
}
finally
{
acceptNextAlert = true;
}
}
}
Some one tried to run Load tests with PhantomJS with many users?
My unit of work class is mentioned below and I am using Ninject and I have tried injecting IUnitOfWork per request per thread scope, transient etc. but I am still getting error which is:
"Message":"An error has occurred.","ExceptionMessage":"The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe.","ExceptionType":"System.InvalidOperationException
I get this error when i make two web API (get) calls at the same time using angularJS and it shows error at the point _context.Set<TEntity>().FirstOrDefault(match);
public class UnitOfWork : IUnitOfWork, IDisposable
{
private My_PromotoolEntities _uowDbContext = new My_PromotoolEntities();
private Dictionary<string, object> _repositories;
// Do it like this if no specific class file
private GenericRepository<MysPerson> _personRepository;
//private GenericRepository<MysDataSource> dataSourcesRepository;
//private GenericRepository<MysCountry> countryMasterRepository;
// Or like this if with specific class file.
private DataSourceRepository _dataSourcesRepository;
private CustomerRepository _customerRepository;
private DeviceRepository _deviceRepository;
private DeviceRegistrationRepository _deviceRegistrationRepository;
private EmailQueueRepository _emailQueueRepository;
public void SetContext(My_PromotoolEntities context)
{
_uowDbContext = context;
}
public void CacheThis(object cacheThis, string keyName, TimeSpan howLong)
{
Cacheing.StaticData.CacheStaticData(cacheThis, keyName, howLong);
}
public object GetFromCache(string keyName)
{
return Cacheing.StaticData.GetFromCache(keyName);
}
public GenericRepository<T> GenericRepository<T>() where T : BaseEntity
{
if (_repositories == null)
{
_repositories = new Dictionary<string, object>();
}
var type = typeof(T).Name;
if (!_repositories.ContainsKey(type))
{
var repositoryType = typeof(GenericRepository<>);
var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), _uowDbContext);
_repositories.Add(type, repositoryInstance);
}
return (GenericRepository<T>)_repositories[type];
}
public GenericRepository<MysPerson> PersonRepository
{
get
{
if (this._personRepository == null)
{
this._personRepository = new GenericRepository<MysPerson>(_uowDbContext);
}
return _personRepository;
}
}
public DataSourceRepository DataSourcesRepository
{
get
{
if (this._dataSourcesRepository == null)
{
this._dataSourcesRepository = new DataSourceRepository(_uowDbContext);
}
return _dataSourcesRepository;
}
}
public CustomerRepository CustomerRepository
{
get
{
if (this._customerRepository == null)
{
this._customerRepository = new CustomerRepository(_uowDbContext);
}
return _customerRepository;
}
}
public DeviceRepository DeviceRepository
{
get
{
if (this._deviceRepository == null)
{
this._deviceRepository = new DeviceRepository(_uowDbContext);
}
return _deviceRepository;
}
}
public DeviceRegistrationRepository DeviceRegistrationRepository
{
get
{
if (this._deviceRegistrationRepository == null)
{
this._deviceRegistrationRepository = new DeviceRegistrationRepository(_uowDbContext);
}
return _deviceRegistrationRepository;
}
}
public EmailQueueRepository emailQueueRepository
{
get
{
if (this._emailQueueRepository == null)
{
this._emailQueueRepository = new EmailQueueRepository(_uowDbContext);
}
return _emailQueueRepository;
}
}
/// <summary>
/// Commits all changes to the db. Throws exception if fails. Call should be in a try..catch.
/// </summary>
public void Save()
{
try
{
_uowDbContext.SaveChanges();
}
catch (DbEntityValidationException dbevex)
{
// Entity Framework specific errors:
StringBuilder sb = new StringBuilder();
var eve = GetValidationErrors();
if (eve.Count() > 0)
{
eve.ForEach(error => sb.AppendLine(error));
}
ClearContext();
// Throw a new exception with original as inner.
var ex = new Exception(sb.ToString(), dbevex);
ex.Source = "DbEntityValidationException";
throw ex;
}
catch (Exception)
{
ClearContext();
throw;
}
}
private void ClearContext()
{
DetachAll();
}
private void DetachAll()
{
foreach (DbEntityEntry dbEntityEntry in _uowDbContext.ChangeTracker.Entries())
{
if (dbEntityEntry.Entity != null)
{
dbEntityEntry.State = EntityState.Detached;
}
}
}
/// <summary>
/// Checks for EF DbEntityValidationException(s).
/// </summary>
/// <returns>Returns a List of string containing the EF DbEntityValidationException(s).</returns>
public List<string> GetValidationErrors()
{
if (_uowDbContext.GetValidationErrors().Count() != 0)
{
return _uowDbContext.GetValidationErrors().Select(e => string.Join(Environment.NewLine, e.ValidationErrors.Select(v => string.Format("{0} - {1}", v.PropertyName, v.ErrorMessage)))).ToList();
}
return null;
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_uowDbContext.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
You should never use a context in 2 places at the same time, that's exactly why you are getting this error. From the MSDN documentation:
Thread Safety: Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
It is a little hard to make suggestions without a repro but there is a brute force approach that should resolve the issue. If you have an interception point before/during DI setup then you can cause all the context initialization etc to happen by creating an instance of your context and calling ctx.Database.Initialize(force: false); Passing 'force: false' will ensure that the initialization still only happens once per AppDomain
Hi I am working on web api as back-end service where I am using Azure notification hub. I need to notify logged in user according to conditional business logic, in short target specific user. I extract code from this article. Everything works fine but tags is not creating nor updating. I need help. Here is my code snippet
// It returns registrationId
public async Task<OperationResult<string>> GetRegistrationIdAsync(string handle)
{
........
}
// actual device registration and tag update
public async Task<OperationResult<RegistrationOutput>> RegisterDeviceAsync(string userName, string registrationId, Platforms platform, string handle)
{
...........
registration.Tags.Add(string.Format("username : {0}", userName)); // THIS TAG IS NOT CREATING
await _hub.CreateOrUpdateRegistrationAsync(registration);
...........
}
// Send notification - target specific user
public async Task<OperationResult<bool>> Send(Platforms platform, string userName, INotificationMessage message)
{
...........
}
Just after submitting this question I tried updating tags from VS notification explorer. There I found that tags does not allowed blank spaces and my tags format in api call has spaces. These spaces are the main culprit. API call silently ignore these invalid tag formats
Here is complete working implementation. Modify according to your need
public class MyAzureNotificationHubManager
{
private Microsoft.ServiceBus.Notifications.NotificationHubClient _hub;
public MyAzureNotificationHubManager()
{
_hub = MyAzureNotificationClient.Instance.Hub;
}
private const string TAGFORMAT = "username:{0}";
public async Task<string> GetRegistrationIdAsync(string handle)
{
if (string.IsNullOrEmpty(handle))
throw new ArgumentNullException("handle could not be empty or null");
// This is requied - to make uniform handle format, otherwise could have some issue.
handle = handle.ToUpper();
string newRegistrationId = null;
// make sure there are no existing registrations for this push handle (used for iOS and Android)
var registrations = await _hub.GetRegistrationsByChannelAsync(handle, 100);
foreach (RegistrationDescription registration in registrations)
{
if (newRegistrationId == null)
{
newRegistrationId = registration.RegistrationId;
}
else
{
await _hub.DeleteRegistrationAsync(registration);
}
}
if (newRegistrationId == null)
newRegistrationId = await _hub.CreateRegistrationIdAsync();
return newRegistrationId;
}
public async Task UnRegisterDeviceAsync(string handle)
{
if (string.IsNullOrEmpty(handle))
throw new ArgumentNullException("handle could not be empty or null");
// This is requied - to make uniform handle format, otherwise could have some issue.
handle = handle.ToUpper();
// remove all registration by that handle
var registrations = await _hub.GetRegistrationsByChannelAsync(handle, 100);
foreach (RegistrationDescription registration in registrations)
{
await _hub.DeleteRegistrationAsync(registration);
}
}
public async Task RegisterDeviceAsync(string userName, string registrationId, Platforms platform, string handle)
{
if (string.IsNullOrEmpty(handle))
throw new ArgumentNullException("handle could not be empty or null");
// This is requied - to make uniform handle format, otherwise could have some issue.
handle = handle.ToUpper();
RegistrationDescription registration = null;
switch (platform)
{
case Platforms.MPNS:
registration = new MpnsRegistrationDescription(handle);
break;
case Platforms.WNS:
registration = new WindowsRegistrationDescription(handle);
break;
case Platforms.APNS:
registration = new AppleRegistrationDescription(handle);
break;
case Platforms.GCM:
registration = new GcmRegistrationDescription(handle);
break;
default:
throw new ArgumentException("Invalid device platform. It should be one of 'mpns', 'wns', 'apns' or 'gcm'");
}
registration.RegistrationId = registrationId;
// add check if user is allowed to add these tags
registration.Tags = new HashSet<string>();
registration.Tags.Add(string.Format(TAGFORMAT, userName));
// collect final registration
var result = await _hub.CreateOrUpdateRegistrationAsync(registration);
var output = new RegistrationOutput()
{
Platform = platform,
Handle = handle,
ExpirationTime = result.ExpirationTime,
RegistrationId = result.RegistrationId
};
if (result.Tags != null)
{
output.Tags = result.Tags.ToList();
}
}
public async Task<bool> Send(Platforms platform, string receiverUserName, INotificationMessage message)
{
string[] tags = new[] { string.Format(TAGFORMAT, receiverUserName) };
NotificationOutcome outcome = null;
switch (platform)
{
// Windows 8.1 / Windows Phone 8.1
case Platforms.WNS:
outcome = await _hub.SendWindowsNativeNotificationAsync(message.GetWindowsMessage(), tags);
break;
case Platforms.APNS:
outcome = await _hub.SendAppleNativeNotificationAsync(message.GetAppleMessage(), tags);
break;
case Platforms.GCM:
outcome = await _hub.SendGcmNativeNotificationAsync(message.GetAndroidMessage(), tags);
break;
}
if (outcome != null)
{
if (!((outcome.State == NotificationOutcomeState.Abandoned) || (outcome.State == NotificationOutcomeState.Unknown)))
{
return true;
}
}
return false;
}
}
public class MyAzureNotificationClient
{
// Lock synchronization object
private static object syncLock = new object();
private static MyAzureNotificationClient _instance { get; set; }
// Singleton inistance
public static MyAzureNotificationClient Instance
{
get
{
if (_instance == null)
{
lock (syncLock)
{
if (_instance == null)
{
_instance = new MyAzureNotificationClient();
}
}
}
return _instance;
}
}
public NotificationHubClient Hub { get; private set; }
private MyAzureNotificationClient()
{
Hub = Microsoft.ServiceBus.Notifications.NotificationHubClient.CreateClientFromConnectionString("<full access notification connection string>", "<notification hub name>");
}
}
public interface INotificationMessage
{
string GetAppleMessage();
string GetAndroidMessage();
string GetWindowsMessage();
}
public class SampleMessage : INotificationMessage
{
public string Message { get; set; }
public string GetAndroidMessage()
{
var notif = JsonObject.Create()
.AddProperty("data", data =>
{
data.AddProperty("message", this.Message);
});
return notif.ToJson();
}
public string GetAppleMessage()
{
// Refer - https://github.com/paultyng/FluentJson.NET
var alert = JsonObject.Create()
.AddProperty("aps", aps =>
{
aps.AddProperty("alert", this.Message ?? "Your information");
});
return alert.ToJson();
}
public string GetWindowsMessage()
{
// Refer - http://improve.dk/xmldocument-fluent-interface/
var msg = new XmlObject()
.XmlDeclaration()
.Node("toast").Within()
.Node("visual").Within()
.Node("binding").Attribute("template", "ToastText01").Within()
.Node("text").InnerText("Message here");
return msg.GetOuterXml();
}
}
I am implementing a Rest service using ServiceStack. We use the repository pattern and auto-wire repositories into services via IOC.
Currently, we have a naive approach where one db model is paired with one repository. This means that whenever more than one entity is manipulated in one service, no transactional boundaries are used. Repositories are invoked sequentially: if one or more steps along the way fail, one has to "rollback" the db to its initial state, manually. Worst case scenario, if the request thread dies, or if an unchecked exception occurs (e.g., OutOfMemoryException) the database will be left in an inconsistent state.
I have a set of hypothetical solutions, but i regard none as adequate:
Open a connection and start a transaction at the Service Level. Invoke repositories, passing them the connection. This is obviously wrong as it goes against all the ddd design guidelines. The whole point is for the upper layers to be completely ignorant about concrete persistence. Moreover, it would mess up unit testing.
Have the first repository starting a transaction. Other dependent repositories would be invoked, but passing the already opened connection. This also sounds like bad design.
Defining aggregates. I'm not a great fan of this one as I'm not a domain modelling expert, and I feel that by introducing aggregates, I am liable to introduce designs errors. One advantage of the current model is that it is simple.
Any one has suggestions for this problem?
Thanks in advance
You can use a pass through class usually called UnitOfWork, where you will open and close the "connection". Search for "Unit of work" you will find many examples. You can customize the below snippet to include transactions.
public class UnitOfWork : IUnitOfWork
{
readonly CompanyDbContext _context;
public UnitOfWork()
{
_context = new CompanyDbContext ();
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Save()
{
_context.SaveChanges();
}
public IProductRepository ProductRepository
{
get { return new ProductRepository(_context); }
}
public ICartRepository CartRepository
{
get { return new CartRepository(_context); }
}
}
then you can do multiple transactions like below
using (_unitOfWork)
{
var p = _unitOfWork.ProductRepository.SingleOrDefault(a => a.id ==1);
_unitOfWork.CartRepository.Add(p);
_unitOfWork.Save();
}
In order to use Transactions effectively with Ormlite, you need to create custom DBManager class which can hold the connection object for each thread (use a ThreadStatic). Then you can use this custom DBManager in your repository to call different ormlite function.
Part of the code that I use is (you need modify the code to work properly):
public class ThreadSpecificDBManager : IDisposable, IDBManager
{
[ThreadStatic]
private static int connectionCount = 0;
[ThreadStatic]
private static int transactionCount = 0;
[ThreadStatic]
private static IDbConnection connection = null;
public string ConnectionString { get; set; }
public IDbConnection Connection { get { EnsureOpenConnection(); return connection; } }
static ThreadSpecificDBManager()
{
}
private void EnsureOpenConnection()
{
if ((connection == null) || (connection.State == ConnectionState.Closed))
{
OrmLiteConfig.TSTransaction = null;
transactionCount = 0;
connectionCount = 0;
connection = (DbConnection)ConnectionString.OpenDbConnection();
//if (ConfigBase.EnableWebProfiler == true)
// connection = new ProfiledDbConnection((DbConnection)connection, MiniProfiler.Current);
}
}
public ThreadSpecificDBManager(string connectionString)
{
ConnectionString = connectionString;
connectionCount++;
EnsureOpenConnection();
}
public void Dispose()
{
if (transactionCount > 0)
{
//Log.Error("Uncommitted Transaction is left");
}
connectionCount--;
if (connectionCount < 1)
{
if ((connection != null) && (connection.State == ConnectionState.Open))
connection.Close();
if (connection != null)
connection.Dispose();
connection = null;
}
}
public void BeginTransaction()
{
if (transactionCount == 0)
{
//Log.SqlBeginTransaction(0, true);
OrmLiteConfig.TSTransaction = Connection.BeginTransaction();
}
else
{
//Log.SqlBeginTransaction(transactionCount, false);
}
transactionCount = transactionCount + 1;
}
public void RollbackTransaction()
{
try
{
if (transactionCount == 0)
{
//Log.SqlError("Transaction Rollback called without a begin transaction call.");
return;
}
if (OrmLiteConfig.TSTransaction == null)
{
//Log.SqlError("Transaction is not saved in the Thread Safe variable- so it cannot be rollbacked.");
throw new Exception("Transaction is not saved in the Thread Safe variable- so it cannot be rollbacked.");
}
if (transactionCount == 1)
{
transactionCount = 0;
try
{
//Log.SqlRollbackTransaction(transactionCount, true);
OrmLiteConfig.TSTransaction.Rollback();
}
catch (Exception ex1)
{
//Log.SqlError(ex1,"Error when rolling back the transaction");
}
OrmLiteConfig.TSTransaction.Dispose();
OrmLiteConfig.TSTransaction = null;
}
else
{
//Log.SqlRollbackTransaction(transactionCount, false);
transactionCount = transactionCount - 1;
}
}
finally
{
}
}
public void CommitTransaction()
{
try
{
if (transactionCount == 0)
{
//Log.SqlError("Transaction Rollback called without a begin transaction call.");
return;
}
if (OrmLiteConfig.TSTransaction == null)
{
//Log.SqlError("Transaction is not saved in the Thread Safe variable- so it cannot be rollbacked.");
throw new Exception("Transaction is not saved in the Thread Safe variable- so it cannot be rollbacked.");
}
if (transactionCount == 1)
{
//Log.SqlCommitTransaction(transactionCount,true);
transactionCount = 0;
OrmLiteConfig.TSTransaction.Commit();
OrmLiteConfig.TSTransaction.Dispose();
OrmLiteConfig.TSTransaction = null;
}
else
{
//Log.SqlCommitTransaction(transactionCount, false);
transactionCount = transactionCount - 1 ;
}
}
finally
{
}
}
}