I have an interface
SQLiteConnection _connection = Getconnection();
public SQLite.SQLiteConnection GetConnection()
{
const string sqliteFilename = "TCRMobile.db3";
string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); // Documents folder
var dbPath = Path.Combine(documentsPath, sqliteFilename);
return new SQLite.SQLiteConnection(dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex, false);
}
So when I kill my app or in any random case, the app crashed. And App Center says reason for crash as:
Cache.get_Connection ()
System.InvalidOperationException: You MUST call Xamarin.Forms.Init(); prior
to using it.
Device.get_PlatformServices ()
Device.GetAssemblies ()
DependencyService.Initialize ()
DependencyService.Get[T] (Xamarin.Forms.DependencyFetchTarget fetchTarget)
Cache.get_Connection ()
TCRMobile.DataAccess.DataAccessBase..cctor () [0x00005] in
<7ccb325064fd467288e39511a2bcad63>:0
I added global::Xamarin.Forms.Forms.Init(this, bundle); in MainActivity.cs file. But still it crashes.
Any help?
First of all, make sure that in your MainActivity.cs (Android) / AppDelegate.cs (iOS) the Forms.Init gets called before the making of the App.
So something like this would be the correct approach
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
If the above is correct, check the flow.
You should have
An interface in your portable class
public interface ISQLite
{
SQLiteConnection GetConnection();
}
An implementation for each platform (so iOS/Android/UWP) containing an assembly tag
Android example
[assembly: Xamarin.Forms.Dependency(typeof(SqLiteAndroid))]
namespace Your.Namespace.Droid
{
public class SqLiteAndroid:ISQLite
{
public SQLiteConnection GetConnection()
{
//return connection here
}
}
}
and iOS example, same principle but notice the different namespace and assembly
[assembly: Xamarin.Forms.Dependency(typeof(SqLiteiOS))]
namespace Your.Namespace.iOS
{
public class SqLiteiOS:ISQLite
{
public SQLiteConnection GetConnection()
{
//return connection here
}
}
}
A call to the DependencyService to get the connection e.g. in your App.xaml.cs
string sqlConnection= DependencyService.Get<ISQLite>().GetConnection()
Let me know if this works for you, if not, please provide some more code.
Related
I have a function app connected with an application insights instance.
When I look at the requests on application insights, all entries have a resultCode of 0, regardless of whether it was successful or not. How can I have the resultCode showing properly?
If I get it correctly, my function app is running at the version "3.0.14916.0".
Here is my startup:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddLogging(loggingBuilder =>
{
var key = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
loggingBuilder.AddApplicationInsights(key);
});
builder.Services.AddSingleton(sp =>
{
var key = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
return new TelemetryConfiguration(key);
});
(...)
}
}
Edit 1:
In the comments it was asked why I am adding logging in Startup. I do it because, as far as I could verify, ILogger < MyClass > only logs to AI if I add logging in Startup.
Following is an example of an injected class. Note that this class is also used in other projects.
public class CosmosDbService : ICosmosDbService
{
private readonly IDocumentClient _documentClient;
private readonly ILogger _logger;
public CosmosDbService(IDocumentClient documentClient, ILogger<CosmosDbService> logger)
{
_logger = logger;
_documentClient = documentClient;
}
public async Task<UserData> GetUserAsync()
{
try
{
// Getting user here
// (...)
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching user.");
throw;
}
}
}
This class is injected as:
builder.Services.AddSingleton<IDocumentClient>(sp =>
{
// This does not really matter for this question
var configuration = sp.GetService<IConfiguration>();
var connectionString = configuration.GetValue<string>("COSMOS_DB_CONNECTION");
var cosmosDbConnectionString = new CosmosDbConnectionString(connectionString);
return new DocumentClient(cosmosDbConnectionString.ServiceEndpoint, cosmosDbConnectionString.AuthKey);
});
builder.Services.AddSingleton<ICosmosDbService, CosmosDbService>();
This answer from #PeterBons helped me fixing the wrong resultCode as well.
Basically I was importing the wrong package: Microsoft.Extensions.Logging.ApplicationInsights
I changed it to Microsoft.Azure.WebJobs.Logging.ApplicationInsights and removed the code in Startup. Now I got the resultCode properly filled in again.
I have my connection string to SQL stored in the Web project in appsettings.json
"ConnectionStrings": {
"MyDbConnectionString": "***"
},
Then I added a DB context using Scaffold
Scaffold-DbContext -Connection "name=MyDbConnectionString" -Provider "Microsoft.EntityFrameworkCore.SqlServer" ... -Force
I can use the context in a controller and I have no issues getting data or writing. However, I would like all my business logic to be on a separate class library. So here is my repository from my Library:
public class MyRepository
{
private static MyContext CurrentContext
{
get { return new MyContext(); }
}
public static async void AddEventLog(EventLog eventLog)
{
using (var context = CurrentContext)
{
context.EventLog.Add(eventLog);
await context.SaveChangesAsync();
}
}
}
But it fails when it tries to write to the DB.
System.InvalidOperationException: 'A named connection string was used, but the name 'MyDbConnectionString' was not found in the application's configuration.
Should I be adding appsettings.json to the library project (This seems redundant, and incorrect)? What am I missing? How do I reference back to the web projects appsettings.json file?
Any help would be greatly appreciated.
Here is my startup
services.AddDbContext<MyContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("MyDbConnectionString")));
***** HERE ARE CHANGES I HAVE MADE TO THE WORK *****
I have found the issue I believe so here we go.
Remove the following from MySsdCaseContext.
public MySsdCaseContext()
{
}
and keep this one..
public MySsdCaseContext(DbContextOptions<MySsdCaseContext> options) : base(options)
{
}
For the purposes of fixing this comment out the following from OnConfiguring.
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("name=MySsdCaseDb");
}
In startup.cs add the following inside ConfigureService method.
services.AddDbContext<MySsdCaseContext>(options
=>options.UseSqlServer(Configuration.GetConnectionString("MySsdCaseDb")));
This should prompt you to add a reference to MySsdCase.Core.Data class library. You don't currently have this. Basically put
the following at the top of startup.cs
using MySsdCase.Core.Data;
Ensure the following is inside MySsdCase.Web.cspoj
<ItemGroup>
<ProjectReference Include="..\MySsdCase.Core\MySsdCase.Core.csproj" />
</ItemGroup>
Do it like this...
public class EventLogRepository
{
private readonly MySsdCaseContext _context;
public async Task AddEventLogAsync(EventLog eventLog)
{
var myVar = await _context.Set<ClientDetails>()
.AsNoTracking()
.Select(p => p)
.Take(2)
.ToListAsync();
}
}
I think overall there was no reference to the DAL from the BL in startup.cs.
I am following this tutorial for SQLite practice in Xamarin:
https://code.tutsplus.com/tutorials/an-introduction-to-xamarinforms-and-sqlite--cms-23020
Now I am stuck here in
public RandomThoughtDatabase ()
{
_connection = DependencyService.Get"ISQLite" ().GetConnection ();
}
ISQLite is an interface in PCL
its giving this error
Error CS0119 'DependencyService.Get(DependencyFetchTarget)' is a method, which is not valid in the given context LocalStorage
Use the following instead:
public RandomThoughtDatabase () { _connection = DependencyService.Get<ISQLite>().GetConnection(); }
I'm trying to get the context for a hub using the following:
var hubContext = GlobalHost.ConnectionManager.GetHubContext<SomeHub>();
The problem is that GlobalHost is not defined. I see it is part of the SignalR.Core dll. At the moment, I have the following in my project .json file, under dependencies:
"Microsoft.AspNet.SignalR.Server": "3.0.0-*"
If I add the latest available version of Core:
"Microsoft.AspNet.SignalR.Server": "3.0.0-*",
"Microsoft.AspNet.SignalR.Core" : "2.1.2"
I get a whole bunch of errors because server and core are conflicting. If I change them to both use version "3.0.0-*", all the conflicts go away, but GlobalHost cannot be found. If I remove Server, and just user Core version 2.1.2 then GlobalHost works, but all the other things needing Server, obviously do not.
Any ideas?
IConnectionManager does not exist any more in SignalR for ASP.Net Core.
I've been using HubContext for getting access to a hub.
public class HomeController : Controller
{
private readonly IHubContext<LiveHub> _hubContext;
public HomeController(IHubContext<LiveHub> hubContext)
{
_hubContext = hubContext;
}
public void SendToAll(string message)
{
_hubContext.Clients.All.InvokeAsync("Send", message);
}
}
I'm using .net core 2.0.0 and SignalR 1.0.0-alpha1-final
Microsoft.AspNet.SignalR.Infrastructure.IConnectionManager is a DI injected service through which you can get the hub context...For example:
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Infrastructure;
using Microsoft.AspNet.Mvc;
public class TestController : Controller
{
private IHubContext testHub;
public TestController(IConnectionManager connectionManager)
{
testHub = connectionManager.GetHubContext<TestHub>();
}
.....
To use the hub in a backgroud service, in addition to controllers, you must use the IHostedService interface and get the hub by DI.
public class MyBackgroundService : IHostedService, IDisposable
{
public static IHubContext<NotifierHub> HubContext;
public MyBackgroundService(IHubContext<NotifierHub> hubContext)
{
HubContext = hubContext;
}
public Task StartAsync(CancellationToken cancellationToken)
{
//TODO: your start logic, some timers, singletons, etc
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
//TODO: your stop logic
return Task.CompletedTask;
}
public void Dispose()
{
}
}
Then you can call your hub from anywhere in your code from HubContext static field:
MyBackgroundService.HubContext.Clients.All.SendAsync("UpdateData", myData).Wait();
Learn more about IHostedService:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1
You can create and start a timer in MyBackgroundService and call the hub in ElapsedEvent.
I needed to be able to access the Hub Context from outside the app request thread - because I was subscribing to NServicebus messages, and needed to be able to trigger a client function when I received a message.
Here's how I got it sorted:
public static IServiceProvider __serviceProvider;
then during startup configuration
app.UseServices(services =>
{
__serviceProvider = new ServiceCollection()
.BuildServiceProvider(CallContextServiceLocator.Locator.ServiceProvider);
});
Then anywhere else in the vNext asp.net application (any other thread)
var manager = Startup.__serviceProvider.GetRequiredService<IConnectionManager>();
var hub = manager.GetHubContext<ChatHub>();
Hope this helps!
I added some code to my Startup.cs to grab reference to the ConnectionManager which you can then use to do a GetHubContext at anytime from anywhere in your code. Similar to Nimo's answer but a little different, maybe simpler.
services.AddSignalR(options =>
{
options.Hubs.EnableDetailedErrors = true;
});
var provider = services.BuildServiceProvider();
//Hold on to the reference to the connectionManager
var connManager = provider.GetService(typeof(IConnectionManager)) as IConnectionManager;
//Use it somewhere else
var hub = connManager.GetHubContext<SignalHub>();
I'm looking at SignalR source code and it seems that IHubContext is registered as a singleton.
Which means you get the same instance whenever you access it.
Which means you can simply save it in a static var and use it from whatever.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHubContext<MyHub> hubContext)
{
_staticVar = hubContext;
}
But be warned - it's an anti-pattern.
I'm starting a web application that contains the following projects:
Booking.Web
Booking.Services
Booking.DataObjects
Booking.Data
I'm using the repository pattern in my data project only. All services will be the same, no matter what happens. However, if a customer wants to use Access, it will use a different data repository than if the customer wants to use SQL Server.
I have StructureMap, and want to be able to do the following:
Web project is unaffected. It's a web forms application that will only know about the services project and the dataobjects project.
When a service is called, it will use StructureMap (by looking up the bootstrapper.cs file) to see which data repository to use.
An example of a services class is the error logging class:
public class ErrorLog : IErrorLog
{
ILogging logger;
public ErrorLog()
{
}
public ErrorLog(ILogging logger)
{
this.logger = logger;
}
public void AddToLog(string errorMessage)
{
try
{
AddToDatabaseLog(errorMessage);
}
catch (Exception ex)
{
AddToFileLog(ex.Message);
}
finally
{
AddToFileLog(errorMessage);
}
}
private void AddToDatabaseLog(string errorMessage)
{
ErrorObject error =
new ErrorObject
{
ErrorDateTime = DateTime.Now,
ErrorMessage = errorMessage
};
logger.Insert(error);
}
private void AddToFileLog(string errorMessage)
{
// TODO: Take this value from the web.config instead of hard coding it
TextWriter writer = new StreamWriter(#"E:\Work\Booking\Booking\Booking.Web\Logs\ErrorLog.txt", true);
writer.WriteLine(DateTime.Now.ToString() + " ---------- " + errorMessage);
writer.Close();
}
}
I want to be able to call this service from my web project, without defining which repository to use for the data access. My boostrapper.cs file in the services project is defined as:
public class Bootstrapper
{
public static void ConfigureStructureMap()
{
ObjectFactory.Initialize(x =>
{
x.AddRegistry(new ServiceRegistry());
}
);
}
public class ServiceRegistry : Registry
{
protected override void configure()
{
ForRequestedType<IErrorLog>().TheDefaultIsConcreteType<Booking.Services.Logging.ErrorLog>();
ForRequestedType<ILogging>().TheDefaultIsConcreteType<SqlServerLoggingProvider>();
}
}
}
What else do I need to get this to work? When I defined a test, the ILogger object was null.
Perhaps some details on how you are calling this code from a test would be useful.
My understanding is that you need to ensure that the ConfigureStructureMap call has been made early in the applications life (e.g. in the Global.asax in a web project).
After that you would be calling for instances of IErrorLog using something like:
IErrorLog log = StructureMap.ObjectFactory.GetNamedInstance<IErrorLog>();