I got a xamarin forms app, and the problem is when I delete an item from a sqlite table, it looks like all works, the item is deleted from the collection, the grids got updated, etc, but when I restart the app, the item is still there. its like the delete only works in memory but it never got saved in the database.
my code is below
I create an instance called DB in my App constructor
public partial class App
{
static Database database;
public static Database DB
{
get
{
if (database == null)
{
string nombreBD = "MyDataBaseFile.db3";
string _databasePath = Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, nombreBD);
database = new Database(_databasePath);
}
return database;
}
}
................
}
I'm using sqlite with tables created from classes, like this
db = new SQLiteAsyncConnection(dbPath);
db.CreateTableAsync<MyType>().Wait();
where MyType is a class like this
public class MyType
{
[PrimaryKey]
public int Idtable { get; set; }
......
}
I try to delete a row of the table like this:
var x = await App.DB.GetItemAsync<MyType>(obj.Idtable );
int regsDeleted = await App.DB.DeleteItemAsync<MyType>(x);
the GetItemsAsync is basically: await db.FindAsync<T>(id);
public async Task<T> GetItemAsync<T>(int id) where T : new()
{
try
{
return await db.FindAsync<T>(id);
}
catch (System.Exception ex)
{
throw new System.Exception($"Error sqlLite {MethodBase.GetCurrentMethod().Name}: {ex.Message}");
}
}
and the delete method is this:
public async Task<int> DeleteItemAsync<T>(T item) where T : new()
{
try
{
int regsDeleted=await db.DeleteAsync(item);
db.GetConnection().Commit();
return regsDeleted;
}
catch (System.Exception ex)
{
throw new System.Exception($"Error sqlLite {MethodBase.GetCurrentMethod().Name}: {ex.Message}");
}
}
like I said I got no errors and all looks like worked, but when restart the app, the item still there!!
any Idea? something to add in the connection maybe? transactions?... any help will be great
thanks
UPDATE After a lot of test I realize the problem is not the delete. The problem is that every time I run the app from VS to my android device through USB cable, I don't know how or why the database get restored from some backup, that I don´t know when or where was done. Looks like Android have a backup and the "data" of my app and when a new version comes he just restore the data. I read somne that said the Xamarin.Essentials.FileSystem.AppDataDirectory should not be used to save databases, so the question is. where is th right place to save the SQLLite database.Any Idea? My app don't deployed an empty database, my app create the database in the first execution. Does anyone knows how to avoid that restauration of the folder? every time I run the app from VisualStudio ?
The DeleteAsync works without Commit. I make come changes for your code. It works on my side.
I add the PrimaryKey and AutoIncrement attributes to ensure that each Note instance in the SQLite.NET database will have a unique id provided by SQLite.NET.
public class MyType
{
[PrimaryKey, AutoIncrement]
public int Idtable { get; set; }
public string Text { get; set; }
}
The code for the connect to the database, save the record, delete the row and get the all the items.
readonly string _databasePath = Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, "MyDataBaseFile.db3");
SQLiteAsyncConnection database;
public MyType myType { get; set; }
int i = 0;
public Page2()
{
InitializeComponent();
}
private void Connect_Clicked(object sender, EventArgs e)
{
database = new SQLiteAsyncConnection(_databasePath);
database.CreateTableAsync<MyType>().Wait();
}
async void Save_Clicked(object sender, EventArgs e)
{
myType = new MyType() { Text = "Hello" + i };
if (myType.Idtable != 0)
{
// Update an existing note.
await database.UpdateAsync(myType);
i++;
}
else
{
// Save a new note.
await database.InsertAsync(myType);
i++;
}
}
async void Delete_Clicked(object sender, EventArgs e)
{
var x = await database.FindAsync<MyType>(myType.Idtable);
int regsDeleted = await database.DeleteAsync(x);
}
async void Get_Clicked(object sender, EventArgs e)
{
var s = await database.Table<MyType>().ToListAsync();
try
{
var s2 = await database.FindAsync<MyType>(myType.Idtable);
}
catch
{
return;
}
}
}
Please note if i restart the app, there is no myType.Idtable. So i use the try catch to make my project run.
Add four items for the database and detele the last one.
After restart the app, the items:
I had a similar error. Very annoying and couldn't figure it out. After reading this question I have just deleted the db3 file on the android device rerun my app and now it works. I suspect that during development and changing the structure of the class for the table something gets screwed up. Deleting the database db3 (or whatever, sqlite doesn't care) re-created the the tables completely.
So how do you get to the file? (For a Pixel 5 emulator)
I used Android Studio and the DeviceFileExplorer (View>ToolWindows)
But where is it. Well In my app I use
private readonly static string filename = "xxx.db3";
...
database = new Database.Database(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), filename));
and I (eventually) found this located in data>data>(my Application Id)>files
where my ApplicationID is something like uk.co.mydomainname.myappname
I just then deleted the file with a right click delete
(Note: I found sometimes you have to right click the files folder and synchronise to refresh the tree and see the db file)
Hope this helps.
PS I wish for me (.net maui) the documentation explained more clearly the paths and where things get located/placed!!
Related
I am using the listview of the same SQLite Table and I am facing a problem as I add new items or update the previous one it updates the values on the current page but need to reopen the app to update the list on Page B. Do you have any solution?Before Update
Updated Value here
Did'nt update here
so can you help me by giving me a link or video?
For adding and deleting items from list and make changes instantly, you need to use ObservableCollection in System.Collections.ObjectModel. There's no difference whether using SQLite or not; it's about list.
Here is an example:
public partial class Page : ContentPage
{
private ObservableCollection<Client> _client;
public SQLiteAsyncConnection connection;
public Page()
{
InitializeComponent();
}
protected override async void OnAppearing()
{
//initialize database
await connection.CreateTableAsync<CLient>();
var listofclient = await connection.Table<Client>().ToListAsync();
//initialize observablecollection
_client = new ObservableCollection<Client>(listofclients);
//using the list of client as source of listview
clientListveiw.ItemsSource = _client;
base.OnAppearing();
}
//add button
private async void onadd(object sender, EventArgs e)
{
var client = new Client { Name = "jhon"};
await connection.InsertAsync(patient);
_client.Add(patient);
}
}
And about the update, you should use INotifyPropertyChange interface; its a long story, but you can google it for further information.
I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).
Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.
My container is initialized like this:
// Global.asax.cs
public static UnityContainer CurrentUnityContainer { get; set; }
protected void Application_Start()
{
// ...other code...
CurrentUnityContainer = UnityConfig.Initialize();
// misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
UnityConfig.RegisterComponents(CurrentUnityContainer);
}
// UnityConfig.cs
public static UnityContainer Initialize()
{
UnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
return container;
}
This is the code that's called upon logging in:
// UserController.cs
UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);
// UnityConfig.cs
public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
container.AddExtension(new DataAccessDependencies(databaseName));
}
// DataAccessDependencies.cs
public class DataAccessDependencies : UnityContainerExtension
{
private readonly string _databaseName;
public DataAccessDependencies(string databaseName)
{
_databaseName = databaseName;
}
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
}
}
// SessionLifetimeManager.cs
public class SessionLifetimeManager : LifetimeManager
{
private readonly string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new SessionLifetimeManager();
}
}
This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.
Then, as soon as a second user logs in, disaster strikes.
The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.
I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.
Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.
Many thanks.
the issue seems to originate from your registration call, when registering the same type multiple times with unity, the last registration call wins, in this case, that will be data access object for whoever user logs-in last. Unity will take that as the default registration, and will create instances that have the connection to that user's database.
The SessionLifetimeManager is there to make sure you get only one instance of the objects you resolve under one session.
One option to solve this is to use named registration syntax to register the data-access types under a key that maps to the logged-in user (could be the database name), and on the resolve side, retrieve this user key, and use it resolve the corresponding data access implementation for the user
Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.
// DataAccessDependencies.cs
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
return repositoryFactory.GetRepository();
}));
}
// ClientRepositoryFactory.cs
public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
private readonly IConfigurationBuilder _configurationBuilder;
public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
{
_configurationBuilder = configurationBuilder;
}
public RepositoryService<ClientContext> GetRepository()
{
var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
ClientContext ctx = new ClientContext(connectionString);
RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);
return repository;
}
}
// UserData.cs (multiton-singleton-hybrid)
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
I'm new in workflows C #, I want to set up an activity that will be blocked until a new element insert into my database , after I pass to another activity.
From your question I believe you are asking for a way to stop the workflow proceeding until you get a database entry and if you do to continue the workflow.
And if you do not get the entry to not continue the workflow.
This answer uses the Flowchart model of workflows.
A way to do this is to write an Code Activity (also called Custom Activity) that reads your database and determines if the entry has arrived and then sets a bool Out Argument of the activity. This bool should set a Variable in the workflow.
Then after that you add a FlowDecision activity to read the bool Variable.
If true you continue the workflow
If false you add a loop back to your database reading activity.
This solution leaves the workflow running in memory.
There are more sophisticated solutions but as you are new to workflows I have given the most simple.
public sealed class Controller: CodeActivity
{
public OutArgument<String> Item { get; set; }
CodeActivityContext con;
public SqlTableDependency<VacationRequest> _dependency;
private void _dependency_OnChanged(object sender, TableDependency.EventArgs.RecordChangedEventArgs<VacationRequest> even)
{
if (even.ChangeType != ChangeType.None)
{
switch (even.ChangeType)
{
case ChangeType.Update:
try
{
Item.Set(con, "yeééés");// ****Exception
Console.WriteLine("iiiiiiiiiiiiiiii");
//_dependency.Stop();
break;
}
catch (Exception)
{
con.SetValue(Item, "tttt");
break;
}
}
}
}
protected override void Execute(CodeActivityContext context)
{
con = context;
_dependency = new SqlTableDependency<VacationRequest>(ConfigurationManager.ConnectionStrings["DbContext"].ConnectionString, "VacationRequests");
_dependency.OnChanged += _dependency_OnChanged;
_dependency.Start();
//context.SetValue(Item, "test");
}
}
I'm using EF 5 with Web Forms (ASP.NET 4.5), with the "one DbContext instance per request" approach.
But this situation is a bit complicated: I have a multi-step create/edit screen, and I store the current entity in Session, then I manipulate it and in the final step, I commit it to the Database.
Creating a new instance was fine, but I can't for the life of me edit an existing entity... Because it's another request, my original DbContext instance was lost and when I attach it to a new one, I get the An entity object cannot be referenced by multiple instances of IEntityChangeTracker error.
My code is far too complex to post here, but I'll try and summarize it accurately:
My DbContext:
public class AppContext : DbContext
{
// DbSet declarations...
public static AppContext Current {
get { var context = HttpContext.Current.Items["Contexts.AppContext"] as AppContext;
if (context == null)
{
context = new AppContext();
HttpContext.Current.Items["Contexts.AppContext"] = context;
}
return context;
}
}
}
An example of what the page code looks like:
protected void Page_Load(object sender, EventArgs e)
{
int? id = null; // After this, I try to get it from the QueryString, parse it, etc.. Omitted for sake of brevity
// If I have an ID, it means I'm editing...
Session["Product"] = id.HasValue ? new Product() : AppContext.Current.Products.Find(id));
MethodToPopulateFields(); // Internally, it uses the Session variable
}
protected void Step1(){ // through n
// Manipulates the Session["Product"] based on page input...
}
protected void Save(){
var product = Session["Product"] as Product;
if(product.ID == 0)
product = AppContext.Current.Products.Add(product);
// throws an exception:
// AppContext.Current.Entry(product).State = EntityState.Modified;
// this too:
// AppContext.Products.Attach(product);
AppContext.Current.SaveChanges();
}
I know I can get the old entity from the database, update it manually and save, all in the last step, but I really don't want to do that...
Thank you.
Try calling
AppContext.Current.Entry(product).State = EntityState.Detached;
in the first method.
In the herding code podcast 14 someone mentions that stackoverflow displayed the queries that were executed during a request at the bottom of the page.
It sounds like an excellent idea to me. Every time a page loads I want to know what sql statements are executed and also a count of the total number of DB round trips.
Does anyone have a neat solution to this problem?
What do you think is an acceptable number of queries? I was thinking that during development I might have my application throw an exception if more than 30 queries are required to render a page.
EDIT: I think I must not have explained my question clearly. During a HTTP request a web application might execute a dozen or more sql statements. I want to have those statements appended to the bottom of the page, along with a count of the number of statements.
HERE IS MY SOLUTION:
I created a TextWriter class that the DataContext can write to:
public class Logger : StreamWriter
{
public string Buffer { get; private set; }
public int QueryCounter { get; private set; }
public Logger() : base(new MemoryStream())
{}
public override void Write(string value)
{
Buffer += value + "<br/><br/>";
if (!value.StartsWith("--")) QueryCounter++;
}
public override void WriteLine(string value)
{
Buffer += value + "<br/><br/>";
if (!value.StartsWith("--")) QueryCounter++;
}
}
In the DataContext's constructor I setup the logger:
public HeraldDBDataContext()
: base(ConfigurationManager.ConnectionStrings["Herald"].ConnectionString, mappingSource)
{
Log = new Logger();
}
Finally, I use the Application_OnEndRequest event to add the results to the bottom of the page:
protected void Application_OnEndRequest(Object sender, EventArgs e)
{
Logger logger = DataContextFactory.Context.Log as Logger;
Response.Write("Query count : " + logger.QueryCounter);
Response.Write("<br/><br/>");
Response.Write(logger.Buffer);
}
If you put .ToString() to a var query variable you get the sql. You can laso use this in Debug en VS2008. Debug Visualizer
ex:
var query = from p in db.Table
select p;
MessageBox.SHow(query.ToString());
System.IO.StreamWriter httpResponseStreamWriter =
new StreamWriter(HttpContext.Current.Response.OutputStream);
dataContext.Log = httpResponseStreamWriter;
Stick that in your page and you'll get the SQL dumped out on the page. Obviously, I'd wrap that in a little method that you can enable/disable.
I have a post on my blog that covers sending to log files, memory, the debug window or multiple writers.
From Linq in Action
Microsoft has a Query Visualizer tool that can be downloaded separetly from VS 2008. it is at http://weblogs.asp.net/scottgu/archive/2007/07/31/linq-to-sql-debug-visualizer.aspx