I'm writing a UWP version of a Windows Phone 8.1 app of mine. This app uses a read-only SQLite database and I'm trying to use Entity Framework 7 on the UWP app to access it.
I can read the database just fine in the app's initial page. However, once I tap one of the rows to navigate to that row's detail page (where I fetch some child records), I get an exception saying there are no database providers configured, even though the code to access the database is the same as in the page that's working. The weirder part is that if I step through the code using the debugger, it works.
I'm also using Template10 on my app, but it does not make a difference whether I put the DB access code in a Template10 ViewModel or straight in the code-behind for the view.
This is the OnConfiguring method for my DbContext class:
protected async override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var assetsFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Assets");
optionsBuilder.UseSqlite($"Filename={Path.Combine(assetsFolder.Path, "joinbus.db")}");
}
This is how I access the database on the app's initial page (which always works):
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
using (var db = new JoinbusContext())
{
Lines = await db.Lines.ToListAsync();
}
}
This is how I access the database on the detail page (which only works when debugging):
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
var line = parameter as Line;
using (var db = new JoinbusContext())
{
var origins = await db.LineOrigins.Where(lo => lo.Line.Id == line.Id)
.Include(lo => lo.Origin).ToListAsync();
LineOrigins = origins;
}
}
The exception thrown is System.InvalidOperationException with the message "No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services."
Related
Realms.Exception.RealmInvalidTransactionException: Cannot modify managed objects outside of a write transaction
I was using Ream .Net SDK 10.13 and recently updated it to 10.19.0. After, the update I am frequently getting the above error.
Here is an example of how I am using it in my Xamarin Forms project
public class TestClass: RealmObject
{
//Class properties are defined
// as per the realm docs, proper attributes
//are added for Kets and mapping
}
Now in some class/view models, where I have an instance of the TestClass injected through the constructor
public async Task<SomeUserDefinedType> SomeMethod(TestClass item){
var realm = Realms.Realm.GetInstance();
await realm.WriteAsync(async()=>{
// setting property of the TestClass
item.SomeProperty = "Some Value";
});
return <Instance of SomeUserDefinedType>;
}
The above method call gives an exception. Above code is modified as per the new version.
The below code was as per the older version which was working fine but it started giving the same exception after the update
public async Task<SomeUserDefinedType> SomeMethod(TestClass item){
await item.WriteAsync(async i =>{
// setting property of the TestClass
i.SomeProperty = "Some Value";
});
return <Instance of SomeUserDefinedType>;
}
I tried many ways to resolve this issue but none is working.
Any Suggestions/Help will be highly appreciated.
I am learning about ASP.NET Core 3 and have built a basic application. I am looking run integration tests to assert calls to the controllers read/write from the database correctly. To avoid having to rely on the actual database I am looking at using EF Core's in-memory database. I have been following this article as my main guide.
The problem I have is that I am struggling to ensure each separate integration test uses a fresh database context.
Initially, I encountered errors calling my database seed method more than once (the second and subsequent calls failed to add a duplicate primary key - essentially it was using the same context).
From looking at various blogs, tutorial and other questions here, I worked around this by instantiating the in-memory database with a unique name (using Guid.NewGuid()). This should have solved my problem. However, this gave me a different issue. The database seed method was correctly called at each test initialisation, however when I then called a controller action the dependency injection instantiated a new database context, meaning that no seed data was present!
I seem to be going in circles either only being able to call seed data once, and only being able to have a single test, or having more than one test but with no seed data!
I have experimented with the scope lifetimes for the DbContext service, setting this to transient/scoped/singleton, but with seemingly no difference in results.
The only way I have managed to get this to work is to add a call to db.Database.EnsureDeleted() before the call to db.Database.EnsureCreated() in the seed method, but this seems like a massive hack and doesn't feel right.
Posted below is my utilities class to set up the in-memory database for the tests, and a test class. Hopefully this is sufficient, as I feel this post is long enough as it is, but the actual controller / startup class can be posted if necessary (though they are fairly vanilla).
Any help much appreciated.
Utilities class to set up the in-memory database
using CompetitionStats.Entities;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
namespace CompetitionStatsUnitTests
{
class Utilities
{
internal class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Remove the app's ApplicationDbContext registration.
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<CompetitionStatsContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Add ApplicationDbContext using an in-memory database for testing.
services.AddDbContext<CompetitionStatsContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
// Build the service provider.
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database context (ApplicationDbContext).
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<CompetitionStatsContext>();
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
db.Database.EnsureDeleted(); // feels hacky - don't think this is good practice, but does achieve my intention
db.Database.EnsureCreated();
try
{
InitializeDbForTests(db);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {Message}}", ex.Message);
}
}
});
}
private static void InitializeDbForTests(CompetitionStatsContext db)
{
db.Teams.Add(new CompetitionStats.Models.TeamDTO
{
Id = new Guid("3b477978-f280-11e9-8490-a8667f2f93c4"),
Name = "Arsenal"
});
db.SaveChanges();
}
}
}
}
Test class
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Net.Http;
using System.Threading.Tasks;
namespace CompetitionStatsUnitTests.ControllerUnitTests
{
[TestClass]
public class TeamControllerTest
{
private HttpClient _testClient;
[TestInitialize]
public void Initialize()
{
var factory = new Utilities.CustomWebApplicationFactory<CompetitionStats.Startup>();
this._testClient = factory.CreateClient();
}
[TestMethod]
public async Task TeamController_GetTeam_Returns_Team()
{
var actualResponse = await this._testClient.GetStringAsync("api/teams/3b477978-f280-11e9-8490-a8667f2f93c4");
var expectedResponse = #"{""id"":""3b477978-f280-11e9-8490-a8667f2f93c4"",""name"":""Arsenal""}";
Assert.AreEqual(expectedResponse, actualResponse);
}
[TestMethod]
public async Task TeamController_PostTeam_Adds_Team()
{
var content = new StringContent(#"{""Name"": ""Liverpool FC""}", System.Text.Encoding.UTF8, "application/json");
var response = await this._testClient.PostAsync("api/teams/", content);
Assert.AreEqual(response.StatusCode, System.Net.HttpStatusCode.Created);
}
}
}
options.UseInMemoryDatabase("InMemoryDbForTesting");
This creates/uses a database with the name “MyDatabase”. If UseInMemoryDatabase is called again with the same name, then the same in-memory database will be used, allowing it to be shared by multiple context instances.
So you will get the error like{"An item with the same key has already been added. Key: 3b477978-f280-11e9-8490-a8667f2f93c4"} when you add data with the same Id repeatedly
You could add a judgment to the initialization method :
private static void InitializeDbForTests(CompetitionStatsContext db)
{
if (!db.Teams.Any())
{
db.Teams.Add(new Team
{
Id = new Guid("3b477978-f280-11e9-8490-a8667f2f93c4"),
Name = "Arsenal"
});
}
db.SaveChanges();
}
You could also refer to the suggestions provided by Grant says adios SE in this thread
I am using a postgres Database through a library called Marten with a .NET app, I have a custom IUserLoginStore which manages retrieving the user and its roles. This seems to be working correctly but I am have an issue with setting up authorization.
I am using authentication through google and it is working fine:
var info = await _signInManager.GetExternalLoginInfoAsync();
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
This action throws an access denied issue:
[HttpPost()]
[Authorize(Roles = "Admin")]
public JsonResult SubmitArticle([FromBody] ArticleInputModel input) {...}
I have dug into the Authorization code and the problem seems to be with the default ClaimsPrincipal code:
public virtual bool IsInRole(string role)
{
return false;
}
Should I implement my own version of ClaimsPrinciple and override the IsInRole, and if I do how do I get this back into the app?
private static void ConfigureSecurity(IServiceCollection services)
{
services.AddIdentity<User, Role>()
.AddUserValidator<UserValidator>()
.AddUserStore<MartenUserStore>()
.AddRoleStore<MartenRoleStore>()
.AddDefaultTokenProviders();
}
Alright figured it out after a large amount of digging, In my case the MartenRoleStore was implementing IUserLoginStore it also needed to implement IUserRoleStore which has GetRolesAsync and IsInRoleAsync. (This is very important it has to be the exact same class you used for .AddUserStore<>();)
This is the code that I found that caused the issue:
https://github.com/aspnet/Identity/blob/master/src/Microsoft.AspNetCore.Identity/UserManager.cs#L258
This is what makes it work:
https://github.com/aspnet/Identity/blob/master/src/Microsoft.AspNetCore.Identity/UserClaimsPrincipalFactory.cs#L96
We have an ASP.Net 4 / MVC 3 hybrid web application which uses NInject 3 and (Fluent) NHibernate 3.2. DB is SQL Server 2008 R2. Server is 6-core 28 GB Windows 2008 64-bit server.
Our customer has recently started testing the site using a spidering tool. As soon as the site experiences the load produced by the spider, our log starts to fill up with exceptions.
We see a variety of errors from NHibernate, including some of the following:
NHibernate.TransactionException: Commit failed with SQL exception ---> System.Data.SqlClient.SqlException: The transaction operation cannot be performed because there are pending requests working on this transaction.
System.Data.SqlClient.SqlException (0x80131904): The server failed to resume the transaction. Desc:410000050f. The transaction active in this session has been committed or aborted by another session.
System.NullReferenceException: Object reference not set to an instance of an object. at System.Data.SqlClient.SqlInternalTransaction.GetServerTransactionLevel()....
NHibernate.Exceptions.GenericADOException: could not execute native bulk manipulation query:exec [Stats.InsertListingStatsList] #ListingStats =:ListingStats[SQL: exec [Stats.InsertListingStatsList] #ListingStats =#p0] ---> System.Data.SqlClient.SqlException: New request is not allowed to start because it should come with valid transaction descriptor.
to give just four examples. All have a similar flavour - they all seem to relate to the management of transactions by ADO.Net as the substrate of NHibernate.
Now, some details of our NH implementation:
SessionFactory is static;
SessionFactory uses AdoNetTransactionFactory;
ISession is in request scope, and stored in the HttpContext.Items collection;
Repositories are also in request scope;
We are now using config.CurrentSessionContext();
Each call to our generic repository uses a transaction
Here are two methods from our repository.
public T GetById<T>(int id)
{
using (var t = Session.BeginTransaction())
{
var entity = Session.Get<T>(id);
t.Commit();
return entity;
}
}
public void Add<T>(T entity)
{
using (var t = Session.BeginTransaction())
{
Session.Save(entity);
t.Commit();
}
}
My question is simple: what is going wrong? What is causing these apparent conflicts between transactions, or between the various data-related operations that our domain instigates as it de/hydrates our domain?
UPDATE: here is our full configuration:
public FluentConfiguration BuildConfiguration(string connectionString)
{
var sqlConfig = MsSqlConfiguration.MsSql2008.ConnectionString(connectionString).AdoNetBatchSize(30);
var config = Fluently.Configure().Database(sqlConfig);
var entityMapping = AutoMap.AssemblyOf<User>(new AutomappingConfiguration())
.UseOverridesFromAssemblyOf<UserMappingOverride>()
.AddMappingsFromAssemblyOf<TableNamingConvention>()
.Conventions.AddFromAssemblyOf<TableNamingConvention>();
var cqrsMapping = AutoMap.AssemblyOf<AdvertView>(new QueryAutomappingConfiguration())
.UseOverridesFromAssemblyOf<AdvertViewMappingOverride>();
config.Mappings(c => c.AutoMappings.Add(entityMapping));
config.Mappings(c => c.AutoMappings.Add(cqrsMapping));
config.Mappings(c => c.HbmMappings.AddFromAssemblyOf<AdvertView>());
config.ExposeConfiguration(c => c.SetProperty(Environment.TransactionStrategy, typeof(AdoNetTransactionFactory).FullName));
config.CurrentSessionContext<WebSessionContext>();
return config;
}
More code for you guys and gals. Here is the relevant section of our IoC Container configuration.
var domainEntityBootstrapper = new DomainEntitySessionBootStrapper("Domain", "NHibernate.ISession.Domain", _enableLucine, HttpContextItemsProvider);
Bind<ISessionFactory>().ToMethod(domainEntityBootstrapper.CreateSessionFactory).InSingletonScope().Named(domainEntityBootstrapper.Name);
Bind<ISession>().ToMethod(domainEntityBootstrapper.GetSession).InRequestScope();
var queryBootstrapper = new QueryEntitySessionBootStrapper("Query", "NHibernate.ISession.Query", HttpContextItemsProvider);
Bind<ISessionFactory>().ToMethod(queryBootstrapper.CreateSessionFactory).InSingletonScope().Named(queryBootstrapper.Name);
Bind<ISession>().ToMethod(queryBootstrapper.GetSession).WhenInjectedInto(typeof (QueryExecutor)).InRequestScope();
and here is the code from the GetSession() method of the base class for these SessionBootstrappers (please note that the CreateSessionFactory method calls the BuildConfiguration method above and then calls BuildSessionFactory()).
public virtual ISession GetSession(IContext context)
{
var items = GetHttpContextItems();
var session = default(ISession);
var sessionExists = items.Contains(SessionKey);
if (!sessionExists)
{
session = context.Kernel.Get<ISessionFactory>(Name).OpenSession();
items.Add(SessionKey, session);
}
else
{
session = (ISession)items[SessionKey];
}
return session;
}
// a Func which serves access to the HttpContext.Current.Items collection
private Func<IDictionary> GetHttpContextItems { get; set; }
Please note that we use two sessions, one for ordinary domain de/hydration and one for CQRS, hence the pair of bindings in the Container.
The error messages indicate that you are not managing transactions correctly. I think the root cause is that you are handling transactions in the repository methods which in my opinion is a very poor design. Your repositories should have an ISession injected into their constructors, and your controllers should have any repositories they are dependent upon injected into their constructors. It's easy to wire this all up with Ninject. With this approach you can use transaction-per-request or (much better imo) manage the transaction in the action methods.
Here's how I'm setting up NHibernate with Ninject in NinjectWebCommon. The root cause of your problem may be that you are binding the ISession in request scope and storing it in HttpContext, which is unnecessary. I am also confused why you have two sets of bindings for Domain and Query.
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISessionFactory>().ToProvider(new SessionFactoryProvider()).InSingletonScope();
kernel.Bind<ISession>().ToProvider(new SessionProvider()).InRequestScope();
}
private class SessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
// create and configure the session factory
// I have a utility class to do this so the code isn't shown
return nhibernateHelper.BuildSessionFactory();
}
}
private class SessionProvider : Provider<ISession>
{
protected override ISession CreateInstance(IContext context)
{
var sessionFactory = context.Kernel.Get<ISessionFactory>();
var session = sessionFactory.OpenSession();
session.FlushMode = FlushMode.Commit;
return session;
}
}
A sample controller action using a transaction. Managing transactions outside of the repositories is important for several reasons:
Allows multiple repositories to participate in a transaction
Allows the controller to set the transaction boundaries (unit of work)
Allows lazy loads to occur in the transaction
Transactions are needed for read operations if second level caching is used. Even if it caching isn't used I think it's a best practice
public ActionResult EditDocuments(int id, string name)
{
using (var txn = _session.BeginTransaction())
{
var summary = _characterizationRepository
.GetCharacterization(id)
.AsCharacterizationSummaryView()
.ToFutureValue();
var documents = _characterizationRepository
.GetCharacterization(id)
.SelectMany(c => c.Documents)
.OrderBy(d => d.FileName)
.AsDocumentSelectView(true)
.ToFuture();
if (summary.Value == null)
{
throw new NotFoundException(_characterizationRepository.ManualId, "Characterization", id);
}
CheckSlug(name, summary.Value.Title);
var model = new DocumentSectionEditView()
{
CharacterizationSummary = summary.Value,
Documents = documents.ToArray()
};
txn.Commit();
return View(model);
}
}
It seems you are using the wrong context manager, check if you are using the WebSessionContext. This context manager will bind your session to the httpcontext of the current call instead of the thread. What happens now under load (the spider), when you are using the ThreadStaticSessionContext, session will 'jump' to an other 'call'.
HI,
I am implementing a custom role provider in my nhibernate application
I have a repository that I call whenever I want to access the nhibernate session.
So when my role provider initializes itself
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) {
base.Initialize(name, config);
Repository = new Repository();
}
Then I override
public override string[] GetRolesForUser(string username) {
var users = Repository.QueryAll<Users>();
//I then filter and so on
}
But when this function is called I always get an error that the NHibernate session is closed.
I debugged the nhibernate source code and it turns out that the session here has a different guid that the session in my controllers(I am using ASP.NET MVC also).
And this particular session is closed by the time I get here.
I don't know who closes it. I know it is started when the application starts and only then.
Does anyone have any idea what I am doing wrong?
I want to still use Nhibernate in this provider but not get the error any more.
Thank you
I had what appears to be the exact same problem. Don't forget that Role and Membership providers are only instantiated once and exist for the lifetime of the application. If you're using a Session per Web request pattern, the ISession will be closed after the first request and then any reference to an ISession internal to the provider will likely be null for subsequent requests.
You can get around this by injecting a reference to the ISessionFactory and calling GetCurrentSession, instead of directly holding a reference to an ISession.
This is how I evetually fixed it.
in my repository class I had this:
public Repository()
{
this.Session = SessionManager.GetCurrentSession();
}
I deleted the constructor entirely
I put in this instead:
private ISession _session;
protected ISession Session
{
get
{
if (_session == null)
{
_session = SessionManager.GetCurrentSession();
}
return _session;
}
}