StoredProcedure custom sink for Serilog - serilog

I want to call a stored procedure using Serilog. I am aware that there is no such sink so I am creating a custom Sink. I have my logic to call the stored procedure inside the Emit method of the StoredProcedureSink that implements ILogEventSink. Now, this stored procedure returns a value. How can I get this value when I use Log.Information();
class StoredProcedureSink : ILogEventSink
{
private string _connectionString;
public StoredProcedureSink(string connectionString)
{
_connectionString = connectionString;
}
public void Emit(LogEvent logEvent)
{
var conn = new SqlConnection(_connectionString);
conn.Open();
SqlCommand cmd = new SqlCommand(logEvent.MessageTemplate.ToString().Substring(0, logEvent.MessageTemplate.ToString().IndexOf('{')), conn);
cmd.CommandType = CommandType.StoredProcedure;
var properties = logEvent.Properties.GetValueOrDefault("SqlParams");
var json = JObject.Parse(properties.ToString().Substring(properties.ToString().IndexOf('{') - 1));
foreach(var kvp in json)
{
cmd.Parameters.Add(new SqlParameter(kvp.Key, ((JValue)kvp.Value).Value));
}
cmd.ExecuteNonQuery();
//I would like to read the value returned by the stored proc.
}
}
//I have a wrapper DBLogger in which I configure the serilog. I have published DBLogger as a nuget package so I can use it in all my apps.
public class DBLogger()
{
public DBLogger()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.StoredProcedureSink(
"connectionString")
.CreateLogger();
}
public void Information(string storedProcedureName, T parameters)
{
try
{
Log.Information(storedProcedureName, parameters);
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
}
}
public class Program
{
static void Main()
{
var logger = new DBLogger();
logger.Information("storedProcedureName", params); //need the Id returned by the stored proc here.
}
}

That's not likely to be workable in practice using the normal Serilog model.
The standard processing involves the LogEvent being captured on the main thread, then supplied to each sink in turn - usually asynchronously, and often buffered.
The other concern is that in general, a sink definitely will not be propagating exceptions to the caller either (there is Audit logging, but even that's not intended for this sort of communication).
It seems to me that the sort of auditing you're seeking to accomplish is some distance from Serilog's sweet spot (I may be wrong though - please add some more detail to your question if so).
If you absolutely must do this, you can add an Enricher when logging, which sequesters a callback Action into the LogEvent's Properties, and have that pass it back. Please think long and hard before actually doing that though!

I ended up creating a static variable of StoredProcedureSink class and assigning the return value to that variable. Not sure if this would be the best way to do it.
class StoredProcedureSink : ILogEventSink
{
private string _connectionString;
**public static int returnValue;**
public StoredProcedureSink(string connectionString)
{
_connectionString = connectionString;
}
public void Emit(LogEvent logEvent)
{
var outputParam = new SqlParameter()
{
Direction = ParameterDirection.Output
};
try
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand(logEvent.MessageTemplate.ToString().Substring(0, logEvent.MessageTemplate.ToString().IndexOf('{')), conn))
{
conn.Open();
cmd.CommandType = CommandType.StoredProcedure;
var properties = logEvent.Properties.GetValueOrDefault("SqlParams");
var jsonProp = JObject.Parse(properties.ToString().Substring(properties.ToString().IndexOf('{') - 1).Replace(#"\",""));
var lastParam = jsonProp.Last;
foreach (var kvp in jsonProp)
{
if(kvp.Key == lastParam.Path)
{
outputParam.ParameterName = kvp.Key;
outputParam.SqlDbType = SqlDbType.Int;
cmd.Parameters.Add(outputParam);
break;
}
cmd.Parameters.Add(new SqlParameter(kvp.Key, ((JValue)kvp.Value).Value));
}
cmd.ExecuteNonQuery();
}
**returnValue = (int)outputParam.Value;**
}
}
catch(System.Exception e)
{
Debug.WriteLine(e.Message);
}
}
}
public class DBLogger : ILogger
{
public DBLogger()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.StoredProcedureSink(
"connectionString")
.CreateLogger();
}
~DBLogger()
{
Log.CloseAndFlush();
}
public static IHost CreateHostBuilder(string[] args) =>
new HostBuilder()
.UseSerilog() // <- Add this line
.Build();
public int Information<T>(string storedProcedureName, T parameters)
{
try
{
Log.Information(storedProcedureName, parameters);
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
**return StoredProcedureSink.returnValue;**
}
}

Related

How to Inject to EmailMessageService

I'm having problem with injecting my service. I've a ISettingService. I'm testing registration onmy application and using email confirmation.
So, at the EmailMessageService class which is inherit from IIdentityMessageService
I'm using Unity for Ioc. I'd registered ISettingService at unity config like below
.RegisterType<ISettingService, SettingService>()
I need to inject this interface to EmailMessageService class to access settings.
Here is the EmailMessageService class
public class EmailMessagingService : IIdentityMessageService
{
private ISettingService SettingService { get; set; }
public Task SendAsync(IdentityMessage message)
{
var fromEmailAddress = ConfigurationManager
.AppSettings["IdentityFromEmailAddress"];
var text = message.Body;
var html = message.Body;
// Do whatever you want to the message
using (var msg = new MailMessage())
{
msg.From = new MailAddress(fromEmailAddress);
msg.To.Add(new MailAddress(message.Destination));
msg.Subject = message.Subject;
msg.AlternateViews.Add(
AlternateView.CreateAlternateViewFromString(
text, null, MediaTypeNames.Text.Plain)
);
msg.AlternateViews.Add(
AlternateView.CreateAlternateViewFromString(
html, null, MediaTypeNames.Text.Html)
);
// var smtpClient = new SmtpClient("smtp.whatever.net", Convert.ToInt32(587));
// var credentials = new System.Net.NetworkCredential(Keys.EmailUser, Keys.EMailKey);
// smtpClient.Credentials = credentials;
using (var smtpClient = new SmtpClient())
{
var setting = SettingService.Query().Select().FirstOrDefault();
if (setting != null)
{
if (!string.IsNullOrEmpty(setting.SmtpHost))
{
smtpClient.Host = setting.SmtpHost;
smtpClient.Port = Convert.ToInt32(setting.SmtpPort);
if (setting.IsSmtpSsl)
{
smtpClient.EnableSsl = true;
}
}
}
smtpClient.Send(msg);
}
}
return Task.FromResult(0);
}
}
EmailMessageService class instantiating at Startup.Auth
var manager =
new ApplicationUserManager(
new ApplicationUserStore(context.Get<DataContext>()));
...
manager.EmailService = new EmailMessagingService();
I cant use Constructor injecting be cause of this direct call. So i used setter injection. But im getting error like "Object reference not set to an instance of an object"
var setting = SettingService.Query().Select().FirstOrDefault();
in EmailMessageService.
O.K What exacly happend i dont know but by changing UnityMvcActivator Start method like below its fixed.
public static void Start()
{
var container = ContainerManager.GetConfiguredContainer();
UnityConfig.RegisterTypes(container);
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
try
{
//http://stackoverflow.com/questions/699852/how-to-find-all-the-classes-which-implement-a-given-interface
foreach (var assembly in assemblies)
{
var instances = from t in assembly.GetTypes()
where t.GetInterfaces().Contains(typeof(IDependencyRegister))
&& t.GetConstructor(Type.EmptyTypes) != null
select Activator.CreateInstance(t) as IDependencyRegister;
foreach (var instance in instances.OrderBy(x => x.Order))
{
instance.Register(container);
}
}
}
catch (ReflectionTypeLoadException ex)
{
http://stackoverflow.com/questions/1091853/error-message-unable-to-load-one-or-more-of-the-requested-types-retrieve-the-l
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
System.IO.FileNotFoundException exFileNotFound = exSub as System.IO.FileNotFoundException;
if (exFileNotFound != null)
{
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
throw new Exception(errorMessage, ex);
//Display or log the error based on your application.
}
// TODO: Uncomment if you want to use PerRequestLifetimeManager
// Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
}

IMobileServiceClient.PullAsync deadlock when trying to sync with Azure Mobile Services

I have the classes below.
public class AzureMobileDataContext : IAsyncInitialization
{
private static readonly Lazy<AzureMobileDataContext> lazy =
new Lazy<AzureMobileDataContext> (() =>
new AzureMobileDataContext(
new MobileServiceClient(
"http://myservice.azure-mobile.net/",
"123456789ABCDEFGHIJKLMNOP")));
public static AzureMobileDataContext Instance { get { return lazy.Value; } }
public Task Initialization { get; private set; }
public IMobileServiceClient Context { get; private set; }
private Object lockObj = new Object ();
private static MobileServiceSQLiteStore store;
public AzureMobileDataContext (IMobileServiceClient context)
{
Context = context;
Initialization = Init ();
Initialization.ContinueWith (async (antecedent) => {
await Context.SyncContext.InitializeAsync (store, new MobileServiceSyncHandler ());
});
}
private Task Init ()
{
return Task.Run (() => {
lock (lockObj) {
if (!Context.SyncContext.IsInitialized) {
try {
store = new MobileServiceSQLiteStore ("mysqlite.db3");
store.DefineTable<Post> ();
store.DefineTable<PostPhotoUrl> ();
store.DefineTable<User> ();
store.DefineTable<Club> ();
store.DefineTable<District> ();
} catch (Exception ex) {
Debug.WriteLine ("Init: {0}", ex.Message);
}
}
}
});
}
public async Task<IMobileServiceSyncTable<TEntity>> GetTableAsync<TEntity> ()
{
await Initialization;
return Context.GetSyncTable<TEntity> ();
}
public async Task PushAsync ()
{
try {
await Initialization;
await Context.SyncContext.PushAsync ();
} catch (MobileServiceInvalidOperationException invalidOperationEx) {
Debug.WriteLine (invalidOperationEx.Message);
} catch (MobileServicePushFailedException pushFailedException) {
Debug.WriteLine (pushFailedException.Message);
}
}
public async Task PullAsync<TEntity> (IMobileServiceTableQuery<TEntity> query)
{
try {
await Initialization;
IMobileServiceSyncTable<TEntity> entityTable = await GetTableAsync<TEntity> ();
await entityTable.PullAsync (typeof(TEntity).ToString (), query); // Never returns, no exception is caught or thrown.
await entityTable.PurgeAsync ();
} catch (MobileServiceInvalidOperationException preconditionFailedEx) {
Debug.WriteLine (preconditionFailedEx.Message);
} catch (Exception ex) {
Debug.WriteLine (ex.Message);
}
}
public async Task SyncAsync<TEntity> ()
{
await PushAsync ();
IMobileServiceSyncTable<TEntity> syncTable = await GetTableAsync<TEntity> ();
await PullAsync (syncTable.CreateQuery ());
}
}
I use this singleton from a BaseRepository I have that is a base class for 5 different entity repositories.
public abstract class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected AzureMobileDataContext MobileServiceContext { get { return AzureMobileDataContext.Instance; } }
protected virtual Task PushAsync ()
{
return MobileServiceContext.PushAsync ();
}
protected virtual Task PullAsync (IMobileServiceTableQuery<TEntity> query)
{
return MobileServiceContext.PullAsync (query);
}
public virtual async Task<DataObjectResponse<IEnumerable<TEntity>>> FindAsync (Expression<Func<TEntity, bool>> predicate)
{
IMobileServiceSyncTable<TEntity> syncTable = await MobileServiceContext.GetTableAsync<TEntity> ();
await PullAsync (syncTable.CreateQuery ());
IEnumerable<TEntity> entities = await syncTable.Where (predicate).ToEnumerableAsync ();
return new DataObjectResponse<IEnumerable<TEntity>> (entities);
}
}
The users repository.
public class UsersAzureRepository : BaseRepository<User>, IUsersRepository
{
public UsersAzureRepository ()
{
}
public async Task<DataObjectResponse<User>> FindByIdAsync (string entityId)
{
DataObjectResponse<IEnumerable<User>> users = await FindAsync (p => p.Id == entityId);
return new DataObjectResponse<User>(users.Data.FirstOrDefault ());
}
}
A DataService Facade class containing the GetUserById method.
public async Task<UserModel> GetUserById (string userId)
{
DataObjectResponse<User> users = await UsersRepository.FindByIdAsync (userId);
UserModel userModel = Mapper.Map<User, UserModel> (users.Data);
return userModel;
}
Users view model method.
public async Task<UserModel> GetUsersAsync() // testing purposes
{
UserModel user = await _dataService.GetUserById("032beb3b-1cbf-4a0d-809c-a25c71139c55");
if (user != null) {
Debug.WriteLine ("User loaded: {0}", user.Id);
}
return user;
}
Call from an iOS UIViewController.
public async override void ViewDidLoad ()
{
base.ViewDidLoad ();
UserModel user = await ViewModel.GetUsersAsync ();
}
The AzureMobileDataContext serves more as a thread safe wrapper to the IMobileServiceClient context operations, making sure not multiple threads will try to initialize the database (I had an exception when using it directly to the BaseRepository<T> before).
I'm not so sure from here where might the problem is. I suspect that the wrapper is not the best solution and any recommendations are welcome.
Any other ways to debug the PullAsync method?
[EDIT]
The local SQLite database syncs the table data from the remote service but still the call doesn't return.
The problem was in the Azure Mobile Service, server side.
I was returning from the TableController an IEnumerable but the SDK uses OData query expressions to do it's own job, returning an IEnumerable is not sufficient, changing to IQueryable fixed this issue of a continuous looping when pulling data.
I strongly believe that the server return type shouldn't be related to the SDK but this is the way it works.

Unable to update row of tableView after connecting to a url

I am building a download manager
Here I have shown a test code which tries to update fileNameColumn of a row of tableView but it is not being updated after I connect to url
To be specific, here fileName remains hello1 and it doesnt get updated to hello2. Yhy's that so?
Main.java :
public static TableView<DownloadEntry> downloadsTable;
public TableColumn<DownloadEntry, String> fileNameColumn;
public void initialize(URL location, ResourceBundle resources) {
downloadsTable = new TableView<DownloadEntry>();
fileNameColumn = new TableColumn<>("File Name");
fileNameColumn.setCellValueFactory(new PropertyValueFactory<>("fileName"));
executor = Executors.newFixedThreadPool(4);
}
public void addDownloadButtonClicked() {
try{
String urlText = urlTextBox.getText();
DownloadEntry task = new DownloadEntry(new URL(urlText));
downloadsTable.getItems().add(task);
executor.execute(task);
}
catch(Exception e) {
System.out.println("addDownloadButtonClicked: " + e);
}
}
DownloadEntry.java:
public class DownloadEntry extends Task<Void> {
public SimpleStringProperty fileName;
public URL url;
//Constructor
public DownloadEntry(URL ur) throws Exception{
fileName = new SimpleStringProperty("hello");
url = ur;
}
#Override
protected Void call() {
try {
HttpURLConnection connect=(HttpURLConnection)url.openConnection();
fileName.set("hello1");
connect.connect();
fileName.set("hello2");
}
catch(Exception E) {
this.updateMessage("Error");
E.printStackTrace();
}
return null;
}
public String getFileName() {
return fileName.get();
}
public void setFileName(String fileName) {
this.fileName = new SimpleStringProperty(fileName);
}
}
Please tell if you need more details..
Your model is incorrectly implemented. The setFileName method should be
public void setFileName(String fileName) {
this.fileName.set(fileName);
}
(The problem with your implementation is that the table is still observing the old property, not the new one you create.)
You will also need to provide a "property accessor" method:
public StringProperty fileNameProperty() {
return fileName ;
}
which will allow the table to properly bind to the property (so that it "knows" when its value changes).

Web API, Light Inject and Passing a Static Dictionary to the data layer

We have a multi-database solution and are passing the connection string to a factory function like so:
container.Register<IDbContextFactory>(
f => new DynamicDbContextFactory(ClientConfig.GetConnectionString()),
new PerScopeLifetime());
ClientConfig contains a static dictionary that gets populated on app start that maps a sub domain to a connection string. It seems that this approach is causing a memory leak (not 100% sure about this causing the leak but there is a leak).
public class ClientConfig
{
private static ConcurrentDictionary<string, string> ConnectionStringManager
{
get;
set;
}
// etc.
}
My question is in MVC what is the best way to hold a list of connection strings that can be easily looked up on each request in order to pass that down the chain.
Edit : The question was initially tagged with Autofac
With Autofac you don't have to use a dictionary and something like that to do what you want. You can use a custom parameter :
public class ConnectionStringParameter : Parameter
{
public override Boolean CanSupplyValue(ParameterInfo pi,
IComponentContext context,
out Func<Object> valueProvider)
{
valueProvider = null;
if (pi.ParameterType == typeof(String)
&& String.Equals(pi.Name, "connectionString",
StringComparison.OrdinalIgnoreCase))
{
valueProvider = () =>
{
// get connectionstring based on HttpContext.Current.Request.Url.Host
return String.Empty;
};
}
return valueProvider != null;
}
}
Then register your Parameter using a Module
public class ConnectionStringModule : Autofac.Module
{
protected override void AttachToComponentRegistration(
IComponentRegistry componentRegistry, IComponentRegistration registration)
{
registration.Preparing += registration_Preparing;
}
private void registration_Preparing(Object sender, PreparingEventArgs e)
{
Parameter[] parameters = new Parameter[] { new ConnectionStringParameter() };
e.Parameters = e.Parameters.Concat(parameters);
}
}
Module you have to register inside your container using
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConnectionStringModule());
Each time Autofac have to resolve a parameter of type String named connectionString it will used the custom parameter and get your connectionstring based on what you want.
By the way this code sample use HttpContext.Current. In case of a multithreaded process it may return null. I don't recommend using HttpContext.Current for such things. You can use an intermediate class instead of accessing it, for example a IConnectionstringProvider interface.
public interface IConnectionstringProvider
{
String ConnectionString { get; }
}
public class ConnectionStringProvider : IConnectionstringProvider
{
public ConnectionStringProvider(Strong host)
{
// get connectionstring based on host
this._connectionString = String.Empty;
}
private readonly String _connectionString;
public String ConnectionString
{
get { return this._connectionString; }
}
}
Inside your Parameter you will have to change the valueProvider by
valueProvider = () =>
{
return context.Resolve<IConnectionstringProvider>().ConnectionString;
};
And finally you will have to register your IConnectionstringProvider at the beginning of the request lifetimescope :
class Program
{
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConnectionStringModule());
IContainer container = builder.Build();
container.ChildLifetimeScopeBeginning += container_ChildLifetimeScopeBeginning;
}
private static void container_ChildLifetimeScopeBeginning(
Object sender, LifetimeScopeBeginningEventArgs e)
{
String host = HttpContext.Current.Request.Url.Host;
ContainerBuilder childLifetimeScopeBuilder = new ContainerBuilder();
childLifetimeScopeBuilder.RegisterInstance(new ConnectionStringProvider(host))
.As<IConnectionstringProvider>()
.SingleInstance();
childLifetimeScopeBuilder.Update(e.LifetimeScope.ComponentRegistry);
}
}
Of course there is many way to do it but you have the idea

How to receive XmlDocument in MVC 4 Web Api?

I am posting XmlDocument to ApiController (from windows service, service is working fine, it is posting correct, i used it in wcf web api), but xml is always null, what am i doing wrong?
I can post some class, such in tutotials, or Get any data and everything will be ok, but i can't post XmlDocument.
public class XmlController : ApiController
{
public void PostXml(XmlDocument xml)
{
// code
}
}
i follow the solution given by #Rhot but somehow it doesn't work so i edit like below which work for me:
public class XmlMediaTypeFormatter : MediaTypeFormatter
{
public XmlMediaTypeFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
}
public override bool CanReadType(Type type)
{
return type == typeof(XDocument);
}
public override bool CanWriteType(Type type)
{
return type == typeof(XDocument);
}
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
{
var reader = new StreamReader(stream);
string value = reader.ReadToEnd();
var tcs = new TaskCompletionSource<object>();
try
{
var xmlDoc = XDocument.Parse(value);
tcs.SetResult(xmlDoc);
}
catch (Exception ex)
{
//disable the exception and create custome error
//tcs.SetException(ex);
var xml = new XDocument(
new XElement("Error",
new XElement("Message", "An error has occurred."),
new XElement("ExceptionMessage", ex.Message)
));
tcs.SetResult(xml);
}
return tcs.Task;
}
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
{
var writer = new StreamWriter(stream);
writer.Write(((XDocument)value).ToString());
writer.Flush();
var tcs = new TaskCompletionSource<object>();
tcs.SetResult(null);
return tcs.Task;
}
}
register to global.asax:
GlobalConfiguration.Configuration.Formatters.Insert(0, new XmlMediaTypeFormatter());
and below my WebAPI Controller:
public HttpResponseMessage Post(XDocument xml)
{
return Request.CreateResponse(HttpStatusCode.OK, xml);
}
I've found a solution:
We need to use inheritance to inherit MediaTypeFormatter
public class XmlMediaTypeFormatter : MediaTypeFormatter
{
public XmlMediaTypeFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
}
public override System.Threading.Tasks.Task<object> ReadFromStreamAsync(Type type, Stream stream,
HttpContentHeaders contentHeaders,
IFormatterLogger formatterLogger)
{
var taskCompletionSource = new TaskCompletionSource<object>();
try
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
var s = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray());
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(s);
taskCompletionSource.SetResult(xmlDoc);
}
catch (Exception e)
{
taskCompletionSource.SetException(e);
}
return taskCompletionSource.Task;
}
public override bool CanReadType(Type type)
{
return type == typeof(XmlDocument);
}
public override bool CanWriteType(Type type)
{
return false;
}
}
Then register it in Global.asax:
GlobalConfiguration.Configuration.Formatters.Insert(0, new XmlMediaTypeFormatter());
Controller:
public HttpResponseMessage PostXml([FromBody] XmlDocument xml)
{//code...}
Is PostXml supposed to be an action on a controller? If so you should mark your controller action as accepting an HttpPost. From there I would modify the action to work as follows:
[HttpPost]
public ActionResult PostXml(HttpPostedFileBase xml)
{
// code
}
If you are still have trouble accepting the posted files, fire up the debugger and inspect the Request files collection: http://msdn.microsoft.com/en-us/library/system.web.httprequest.files.aspx

Resources