0

I want to fetch first record from AboutUs table and display as a content label.

I have created 4 classes in MVVM pattern.

First is Model class AboutUs.cs

[Table("tblAboutUs")]
    public class AboutUs
    {
        [PrimaryKey, AutoIncrement, NotNull]
        public int IDP { get; set; }
        public string Content { get; set; }
    }

Second is Data Access class

SQLiteAboutUs.cs

public class SQLiteAboutUs
    {
        private static readonly AsyncLock Mutex = new AsyncLock();
        private SQLiteAsyncConnection dbConn;

        public int StatusCode { get; set; }

        public SQLiteAboutUs(ISQLitePlatform sqlitePlatform, string dbPath)
        {

            if (dbConn == null)
            {
                var connectionFunc = new Func<SQLiteConnectionWithLock>(() =>
                    new SQLiteConnectionWithLock
                    (
                        sqlitePlatform,
                        new SQLiteConnectionString(dbPath, storeDateTimeAsTicks: false)
                    ));

                dbConn = new SQLiteAsyncConnection(connectionFunc);
                dbConn.CreateTableAsync<Model.AboutUs>();
            }
        }

        public SQLiteAboutUs()
        {
        }
        public async Task Save(Model.AboutUs content)
        {
            using (await Mutex.LockAsync().ConfigureAwait(false))
            {
                StatusCode = 0;


            await dbConn.InsertAsync(new Model.AboutUs { Content = content.Content });
            StatusCode = 1;
        }

        //For Get first Row from Table
        public async Task<Model.AboutUs> GetAllData()
        {
            return await dbConn.Table<Model.AboutUs>().Where(x => x.IDP == 1).FirstOrDefaultAsync();
        }
    }

Third class ViewModel Class

AboutUsViewModel.cs

public class AboutUsViewModel
    {
        readonly SQLiteAboutUs _db;
        public string AboutUsContent { get; set; }

        //public string AboutUs;  
        public AboutUsViewModel()
        {
            _db = new SQLiteAboutUs();
        }
        public async void FirstRecord()
        {
            Model.AboutUs obj = await _db.GetAllData();
            this.AboutUsContent = obj.Content;
        }
    }

Forth one is Code behind file of my xaml pages.

AboutUs.xaml.cs

public partial class AboutUs : ContentPage
    {
        readonly AboutUsViewModel aboutUsViewModel;
        public AboutUs()
        {
            InitializeComponent();

            aboutUsViewModel = new AboutUsViewModel();

            aboutUsViewModel.FirstRecord();

            lblContent.Text = aboutUsViewModel.AboutUsContent;
        }
    }

I have debug code but problem is In AboutUsViewModel.cs class in FirstRecord Method object can not be set that's why AboutUsContent string property is also not set.

I can't figure out why my debugger directly jump from GetAllData() method in SQLiteAboutUs.cs to label.text in code behind file of view?

RMR
  • 599
  • 3
  • 12
  • 35
  • What is exactly being null here? Are you sure dbConn is not null? It happened to me many times. can you also try to assign a variable like this? var result= await dbConn.Table().Where(x => x.IDP == 1).FirstOrDefaultAsync(); with a try catch maybe, just to make sure if FirstOrDefaultAsync makes the problem or return type makes the problem? – Emil Jul 25 '16 at 10:30

1 Answers1

1

Welcome in the wonderfull world of asynchronicity. I encourage you to read carefully about how await is working: How and When to use `async` and `await`

It is not a blocking call. Thus you create the view AboutUs. It creates the ViewModel AboutUsViewModel. It calls

aboutUsViewModel.FirstRecord();

But does not wait for the call to be complete (dont't forget you marked your FirstRecord function as async...)

So it calls

Model.AboutUs obj = await _db.GetAllData();

And directly return to the caller because of the await operator. That's why it directly jump to

lblContent.Text = aboutUsViewModel.AboutUsContent;

What you would like is Something like

await aboutUsViewModel.FirstRecord();

To wait the call to be complete before going to the next line. But of course you can't do that, because you are in a constructor and you can't have an async constructor. And calling a database (or actually anything that could likely failed) in a constructor is a bad practice anyway.

I would advise you to let only InitializeComponent() in the constructor, and then use Something like the OnDataLoaded() event of your view to perform your async call with a await.

Hope it helps.

Community
  • 1
  • 1
Ouarzy
  • 3,015
  • 1
  • 16
  • 19
  • Thank you for encourage me. Not I see the actual problem. I can't find OnDataLoaded() method in xamarin.forms. So I am using OnAppearing() method. – RMR Jul 25 '16 at 09:20
  • but problem is when GetAllData Method call it gives me error "Object reference not set to the instance of the object". – RMR Jul 25 '16 at 09:22
  • OnAppearing is probably to soon, you should look for another event later, or just call something like a initialize function by hand. – Ouarzy Jul 25 '16 at 11:53