I'm trying to develop a .NET MAUI class library. During an event, if an exception raises a display alert window should be visualized. In a .NET MAUI project this works fine, but in .NET MAUI class library project there is something missing, maybe I should add a reference. Could someone provide a solution?
catch (Exception ex)
{
await DisplayAlert("EXCEPTION RAISED", ex.ToString(), "CANCEL");
}
This is really bad idea. For more than one reason.
Tomorrow when you try to reuse this library in a thread, that has no interface, what will you do?
How will you know the consumer of your library will know what exactly went wrong? I've dealt in the past with libs that basically return true/false, and hide the exception. Go figure out.
If you decide to make changes and fix it at some point, everyone that is using the old version of your library will have really bad time to edit every line he uses your library.
To answer your question. You can pass any object as parameter to any function. Or you can do this as initialization. If you still want to do it.
As alternative, I sometimes use messaging service. If the elements are de-coupled and I don't want them to be coupled. The code that does work broadcasts messages, and IF (this is important) there is interface registered to receive such messages, they are handled.
Related
I'm developing a DLL and I want to log some data it generates.
I wanted to use "Log4Net", but I found the problem that in a DLL I don't have an "App.config" file where I can write the XML code, so I don't know how to implement this (I'm new in this matters).
I read about "Singleton" but I saw it's better to avoid it since it has it's issues (i.e hide some visibility of the code, problems with unit tests, ...).
So my question is: How and what is the best way to create a log file for the data generated by my DLL?
A DLL - a class library - should never be logging by itself. Even the ones that are there for output - like the one containing Console or even logger code - should never decide to write their own logfile. Logging work - all output work - that is not controllable or even fully controlled by the programmer using your DLL, is just going to be vexing behavior. And you should never write something with Vexing behavior.
Logging is the job of the person using your code, not of your code. If you are writing a Library or really anything else that usually has no output (like a Windows Service), it is customary to have a wrapper project for debugging and testing.
If it is important enough it warants an Exception. If it is not important enough for a Exception - it is propably not important enough at all. It is a daunting challenge to write good Exception handling, nevermind good Exception throwing code. But there are two articles on the mater that I link very often. And I really think would help you get you on the right paths:
https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/
https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
They really helped me get a handle on it. And thus far they helped countless others. And their ideas are not even tied to .NET only.
The config file will be connected in running module.
It will be in exe file if it's a console application,
or in web.config in case of web application.
To log the application flow in the DLL,
Just create a Class that create and access the log text file.
In that class, declare the object LoggingClass loggingObject; and then use this instance to access the log file.
In creating object for it, you can use,
public static LoggingClass createOrGetObject()
{
return (loggingObject == null)? new LoggingClass() : loggingObject;
}
Now, just you can call this method to get the same instance that access the log file to write the log.
In this example, Log4Net is not used but works fine for logging.
You don't say who you expect to use your dll.
If it will be used by lots of other people and if the logging is useful to them, then may not want to be forced to use log4net or this may cause problems if they want to use a different version of log4net than you are using.
I have seen several dlls which use Common.Logging to avoid this issue which allows the consumers to use whichever logging package they want.
Having said that, see Configure log4net logging in dll for another possible solution.
I was wondering if there is any way to catch a 'Project add' event. I know you can do that with the DTE e.g. SolutionEvents.ProjectAdded event. However I am trying to accomplish this without the use of the DTE. I already tried it with the IVsHierarchyEvents, sadly the OnItemAdded and OnItemsAppended methods never get called for added Projects.
Note that I want to catch Projects which are either direct children of the solution or a solution-folder.
SolutionEvents.ProjectAdded is the best way to do this as far as I can tell. Sometimes (not always), but sometimes there aren't more appropriate lower level APIs or interfaces that provide the same or similar functionality found in the EnvDTE automation interfaces. In this particular instance the SolutionEvents ProjectAdded event isn't built upon a lower level API or service, and it's the only event I could find that fits what you're looking for here.
Sincerely,
So we built this library/framework thing full of code related to business processes and common elements that are shared across multiple applications (C#, .Net 4.7.1,WPF, MVVM). Our Logging stuff is all set up through this framework so naturally it felt like the best place for Sentry. All the references in our individual applications are manually pointed to the dlls the folder where our shared library thingy installs itself. So far so good.
When we set up Sentry initially everything seemed to work great. We do some updates and errors seem to be going way down. That's cause we are awesome and Sentry helped us be more awesome, right? Nope! Well I mean kind of.
The scope is being disposed of so we are no longer getting Unhandled exceptions. We didn't notice at first because we are still getting sentry logs when we are handling errors through our Logging.Log() method. This logging method calls SentrySdk.Init() which I suspect is disposing the client in the executing assembly.
We also started using Sentry for some simple Usage tracking by spinning up a separate project in Sentry called Usage-Tracker and passing a simple "DoThingApplication has been launched" with an ApplicationName.UsageTracker Enum as a parameter to our Logging method.
Question: What is a good way to handle this where my setup can have a Sentry instance that wraps my using(sentryClientStuff){ ComposeObjects(); } and still have my logging method look for the existing client and use it if it exists?
Caveats:
I believe before any of this happens we still need to make a call to send a Sentry log to our UsageTracker.
I would like to pass in as few options as possible if I'm setting up the Sentry Client/Scope in our shared library. Maybe Release and Environment. Maybe check tags for Fingerprint and set it in the Log method.
I'm open to new approaches to any of this.
Some related thoughts
Maybe there is a better way to handle references that could solve both this and some other pains of when they have become mismatched between client and shared framework/library thing
Maybe the answer can be found through adding some Unit Tests but I could use a Sentry specific example or a nudge there because I don't know a muc about that.
Maybe there is a way to use my shared library to return a Sentry Client or Scope that I could use in my client assembly that would not be so fragile and the library could somehow also use it.
Maybe there is a better solution I can't conceive because I'm just kind of an OK programmer and it escapes me. I'm open to any advice/correction/ridicule.
Maybe there is a smarter way to handle "Usage-Tracker" type signals in Sentry
Really I want a cross-assembly singleton kind of thing in practice.
There are really many things going on here. Also without looking at any code it's hard to picture how things are laid out. There's a better chance you can get the answer your are looking for if you share some (dummy even) example of the structure of your project.
I'll try to break it down and address what I can anyway:
With regards to:
Usage-Tracker:
You can create a new client and bind to a scope. That way any use of the SentrySdk static class (which I assume your Logger.Log routes to) will pick up.
In other words, call SentrySdk.Init as you currently do, with the options that are shared across any application using your shared library, and after that create a client using the DSN of your Usage-Tracker project in sentry. Push a scope, bind the client and you can use SentrySdk with it.
There's an example in the GitHub repo of the SDK:
using (SentrySdk.PushScope())
{
SentrySdk.AddBreadcrumb(request.Path, "request-path");
// Change the SentryClient in case the request is to the admin part:
if (request.Path.StartsWith("/admin"))
{
// Within this scope, the _adminClient will be used instead of whatever
// client was defined before this point:
SentrySdk.BindClient(_adminClient);
}
SentrySdk.CaptureException(new Exception("Error at the admin section"));
// Else it uses the default client
_middleware?.Invoke(request);
} // Scope is disposed.
The SDK only has to be initialized once but you can always create a new client with new SentryClient, push a new scope (SentrySdk.PushScope()) and bind it to that new scope (SentrySdk.BindClient). Once you pop the scope the client is no longer accessdible via SentrySdk.CaptureException or any other method on the static class SentrySdk.
You can also use the client directly, without binding it to the scope at all.
using (var c = new SentryClient(new SentryOptions { Dsn = new Dsn("...") })) {
c.CaptureMessage("hello world!");
}
The using block is there to make sure the background thread flushes the event.
Central place to initialize the SDK:
There will be configuration which you want to have fixed in your shared framework/library but surely each application (composition root) will have its own setting. Release is auto-discovered.
From docs.sentry.io:
The SDK will firstly look at the entry assembly’s AssemblyInformationalVersionAttribute, which accepts a string as value and is often used to set a GIT commit hash.
If that returns null, it’ll look at the default AssemblyVersionAttribute which accepts the numeric version number.
If you patch your assemblies in your build server, the correct Release should be reported automatically. If not, you could define it per application by taking a delegate that passes the SentryOptions as argument.
Something like:
Framework code:
public class MyLogging
{
void Init(Action<SentryOptions> configuration)
{
var o = new SentryOptions();
// Add things that should run for all users of this library:
o.AddInAppExclude("SomePrefixTrueForAllApplications");
o.AddEventProcessor(new GeneralEventProessor());
// Give the application a chance to reconfigure anything it needs:
configuration?.Invoke(o);
}
}
App code:
void Main()
{
MyLogging.Init(o => o.Environment = "my env");
}
The scope is being disposed of so we are no longer getting Unhandled exceptions."
Not sure I understand what's going on here. Pushing and popping (disposing) scopes don't affect the ability of the SDK to capture unhandled exceptions. Could you please share a repro?
This logging method calls SentrySdk.Init() which I suspect is disposing the client in the executing assembly.:
Unless you create a client "by hand" with new SentryClient, there's only 1 client in the running process. Please note I said running process and not assembly. Instances are not held within an assembly. The assembly only contains the code that can be executed. If you call SentrySdk.CaptureException it will dispatch the call to the SentryClient bound to the current scope. If you didn't PushScope, there's always an implicit scope, the root scope. In this case it's all transparent enough you shouldn't care there's a scope in there. You also can't dispose of that scope since you never got a handle to do so (you didn't call PushScope so you didn't get what it returns to call Dispose on).
All the references in our individual applications are manually pointed to the dlls the folder where our shared library thingy installs itself.:
One thing to consider, depending on your environment is to distribute packages via NuGet. I'm unsure whether you expect to use these libraries in non .NET Framework applications (like .NET Core). But considering .NET Core 3.0 is bringing Windows Desktop framework support like WPF and WinForm, it's possible that eventually you will. If that's the case, consider targeting .NET Standard instead of .NET Framework for your code libraries.
I'm looking for a way to do one thing I have in mind and I hope anybody here can help me. This is my scenario: I have two libraries, the first performs some actions, the second is a simple logger. In the first library I have a winform which makes some operations to a database, and the user can add or delete data using this form; I would like to use the second library to log all the operations the user makes but without referencing the log library from the first one. What I am looking for is a way to let the main application know what the user makes in the first library and then call the second one to write some data in the log. Another thing to keep in mind is that the form in the first library is opened as a dialog form. Is it possible to do? And if yes, how can I do this? Thanks in advance.
[EDIT]
I realize that maybe my question was not so clear as it was in my mind. In order to keep the two libraries separated (I don't want to call the logging library from the first library in any way), I was thinking about something in the first library that raises an event or something similar or something else in the main application, so if I want i can call the logging methods in the second library from the main application when needed. I hope this is more clear.
I'm only wondering why you want this slower, 'hackier' way of doing this if you can just add a reference to that library. Why not use the logical approach of referencing the needed library? Nonetheless, it's possible to use classes and methods in a library without having to reference that library, using reflection.
You can use something like this
Assembly SampleAssembly;
SampleAssembly = Assembly.LoadFrom("c:\\Sample.Assembly.dll");
// Obtain a reference to a method known to exist in assembly.
MethodInfo Method = SampleAssembly.GetTypes()[0].GetMethod("Method1");
Source: https://msdn.microsoft.com/en-us/library/system.reflection.assembly.gettypes(v=vs.110).aspx
Do understand that using reflection is considered slow, so I'd advice not to use this in loops or recursive pieces of code.
Dependency injection is the best candidate for your problem:
http://www.martinfowler.com/articles/injection.html
Here you can find how to implement it for .net
https://visualstudiomagazine.com/articles/2010/08/01/inversion-of-control-patterns-for-the-microsoft-net-framework.aspx
In two words: you need to define interface in core library (Core.dll):
interface ILogger
{
void log(string message);
}
You libraryA refers to Core.dll, but not to libraryB.
and resolve ILogger interface in your library
var logger = ServiceContainer.Resolve<ILogger>()
In your app (refers to Core,libraryA, libraryB) you should register your logger:
ServiceContainer.Register(typeof(ILogger),<MyLogger is implemented in AssemblyB or in the main app, implements ILogger interface>);
I've made sample, which demonstrates how to call Logger from PCL library. Logger is implemented in console application:
https://github.com/Mogikan/Stackoverflow-samples/tree/master/Logger
Recently I looked at some coding at the web. I found some people use Microsoft.VisualBasic.CompilerServices.ProjectData.ProjectData class in catch block.
catch (Exception exception1)
{
//ProjectData.SetProjectError(exception1);
Console.WriteLine(exception1.ToString());
Console.WriteLine();
Console.WriteLine(sSQL);
//ProjectData.ClearProjectError();
}
I searched it on msdn that mentioned that this API supports the .NET Framework infrastructure and is not intended to be used directly from your code.
I am curious what reason people use it. Would you explain it to me?
My experience has been that this type of code use is found in c#/VB.NET projects that have been converted from VB6. When developing new c#/VB.NET solutions/projects, this practice should not be used.
Note: This technique can safely be replaced with proper exception handling that you would be used to seeing in other .NET solutions/projects.
This code is either emitted by a code conversion tool that converted VB code to C#, or resulted from decompiling an assembly that was originally created using VB.
I'm porting a VB project to Mono, and found out that the VB compiler injects these calls ProjectData.SetProjectError(exception) and ProjectData.ClearProjectError() in any catch block, and try to find a way to prevent the compiler from doing so because Mono doesn't implement the ProjectData module. And found your question while doing my research!
so this is a result of old legacy vb6 for those interested. when vb6 started out there was an err object which is still around but has moved to the projectdata object in vb. if anybody like me remembers vb6 ( this is back when dinosaurs roamed the earth), there was a handy little call on error resume next. this is if you did not like those pesky little exceptions. most vb6 programs used it copiously and voila you had no exceptions because you ignored any. so here comes the explanation.
catch (Exception exception1) // catch any exceptions that just happened
{
ProjectData.SetProjectError(exception1); // set the information
//in the err object if anyone actually wants to check
ProjectData.ClearProjectError(); //clear the err object
}
as you can see this is completely ignoring any exceptions and in true vb6 fashion, your code just explodes without any explanation. needless to say if anyone writes code like this or uses vb in this way, I will find you and figure out a way to get you incarcerated.