Xamarin iOS not finding path for sqlite database - ios

I am working on an iOS app that needs to have a simple sqlite db. This is in a portable class library using Xamarin. In the code I'm attempting to get the connection to the DB, but I'm not sure where I should be placing the database in my project folder nor if the #if __ IOS__ is even working honestly, but I'm using based on the Xamarin docs here: http://bit.ly/1MxSYey
public static SQLiteConnection GetConnection()
{
#if __IOS__
var sqliteFilename = "messages.db";
var docs = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var db = Path.Combine(docs, "..", "Library", sqliteFilename);
return new SQLiteConnection(db);
#endif
return null;
}

IN PCL you should be using interfaces and dependency injection instead using IF directives as in shared solution.
Eg. Xamarin Forms has dependency injection build in (but you can also use another library):
PCL shared library:
public interface ISqlite {
SQLiteConnection GetConnection();
}
iOS specific project:
[assembly: Dependency (typeof (SqliteApple))]
public class SqliteApple : ISqlite
{
public SQLite.SQLiteConnection GetConnection ()
{
var sqliteFilename = "messages.db";
var docs = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var db = Path.Combine(docs, "..", "Library", sqliteFilename);
return new SQLiteConnection(db);
}
}
and then use it like that:
var database = DependencyService.Get<ISqlite>().GetConnection();

Related

How to test database actions in asp.net core integration test?

I am trying to integration test my app.
for example, in my AbController I have PostAb(AbDTO abDTO) method, and I want to test that calling this method will add abDTO to db.
now my test setup:
[SetUp]
public void SetUp()
{
_server = new TestServer(new WebHostBuilder()
.UseEnvironment("testing")
.UseStartup<Startup>());
_client = _server.CreateClient();
}
and my test should look like:
[Test]
public async Task PostAbSanity()
{
await _client.PostAsync("/rest/v1/Ab", new Ab{Id=1});
_context.Abs.find(1).should().NotBeNull();
}
but, how can I inject _context into test? in my app I inject it through constructors, but in tests I cant.
thanks!
I'm assuming you are using SqlLite, so you can edit your SetUp method like this:
[SetUp]
public void SetUp()
{
_server = new TestServer(new WebHostBuilder()
.UseEnvironment("testing")
.UseStartup<Startup>());
_client = _server.CreateClient();
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite("Filename=./blog.db");
_context = new BloggingContext(optionsBuilder.Options);
}
Please let me know if this is useful
While I will concede that integration testing with an in-memory database is the most thorough and safe way to work, my specific situation would make this aspect extremely difficult and time consuming. I am using a hosted SQL development server that I overwrite after a period of tests. I worked for several days and found the below to be the process that gave me the results I was looking for.
DotNet Core 2.1
Hexagonal (Onion) architecture needing to test the business logic written in my Data
Access Layer
Program.CS file I added:
//for integration testing
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureServices(services => services.AddAutofac())
.UseStartup<Startup>();
My Integration Test File:
using Core.Data.Entities.Model;
using Core.Data.Entities.UTIA;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Profile.Data.Repos;
using Profile.Domain.DomainObjects;
using Super2.Web;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace Profile.Test
{
public class EmployeeProfileIntegrationTest : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
public EmployeeProfileIntegrationTest(WebApplicationFactory<Startup> factory)
{
_client = factory.CreateClient();
}
private DBContext GetContext()
{
var options = new DbContextOptionsBuilder<DBContext>()
.UseSqlServer("Server = 'Connection String from appsettings'")
.Options;
var context = new DBContext(options);
return context;
}
[Fact]
public async Task TestChildProtectionEmployeeGetData()
{
//Arrange
var ApplicationUserId = XXXX;
var repo = new EmployeeProfileRepo(GetContext());
//ACT
var sut = await repo.GetChildProtectionHistory(ApplicationUserId);
//Assert
var okResult = sut.Should().BeOfType<List<DomainObject>>().Subject;
okResult.First().ApplicationUserId.Should().Be(XXXX);
}
}
}
While I am injecting my context into a different layer, I would suspect that it would work the same for a controller. I included the startup snippet as this caused me some issues as the testserver was looking for IWebHostBuilder instead of the default Core2.1 IWebHost.
Either way, this worked for me. Hope you can get some help from this.

How to get Sqlite Connection in Xamarin Forms in iOS?

I've created a Xamarin.Forms PCL project and trying to access the local data stored in sqlite database which is working file in Android but not working in iOS. Whenever I'm trying to call the iOS specific code using DependencyService it throws System.NullReferenceException: Object reference not set to an instance of an object.
Here is my calling statement
var db = DependencyService.Get<IDBPath>().GetDBPath();
Here is my iOS specific code for getting Sqlite Connection
using SQLite.Net;
using SQLite.Net.Async;
using SQLite.Net.Platform.XamarinIOS;
using SwachhParyatanApp.iOS;
using System;
using System.IO;
[assembly: Xamarin.Forms.Dependency(typeof(DBPath_iOS))]
namespace SwachhParyatanApp.iOS
{
class DBPath_iOS
{
public SQLiteAsyncConnection GetDBPath()
{
var sqliteFilename = "localData.db";
string folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string libraryPath = Path.Combine(folder, "..", "Library");
var path = Path.Combine(libraryPath, sqliteFilename);
var platform = new SQLitePlatformIOS();
var param = new SQLiteConnectionString(path, false);
var connection = new SQLiteAsyncConnection(() => new SQLiteConnectionWithLock(platform, param));
return connection;
}
}
}
I don't think the calling method is going to reach the iOS specific code because I used the break point in iOS specific code but it never came to the break point and it immediately gives the error. I've also tried going to the exception for details but there is no inner exception and in stacktrace it only points to the line which called the method.
Using SQLite.Net PCL below is a working example of an iOS dependency injection recipient for SQLite. A couple of differences I noticed are your db extension .db instead of .db3 and your 'assembly' header does not implement the full namespace. I am not sure if that matters.
[assembly: Dependency(typeof(NameSpace.iOS.SQLiteUtility.SQLite_iOS))]
namespace NameSpace.iOS.SQLiteUtility
{
class SQLite_iOS : ISQLite
{
public SQLiteConnection GetConnection()
{
try
{
var sqliteFilename = "MyDB.db3";
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
string libraryPath = Path.Combine(documentsPath, "..", "Library"); // Library folder
var path = Path.Combine(libraryPath, sqliteFilename);
var plat = new SQLite.Net.Platform.XamarinIOS.SQLitePlatformIOS();
var conn = new SQLite.Net.SQLiteConnection(plat, path,
SQLite.Net.Interop.SQLiteOpenFlags.ReadWrite |
SQLite.Net.Interop.SQLiteOpenFlags.Create |
SQLite.Net.Interop.SQLiteOpenFlags.FullMutex, true);
return conn;
}
catch (SQLiteException ex)
{
Helpers.Helper_ErrorHandling.SendErrorToServer(ex);
return null;
}
catch (System.Exception ex)
{
Helpers.Helper_ErrorHandling.SendErrorToServer(ex);
return null;
}
}
}
If it must be the async version you may want to look at How to use SQLiteAsyncConnection from the async PCL version of SQLite?

Reusing Registrations with Castle Windsor Installers?

We currently have 2 web applications, one is a front end customer facing application while the other is an administrative backend application. What we noticed is that alot of registration is duplicated between the 2 applications. Example, RavenDb Setup. For example both applications have this code in the asp.net global.asax
container.Register(
Component.For<IDocumentStore>()
.UsingFactoryMethod(x =>
{
var docStore = new DocumentStore { ConnectionStringName = "RavenDB" };
docStore.Initialize();
return docStore;
}).LifestyleSingleton()
);
We refactored this code out into an installer and placed it in an assembly called CastleWindsor.RavenDbInstaller that can be referenced and reused by both applications.
public class RavenDbInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IDocumentStore>()
.UsingFactoryMethod(x =>
{
var docStore = new DocumentStore { ConnectionStringName = "RavenDB" };
docStore.Initialize();
return docStore;
}).LifestyleSingleton()
);
}
}
All is fine but is this the recommended approach to reusing registration logic between applications??
Also, What happens when an installer in a seperate assembly has a dependency on another class. How should this be handled. For example, What if my ravendb connectionstring should not be hardcoded and should be attached to a ApplicationConfiguration class. How do i deal with this dependency in regards to my CastleWindsor.RavenDbInstaller Assembly and the installer class it contains?
public class RavenDbInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IDocumentStore>()
.UsingFactoryMethod((c, y) =>
{
var connectionStringName = c.Resolve<IApplicationConfiguration>().ConnectionStringName; // <---- How do i deal with this dependency?
var docStore = new DocumentStore { ConnectionStringName = connectionStringName };
docStore.Initialize();
return docStore;
}).LifestyleSingleton()
);
}
}
if you want to use the same implemetation for IApplicationConfiguration for backend and frontend then it makes sense to place it in CastleWindsor.RavenDbInstaller Assembly. Otherwise not.
Cheers.
Personally I would leave the installer into the assembly where the components it references are. You will not resolve a component that you don't install, or resolve a component you don't use, so separating both elements doesn't really make sense to me. Both application should reference a business component that uses RavenDB and also contains install elements.
Regarding your reference to the connection string, I would recommend adding a custom resolver to your castle installation that will be able to resolve your connection string from whatever source you need: this lets you resolve the connection string in the installer without having a reference on external components. Another bonus is that if you don't register a way to resolve the connection string you don't have to handle errors, the resolution will fail explicitly with the name of the connection string.
public class ConnectionStringResolver : ISubDependencyResolver
{
public bool CanResolve(Castle.MicroKernel.Context.CreationContext context, ISubDependencyResolver contextHandlerResolver, Castle.Core.ComponentModel model, Castle.Core.DependencyModel dependency)
{
var connectionStringInformation = ConfigurationManager.ConnectionStrings[dependency.DependencyKey];
return connectionStringInformation != null
&& !string.IsNullOrEmpty(connectionStringInformation.ConnectionString)
&&
TypeDescriptor.GetConverter(dependency.TargetType)
.CanConvertFrom(typeof(string));
}
public object Resolve(Castle.MicroKernel.Context.CreationContext context, ISubDependencyResolver contextHandlerResolver, Castle.Core.ComponentModel model, Castle.Core.DependencyModel dependency)
{
return TypeDescriptor
.GetConverter(dependency.TargetType)
.ConvertFrom(ConfigurationManager.ConnectionStrings[dependency.DependencyKey].ConnectionString);
}
}
Then your installer would be:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IDocumentStore>()
.UsingFactoryMethod((c, y) =>
{
var connectionStringName = container.Resolve("TheConnectionString");
var docStore = new DocumentStore { ConnectionStringName = connectionStringName };
docStore.Initialize();
return docStore;
}).LifestyleSingleton()
);
}

Web API & Simple Injector - Problem resolving Controller that was loaded from external dll

I am building a Web API using MVC4 Web API and Simple Injector that should expose a variety of CRUD and query operations. The reason for using IOC in my case is that we are a dev shop and I need to be able to let customers build their own web api controllers to expose the data they need to expose need from our system. Consequently, I was hoping to design my solution in a way that allowed me to dogfood my own product by making all the controllers, both ours and our customers', external and loadable through IOC.
The website does not have any reference to the library but the library contains controllers that I want to use in the website. I have the code finding the dll plugin and loading the controller type but when I try to navigate to the route that it would represent It says it can't find it.
i.e. if I try to navigate to /api/Test1Api I should see the text "hello world"
My problem here is that although I have loaded my controller type, I am unable to translate that into a route that the website says is there.
Here is how I register the container
[assembly: WebActivator.PostApplicationStartMethod(typeof(Spike.Web.Api.App_Start.SimpleInjectorInitializer), "Initialize")]
public static class SimpleInjectorInitializer
{
public static void Initialize()
{
// Create the IOC container.
var container = new Container();
InitializeContainer(container);
container.RegisterMvcAttributeFilterProvider();
// Verify the container configuration
container.Verify();
// Register the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
//DependencyResolver.SetResolver(new SimpleInjectorWebApiDependencyResolver(container));
}
private static void InitializeContainer(Container container)
{
var appPath = AppDomain.CurrentDomain.BaseDirectory;
string[] files = Directory.GetFiles(appPath + "\\bin\\Plugins", "*.dll",
SearchOption.AllDirectories);
var assemblies = files.Select(Assembly.LoadFile);
// register Web API controllers
var apiControllerTypes =
from assembly in assemblies
where !assembly.IsDynamic
from type in assembly.GetExportedTypes()
where typeof(IHttpController).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
where type.Name.EndsWith("Controller", StringComparison.Ordinal)
select type;
// register MVC controllers
var mvcControllerTypes =
from assembly in assemblies
where !assembly.IsDynamic
from type in assembly.GetExportedTypes()
where typeof(IController).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
where type.Name.EndsWith("Controller", StringComparison.Ordinal)
select type;
foreach (var controllerType in apiControllerTypes)
{
container.Register(controllerType);
}
foreach (var controllerType in mvcControllerTypes)
{
container.Register(controllerType);
}
}
}
I figured it out!
So I created a new class in my Web Api call CustomAssembliesResolver that inherits from DefaultAssembliesResolver. Essentially I add my assembly to the list of assemblies that are parsed when looking for controllers. I still have the code that uses Simple Injector for the DI portion of the solution.
public class CustomAssembliesResolver : DefaultAssembliesResolver
{
public override ICollection<Assembly> GetAssemblies()
{
var appPath = AppDomain.CurrentDomain.BaseDirectory;
var baseAssemblies = base.GetAssemblies();
var assemblies = new List<Assembly>(baseAssemblies);
var files = Directory.GetFiles(appPath + "\\bin\\Plugins", "*.dll",
SearchOption.AllDirectories);
var customAssemblies = files.Select(Assembly.LoadFile);
// register Web API controllers
var apiControllerAssemblies =
from assembly in customAssemblies
where !assembly.IsDynamic
from type in assembly.GetExportedTypes()
where typeof(IHttpController).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
where type.Name.EndsWith("Controller", StringComparison.Ordinal)
select assembly;
foreach (var assembly in apiControllerAssemblies)
{
baseAssemblies.Add(assembly);
}
return assemblies;
}
}
I also added the following line to the beginning of the App_Start in the Global.asax.cs
GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());
Hope this helps someone!

Profiling Entity Framework With MvcMiniProfiler

I've got an Asp.net Mvc 3 Application which is now using the MvcMiniProfiler. I'm also using the Entity Framework to access my database, and I'd like to enable the profiler to work with the entity model. So far I've created the Context factory below:
internal class ProfiledContextFactory : IContextFactory
{
public ModelContainer GetContext()
{
var conn = ProfiledDbConnection.Get(GetConnection());
return ObjectContextUtils.CreateObjectContext<ModelContainer>(conn);
}
private static EntityConnection GetConnection()
{
return new EntityConnection(ConfigurationManager.ConnectionStrings["ModelContainer"].ConnectionString);
}
}
When I run the above code, which is called by my repository layer when I start a unit of work, it gets stuck in an infite loop when calling CreateDbCommandDefinition in the MvcMiniProfiler.ProfiledDbServices class.
Any clues what I'm doing wrong?
The problem was my GetConnection was returning the EntityConnection, not the SqlConnection within the EntityConnection. I've now modified my code so that it reads:
private static SqlConnection GetConnection()
{
var connStr = ConfigurationManager.ConnectionStrings["ModelContainer"].ConnectionString;
var entityConnStr = new EntityConnectionStringBuilder(connStr);
return new SqlConnection(entityConnStr.ProviderConnectionString);
}
And it works fine.
I discovered this while looking at this question: Using mvc-mini-profiler with EF 4.0 and Ninject

Resources