-1

Please help me figure out how to add an image to the database. By clicking the "TakePhoto_Tapped" button with a plus, I add a photo from the camera to AvataView, but by clicking the "Next" button, I need to save this photo to the database. I think this screenshot will make it clearer. The screenshot corresponds to the code. enter image description here

XAML

`<toolkit:AvatarView HeightRequest="400"
                                            WidthRequest="280"
                                            Background="#F9F9F9"
                                            x:Name="currenPhoto"
                                            ImageSource="{Binding Photoprofile}">
                            
                            <Grid>
                                <Image Source="add.png" HeightRequest="26">
                                    <Image.GestureRecognizers>
                                        <TapGestureRecognizer Tapped="TakePhoto_Tapped"/>
                                    </Image.GestureRecognizers>
                                </Image>
                            </Grid>

                        </toolkit:AvatarView>`

Method for working with the stream:

private async void TakePhoto_Tapped(object sender, EventArgs e)
{
FileResult photo = await MediaPicker.Default.CapturePhotoAsync(new           MediaPickerOptions  
        {
            Title = "Select your photo"
        }); 

        if (photo != null)
        {
            var stream = await photo.OpenReadAsync();
            currenPhoto.ImageSource = ImageSource.FromStream(() =>  stream);

        }
    }

This is my view model:

public partial class VMSteptree : ObservableObject
    {
        [ObservableProperty]
        private byte _photoprofile;

        [RelayCommand]

        public static void AddAsync()
        {
           DbMeeto dba = new();

           Auchman auchman = new()
           {
              Photoprofile = new()
           };

            dba.Auchmans.Add(auchman);
        }
    }

Command tied to the button:

 <Button Text="Далее"
         TextColor="White"
         FontSize="Body"
         CornerRadius="20"
         FontAttributes="Bold"
         x:Name="Gotostepfor"
         Background="#4C25D9"
         Clicked="Gotostepfor_Clicked"
         Command="{Binding AddAsyncCommand}">

           <Button.Triggers>
              <EventTrigger Event="">
                   <aninmbtn:BtnAnimation/>
               </EventTrigger>
           </Button.Triggers>

 </Button>

My expectation: I expect that the image downloaded from the stream will be transferred to the vie model through data binding and then saved to it through the command on the button.

But what's really going on: When the command is executed, no record is created in the database. I want to add that i use Postgres. And I also connected the necessary packages and directives using and the rest of the data is saved as it should, except for the images.

8
  • I suspect that you have connection string to the database in your program code file. Which is very bad. Usually it is very bad, because the client can modify the data. But in this case, reading is much much worse. Especially since I understand your language, and I see that you plan to collect and store images of people for identity verification. I know the laws are applied different at different parts of the world (or not applied at all). But people who provide data to you, expect it to be handled with care. Be it for a school project, or the data from all the women on the tea app that leaked Commented Jul 29 at 13:06
  • That's right, my DbContext has a connection string. But the Microsoft Learn instructions give this example. Are you saying I should hide this connection string somewhere? Commented Jul 29 at 14:25
  • You need to write another program, a server application, and you have to transfer this data from the clients (phone app) to this server application, and then the server application has this connection string to connect to the database. And you have to pick a way to transfer, say for example - http requests and answers. This is not "entry level" task. This client server communication is how everything works. Including this site. Commented Jul 30 at 11:29
  • Sure, you can put it in config file. You can encrypt it. You can encode it in .jpg file. It doesn't matter really. If you put it in the config file, it will take me few minutes to get it. If you try some encryption, it will take me few hours (if you obfuscated your code well enough). You can delay it, but not prevent it. This connection string has no place in the client. Commented Jul 31 at 14:49
  • If I understand you correctly, then the client should have a class and a method in it that is responsible for connecting to the "server" application, which opens a connection and access upon request and closes it, and receives data for the connection string each time. Since the server application is not part of the client project, it is located in an unknown location, making it difficult for malicious actors to identify the server. Additionally, the server has its own security measures. Commented Aug 1 at 6:35

2 Answers 2

1

First I want to say that you have business logic inside your page code, and you are directly accessing your controls fields, and at the same time you have more business logic inside your view model and you are using commands and bindings. It should be one or the other.

Now, in this part of your code:

[RelayCommand]

public static void AddAsync()
{
   DbMeeto dba = new();

   Auchman auchman = new()
   {
      Photoprofile = new()
   };

    dba.Auchmans.Add(auchman);
}

Does nothing but adding default record. And your observable byte property does even less.

Approximation how this should look like when using MVVM:

1. Add some property for binding as source for the image view.

ImageSource? ProfileImage => GetStreamImageSource();

2. Add this method that has body like this:

ImageSource? GetStreamImageSource(){
    return ImageSource.FromStream(() => new MemoryStream(PhotoByteArray));
}

3. And your PhotoByteArray is either from your stream when taking the photo:

PhotoByteArray = stream.GetBuffer();

Or from some field in your Photoprofile when loading from database.

4. And after that you can finally modify your command to actually do some work, changing this line:

Photoprofile = new()

To something like:

Photoprofile = new( PhotoByteArray = PhotoByteArray )

Or give up on MVVM entirely, and add even handler on your "Next" button, instead of a command, and add all those properties to the page code file.

But you have to pick one approach and stick to it. Because you split you logic in two, it took me some time, before I could understand what you are trying to do.

Edit: I edit this answer, because I read your comment on the other question:

You can use convert this byte array to Base64 string, and store it in the database as string, and convert it back from Base64 to byte array, when reading it.

This way you wont have to store blobs of bytes at all in your database.

Edit: About your comment for saving the path to the file in local storage.

What this fella suggested to you is very simple:

You have a variable named "photo" that is of type FileResult. Instead of opening this photo as stream, and storing the byte array of the stream buffer inside the database, you simply store the photo.FullPath property. And when you have the file name stored, later you can use the file as image source.

I would like to ask him "What happens when tomorrow you try to read the photo and it isn't there?". (Not to mention that tomorrow also may be the time you decide to store this user data on some server, and then you will have to change the whole logic, not just the transport). Anyway, I am in no position to argue with this suggestion.

Sign up to request clarification or add additional context in comments.

5 Comments

Добрый день, спасибо за помощь, я добавил скриншот и описание чтобы было лучше менять понять, посмотрите пожалуйста.
I have studied the issue in more detail, and the best option is to save the file in the storage and pass its path to the database. I was able to put the file in the storage, but now I don't understand how to pass this path to the database. I would appreciate your assistance in thinking about how to do this. This is the recommendation provided by Gerald Versluis.
@АртурДементьев edited the answer.
But I only want to save media files such as photos and videos in the local storage, while the rest of the data is stored in the database.
@АртурДементьев I know. Say you have some auchman. This auchman has "id = 3", "imagePath = storage/emulated/0/DCIM/pic0251256.jpg", name = "Artur". And another auchman "id = 4" , "imagePath = storage/emulated/0/DCIM/pic1361615.jpg", name "H.A.H". See, the photos files are in the local storage, but the databases hold the id, names and paths to the profile pictures.
0

We can only guess here because your question is not really understandable and there is quite some mixing up here. But I will answer to the best of my understanding and ability.

In the codebehind of your XAML file with the method "TakePhoto_Tapped" your are storing the image information in "currenPhoto". Is this information later stored into a file? You need to find a way to transfer this information to your viewmodel. Typically you should NOT have this image data in your code-behind anyways. The image data should be available in your viewmodel (which should also do the work of "loading" it into your application via datastream). Your view should later on request this image data via databinding. I would create a new RelayCommand and execute the mediapicker from the viewmodel like this:

public partial class VMSteptree : ObservableObject
    {
        [ObservableProperty]
        private Image _loadedImage; //data storage after image was loaded

        [RelayCommand]

        public static void SelectImage()
        {
            //Open MediaPicker to get Filepath
            //Call method to load imagefile from Path
            //Call Database handler to store image file in database
            //Set reference to image file to a [ObservableProperty] that View can bind to
        }
    }

Then connect this command also the gesture recognizer:

<toolkit:AvatarView HeightRequest="400"
                    WidthRequest="280"
                    Background="#F9F9F9"
                    x:Name="currenPhoto"
                    ImageSource="{Binding Photoprofile}">
                            
    <Grid>
      <Image Source="add.png" HeightRequest="26">
        <Image.GestureRecognizers>
          <TapGestureRecognizer Command="{Binding SelectImageCommand"/>
         </Image.GestureRecognizers>
      </Image>
    </Grid>

</toolkit:AvatarView>

Also I want to add, that i doubt that "byte" is a reasonable type for a binding to "ImageSource" property of the AvatarView element. This should be a self containing image type, filepath or URI that directs the view to that image that you want to view. In your case this should probably be something like, request to database to load respective ProfilePhoto for user into memory, and then display it. Or download it to a temporary data folder and provide filepath to view, something in that direction.

This should answer your actual question: We dont know which ORM you are trying to use, but ususally this line: dba.Auchmans.Add(auchman); also requires something like this to be actually executed on the database: DatabaseContext.SaveChanges(); (For Entity Framework as example)

To provide more help you need to explain in more detail with which packages you are working and which types some of the objects are.

A few things to take away:

If you keep the logic at least in the viewmodel you dont need to worry about passing data from the codebehind of the view to the viewmodel.

Usually database access should not happen directly in the viewmodel. Create a service class handling the database interaction for the respective models. If its just small usecase at least have a class like DbAccess where the data is added, saved and loaded.

5 Comments

Good afternoon, Nico, thank you for your response. Please forgive me for being confused. I'm new to this. Here's what I wanted to do: I wanted the user to be able to take a photo and save it (or the path to it) in the database using the following packages: CommunityToolkit.Maui, CommunityToolkit.Mvvm, Npgsql.EntityFrameworkCore.Postgres, and Microsoft.EntityFrameworkCore. If I understand you correctly, I need to change the Image data type and implement the MediaPicker in a command within the View Model?
@АртурДементьев you have to decide what exactly it is you want to do first. Do you want to store the image file? Your database table needs a field for an image file (for example a blob type) You want to store only the filepath? a string field in your table will do.
Тип Blob выдает ошибку при формировании контекста данных: Unable to create a 'DbContext' of type 'DbMeeto'. The exception 'The property 'Auchman.Photoprofile' could not be mapped because it is of type 'Blob', which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.' was thrown while attempting to create an instance. For the different patterns supported at design time, see go.microsoft.com/fwlink/?linkid=851728
Возможно лучше хранить путь к файлу в локальном хранилище, но тогда вопрос как получить этот путь и записать его в базу данных?
Niko, good afternoon. At this point, I have understood the essence of your comments and removed the logic from the View and moved it to the ViewModel. The only thing that doesn't work is to pass a link to the object in local storage, can you help? How can I show you my code? It doesn't fit in the comment))

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.