Maui: [Spec] Microsoft.Extensions.Hosting dan/atau Microsoft.Extensions.DependencyInjection

Dibuat pada 18 Mei 2020  ·  21Komentar  ·  Sumber: dotnet/maui

Panggang fitur Microsoft.Extensions.Hosting ke .NET MAUI

https://montemagno.com/add-asp-net-cores-dependency-injection-into-xamarin-apps-with-hostbuilder/

Manfaatkan struktur Host Generik yang disetel dengan .netcore 3.0 untuk menginisialisasi aplikasi .NET MAUI.

Ini akan memberi pengguna pengalaman yang sangat Microsoft dan akan membawa banyak kode kami sesuai dengan inti ASP.NET

Rooting IServiceProvider secara mendalam ke .NET MAUI

Ganti semua instance Activator.CreateInstance(Type) dengan IServiceProvider.Get()

Misalnya jika kita mengubah ini di ElementTemplate
https://github.com/dotnet/maui/blob/1a380f3c1ddd9ba76d1146bb9f806a6ed150d486/Xamarin.Forms.Core/ElementTemplate.cs#L26

Kemudian DataTemplate apa pun yang ditentukan melalui tipe akan memanfaatkan pembuatannya melalui injeksi konstruktor.

Contoh

```C#
Host.CreateDefaultBuilder()
.ConfigureHostConfiguration(c =>
{
c.AddCommandLine(string baru[] { $"ContentRoot={FileSystem.AppDataDirectory}" });
c.AddJsonFile(fullConfig);
})
.ConfigureServices((c, x) =>
{
nativeConfigureServices(c, x);
KonfigurasikanLayanan(c, x);
})
.ConfigureLogging(l => l.AddConsole(o =>
{
o.DisableColors = true;
}))
.Membangun();

static void ConfigureServices (HostBuilderContext ctx, layanan IServiceCollection)
{
jika (ctx.HostingEnvironment.IsDevelopment())
{
var world = ctx.Configuration["Halo"];
}

services.AddHttpClient();
services.AddTransient<IMainViewModel, MainViewModel>();
services.AddTransient<MainPage>();
services.AddSingleton<App>();

}

### Shell Examples

Shell is already string based and just uses types to create everything so we can easily hook into DataTemplates and provide ServiceCollection extensions 

```C#
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
     services.RegisterRoute(typeof(MainPage));
     services.RegisterRoute(typeof(SecondPage));
}

Jika semua DataTemplates terhubung melalui pengguna IServiceProvider dapat menentukan Antarmuka pada DataTemplates

<ShellContent ContentTemplate="{DataTemplate view:MainPage}"/ShellContent>
<ShellContent ContentTemplate="{DataTemplate view:ISecondPage}"></ShellContent>

Dipanggang dalam injeksi konstruktor

```C#
Aplikasi kelas publik
{
Aplikasi publik()
{
Inisialisasi Komponen();
Halaman Utama = ServiceProvider.GetService();
}
}
Halaman Utama kelas parsial publik : Halaman Konten
{
Halaman Utama publik (IMainViewModel viewModel)
{
Inisialisasi Komponen();
BindingContext = viewModel;
}
}

MainViewModel kelas publik
{
MainViewModel publik (ILoggerlogger, IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient();
logger.LogCritical("Selalu masuk!");
Halo = "Halo dari IoC";
}
}

### This will allow Shell to also have baked in Constructor Injection

```C#
Routing.RegisterRoute("MainPage", MainPage)

GotoAsync("MainPage") // this will use the ServiceProvider to create the type

Semua ContentTemplates yang ditentukan sebagai bagian dari Shell akan dibuat melalui IServiceProvider

    <ShellContent
        x:Name="login"
        ContentTemplate="{DataTemplate MainPage}"
        Route="login" />

Detail Implementasi untuk dipertimbangkan

Gunakan Microsoft.Build untuk memfasilitasi jalur startup

Tarik fitur host untuk mengartikulasikan lokasi startup tertentu di mana segala sesuatunya terdaftar
https://montemagno.com/add-asp-net-cores-dependency-injection-into-xamarin-apps-with-hostbuilder/

Ini memiliki manfaat membiarkan kita mengikat ke dalam implementasi wadah IoC yang sudah bekerja melawan asp.net core

Kelebihan: Ini memberi pengembang .NET pengalaman yang konsisten.
Kekurangan: Performa? Apakah ini berlebihan untuk seluler?

Opsi wadah DI

Menghentikan DependencyService demi Microsoft.Extensions.DependencyInjection

Xamarin.Forms saat ini memiliki layanan ketergantungan rumahan yang sangat sederhana yang tidak datang dengan banyak fitur. Menumbuhkan fitur layanan ini di hadapan opsi yang sudah tersedia tidak masuk akal. Untuk menyelaraskan diri kita lebih tepat dengan Platform Microsoft lainnya, kita dapat beralih ke wadah di dalam Microsoft.Extensions.DependencyInjection .

Pendaftaran otomatis dari DependencyService akan diikat ke registrar baru. Jika pengguna telah memilih registrar untuk melakukan pemindaian rakitan maka ini akan memicu DependencyService untuk memindai atribut tingkat rakitan

Salah satu peringatan menggunakan wadah ini adalah bahwa jenis tidak dapat didaftarkan dengan cepat setelah aplikasi dimulai. Anda hanya dapat mendaftarkan jenis sebagai bagian dari proses startup dan kemudian setelah IServicePRovider dibangun, itu saja. Pendaftaran ditutup untuk bisnis

Kelebihan: Ini adalah wadah berfitur lengkap
Kekurangan: Performa?

Konversikan DependencyService kami untuk menggunakan IServiceCollection sebagai wadah internal dan implementasikan IServiceProvider

Ini akan memungkinkan kami untuk menggunakan wadah tanpa fitur yang sangat ramping jika orang hanya menginginkan kinerja terbaik. Kami mungkin dapat menggunakan ini sebagai default dan kemudian orang dapat memilih yang lebih berfitur jika mereka mau.

```C#
DependencyService kelas publik : IServiceProvider
{
}

ServiceCollectionExtensions publik statis
{
Buat DependencyService publik statis (IServiceCollection ini);
}
```

Pertimbangan

Apakah ini secara keseluruhan berguna untuk pengalaman aplikasi pengguna baru? Apakah kita benar-benar ingin menambahkan overhead untuk memahami loop startup host build untuk pengguna baru? Mungkin akan berguna untuk hanya memiliki pengaturan default untuk semua ini yang hanya menggunakan Init dan kemudian pengguna baru dapat dengan mudah melakukan apa yang mereka butuhkan tanpa harus mengatur file pengaturan/configureservices/etc..

Pertunjukan

Dalam pengujian saya, pengujian terbatas dibutuhkan sekitar 25 ms untuk bit Microsoft Hosting untuk memulai. Kami mungkin ingin menyelam lebih dalam ke 25 md tersebut untuk melihat apakah kami dapat menyiasatinya atau jika biaya itu sudah menjadi bagian dari biaya awal yang berbeda yang sudah akan kami keluarkan

Kompatibilitas terbalik

  • DependencyService saat ini memindai atribut tingkat perakitan yang kemungkinan besar akan kami alihkan untuk ikut serta dalam .NET MAUI. Defaultnya akan mengharuskan Anda untuk mendaftarkan sesuatu melalui Koleksi Layanan secara eksplisit

Kesulitan: Sedang/Besar

Pekerjaan yang ada:
https://github.com/xamarin/Xamarin.Forms/pull/8220

breaking proposal-open

Komentar yang paling membantu

Sesuatu yang perlu dipertimbangkan ketika mengimplementasikan ini adalah menggunakan proyek Microsoft.Extensions.DependencyInjection.Abstractions sebagai ketergantungan utama di seluruh implementasi di Maui. Dengan mengurangi jejak Microsoft.Extensions.DependencyInjection menjadi hanya digunakan pada pembuatan wadah, ini akan memungkinkan perpustakaan dan kerangka kerja pihak ke-3 menjadi agnostik wadah.

Ini memungkinkan pengembang atau kerangka opsi untuk menggunakan wadah yang berbeda dari yang disediakan oleh Maui. Dengan menggunakan proyek abstraksi, pekerjaan yang dibutuhkan oleh pengembang atau kerangka kerja hanyalah mengimplementasikan antarmuka dalam proyek abstraksi. Di mana semua kode Maui hanya akan menggunakan antarmuka. Ini akan memberikan titik ekstensi besar untuk semua orang saat kami mengerjakan kembali cara kerja Dependency Injection di platform.

Semua 21 komentar

Sesuatu yang perlu dipertimbangkan ketika mengimplementasikan ini adalah menggunakan proyek Microsoft.Extensions.DependencyInjection.Abstractions sebagai ketergantungan utama di seluruh implementasi di Maui. Dengan mengurangi jejak Microsoft.Extensions.DependencyInjection menjadi hanya digunakan pada pembuatan wadah, ini akan memungkinkan perpustakaan dan kerangka kerja pihak ke-3 menjadi agnostik wadah.

Ini memungkinkan pengembang atau kerangka opsi untuk menggunakan wadah yang berbeda dari yang disediakan oleh Maui. Dengan menggunakan proyek abstraksi, pekerjaan yang dibutuhkan oleh pengembang atau kerangka kerja hanyalah mengimplementasikan antarmuka dalam proyek abstraksi. Di mana semua kode Maui hanya akan menggunakan antarmuka. Ini akan memberikan titik ekstensi besar untuk semua orang saat kami mengerjakan kembali cara kerja Dependency Injection di platform.

@ahoefling Ya begitulah Maui akan menghabiskan segalanya. Satu-satunya pertanyaan adalah apakah kita perlu menggunakan wadah yang sudah diimplementasikan untuk implementasi default atau menggulung sendiri itu sedikit lebih mobile minded sejauh kinerja

@PureWeen Implementasi wadah default saat ini berkinerja baik untuk seluler. Gumpalan dependensi yang dibawa oleh pembuat host sangat mengejutkan.

@PureWeen ini bagus untuk didengar!

Sejauh yang saya pahami, kinerja wadah proyek ekstensi cukup solid. Saya akan merekomendasikan agar kita memulai dengan wadah default dan kita dapat mengulangi jika tes kinerja tidak memenuhi harapan kita. Saya mengumpulkan contoh kode kecil dari apa yang saya pikirkan untuk menangani beberapa poin ekstensi.

Hal ini memungkinkan kami untuk dengan mudah menukar wadah dengan pengembang, kerangka kerja, atau platform Maui jika kami ingin menggunakan wadah yang berbeda.

Mungkin System.Maui.Application bisa terlihat seperti ini:
```c#
Aplikasi kelas publik: Elemen, IResourcesProvider, IApplicationController, IElementConfiguration
{
wadah IServiceProvider publik { dapatkan; }

protected virtual IServiceProvider CreateContainer()
{
    var serviceCollection = new ServiceCollection();
    RegisterDependencies(serviceCollection);
    return serviceCollection.BuildServiceProvider();
}

// This should be implemented in the app code
protected virtual void RegisterDependencies(IServiceCollection serviceCollection)
{
    // TODO - Register dependencies in app code
}

public Application()
{
    Container = CreateContainer();

    // omited code
}

// omitted code

}
```

@ahoefling dapatkah kita menggunakan ini untuk memungkinkan orang membuat IServiceProvider mereka sendiri?

https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.hostbuilder.useserviceproviderfactory?view=dotnet-plat-ext-3.1

Itulah yang dihubungkan dengan AutoFac

Sejauh yang saya pahami, Host Builder menyediakan API yang kaya untuk menambahkan lebih banyak dependensi default seperti keluar dari kotak. Di mana kode yang saya sarankan jauh lebih ramping. Saya tidak berpikir salah satu dari pendekatan itu lebih 'benar' daripada yang lain, karena mereka memecahkan masalah yang berbeda dengan Injeksi Ketergantungan.

Pembuat Tuan Rumah
Jika kita ingin membuat opini bahwa logging konsol akan terjadi dengan cara tertentu dan katakanlah HttpClientFactory akan bekerja dengan cara tertentu daripada Host Builder mungkin akan menjadi pilihan terbaik kita.

Koleksi Layanan Lean
Jika kita ingin sistem DI kita menjadi semirip mungkin dan membiarkan pengembang aplikasi memutuskan apa yang akan berhasil untuk mereka dan apa yang tidak, maka saya pikir kode yang saya sarankan atau yang serupa akan menjadi cara untuk mengimplementasikannya.

Cara saya melihat masalahnya adalah cara yang paling tidak invasif untuk menukar DependencyService . Jika tujuan akhirnya adalah mengikuti pola yang ada dengan asp.net core dan model pembuat host, mungkin yang terbaik adalah membuat kelas Startup.cs yang menggunakan pembuat host.

Kode di OP bermasalah karena tidak jelas dari jenis yang terlibat bagaimana ServiceProvider.GetService<MainPage>() bekerja, dan bagaimana MainViewModel ditemukan. Ini juga melibatkan null yang seharusnya memicu peringatan NRT. Ini semua tampak seperti cara untuk menghindari sistem tipe. ASP.Net bukan contoh cemerlang dari API yang bersih.

Sangat penting bahwa pengguna harus dapat menghindari penggunaan kemampuan ini, seperti yang mereka bisa sekarang, dan menentukan objek dengan cara yang aman untuk tipe. Dan dengan menghindarinya, pengguna juga terhindar dari membayar penalti kinerja.

type App() =
    inherit Application()
    let vm = someMainViewModel(...)
    let mainPage = MainPage(vm)
    do  base.MainPage <- mainPage

Jika orang ingin mendapatkan model tampilan dengan cara berbasis refleksi/konvensi/anotasi, apakah MAUI benar-benar perlu memberikan dukungan eksplisit? Tidak bisakah mereka melakukan ini? Tautan asli menyarankan mereka bisa.

@ahoefling

Host Builder vs Lean ServiceCollection

Benar, mencari tahu manfaat dari satu vs yang lain jelas merupakan bagian besar dari ini!

Awalnya saya hanya melihat Lean Service Collections tetapi memutuskan untuk masuk semua.
Bagian dari keuntungan menggunakan arahan builder adalah bahwa hal itu hanya terkait dengan implementasi netcore yang ada. Misalnya dengan AutoFac, Anda bisa menggunakan sintaks loop startup yang sama dan tidak harus menciptakan hal kita sendiri. Pengetahuan domain dapat ditransfer.

@PureWeen Ini masuk akal dan merupakan ide bagus untuk tetap dengan penyatuan .NET 5+ dan keterampilan yang dapat ditransfer antara alat yang berbeda.

Saya pikir kita harus membuat Startup.cs yang berada tepat di sebelah App.cs atau App.xaml.cs yang menangani semua kode startup. Ini akan menyatukan kisah Injeksi Ketergantungan di Maui dengan proyek lain di Ekosistem .NET. Ketika saya menerapkan Injeksi Ketergantungan di Modul DNN tahun lalu, saya melakukan hal serupa yang memungkinkan pengembang untuk mentransfer keterampilan mereka dengan mudah.

pikir kita harus membuat Startup.cs yang berada tepat di sebelah App.cs atau App.xaml.cs yang menangani semua kode startup.

Pengguna baru tidak akan ingin mengetahui semua hal bootstrap ini. Menurut pendapat saya, ini adalah hal terakhir yang Anda inginkan terutama karena MAUI akan meringankan semua boilerplate di sekitar AppDelegate, MainActivity, dll. 1 startup/app.xaml untuk mengatur semuanya.

@aritchie setuju

Saya ingin mengaktifkan orang sebanyak mungkin tetapi untuk pengguna baru sebagian besar ini hanya bisa disembunyikan.
Tapi kemudian begitu mereka merasa nyaman, mereka bisa mulai memperluas sesuatu.

Saya pikir ada skenario di sini di mana pengguna bisa mendapatkan keuntungan di sini tanpa harus membeli semuanya.

Misalnya dengan Shell kita bisa memperkenalkan sintaks RegisterRoute seperti

Shell.RegisterRoute<TBindingContext, TPage>(String routeName);
atau hanya
Shell.RegisterRoute<TType>();

Bahwa di bawah tenda menggunakan semua hal ini untuk membuat tipe. Kemudian jika pengguna menginginkan HttpContext yang akan disuntikkan dll.

Bagian lain dari proposal ini adalah mencoba dan membuat semua DataTemplates melalui IServiceProvider juga yang memungkinkan beberapa skenario menyenangkan.

Saya bermain dengan beberapa variasi dengan Shell di sini berdasarkan sampel James

https://github.com/PureWeen/AllExtensions-DI-IoC/tree/shell_ioc

@charlesroddie

Jika orang ingin mendapatkan model tampilan dengan cara berbasis refleksi/konvensi/anotasi, apakah MAUI benar-benar perlu memberikan dukungan eksplisit?

Rencananya saat ini bukan untuk memberikan apa pun melalui refleksi karena pertimbangan kinerja. Mungkin ada beberapa area yang dapat kami sederhanakan pendaftarannya tanpa mengalami penurunan performa tetapi masih perlu menjelajahi beberapa hal ini.

Saya juga telah memperbarui komentar asli dengan sintaks pendaftaran

@PureWeen

Saya akan merekomendasikan memiliki semacam ekstensi rute dari IServiceCollection sehingga hal-hal seperti Prism dapat plugin ke model. Saya telah menggunakan XF selama bertahun-tahun dan saya tidak menggunakan Shell.

yaitu. services.UseRoute(nama opsional)

Pada catatan lain, Shiny memiliki banyak penggunaan di sekitar mekanisme Microsoft DI jika Anda membutuhkan ide lain. Saya akhirnya menolak model hostbuilder (dalam bentuknya saat ini) karena banyaknya hal yang dibawanya yang mengarah pada pertarungan mengerikan dengan linker. Saya akan dengan senang hati membagikan pengalaman ini melalui telepon di beberapa titik.

Mungkin Generator Sumber dapat digunakan untuk meringankan masalah kinerja? Kemudian Anda masih dapat menggunakan atribut khusus tetapi melakukan semua pendaftaran (default) dalam file sumber yang dihasilkan.

@aritchie terdengar bagus!

setelah kita berada di sisi lain dari membangun kesenangan, mari kita mengobrol!

@rogihee

Mungkin Generator Sumber

Ya!! Kami ingin merestrukturisasi pendaftaran penyaji saat ini sebelum Maui dan kami sedang melihat Generator Sumber untuk pekerjaan itu. Jika kita akhirnya menempuh rute itu, kita pasti bisa memanfaatkannya untuk ini

Akan sangat bagus untuk memiliki DI dalam Formulir Xamarin, tetapi saya tidak membaca apa pun tentang cakupan DI. Menggunakan cakupan DI adalah cara yang bagus untuk mengatur masa pakai komponen. Ini sangat berguna ketika bekerja dengan konteks basis data inti EF.

Akan sangat bagus untuk memiliki implementasi DI-aware ICommand yang membuat cakupan dan meminta objek pengendali perintah pada cakupan ini.

Menggunakan injeksi ketergantungan dalam aplikasi WPF juga sulit, jadi mungkin bekerja sama dengan tim WPF untuk mengizinkan penggunaan DI di WPF dengan cara yang sama.

Satu-satunya pertanyaan adalah apakah kita perlu menggunakan wadah yang sudah diimplementasikan untuk implementasi default atau menggulung sendiri itu sedikit lebih mobile minded sejauh kinerja

Saya akan mengatakan untuk mengimplementasikan wadah untuk Maui dari awal. Seperti yang Anda lihat di sini , DependecyService diimplementasikan di Xamarin.Forms adalah yang lebih cepat. (Saya tahu... Artikel itu agak lama, tapi saya rasa nilainya tidak terlalu banyak berubah).
Juga jauh lebih baik - dalam hal kinerja - untuk membuat DI Container kita sendiri. Tapi kekhawatiran saya adalah untuk dunia tanpa .NET 5. Dengan .NET 5 dan generator kode sumber, kami mungkin memiliki solusi lain untuk masalah ini.
Dan, untuk seluler , jika Anda membutuhkan DI yang berat, Anda melakukan sesuatu yang salah.

Saya datang ke repositori ini untuk mengusulkan agar aplikasi MAUI mendukung host generik (Microsoft.Extensions.Hosting), lebih disukai secara default (walaupun beberapa orang mungkin ingin melindungi), karena sangat wajar memiliki DI dalam aplikasi UI. Ini berarti bahwa orang perlu memahami lebih sedikit, jika mereka terbiasa dengan injeksi ketergantungan untuk inti asp.net daripada itu juga akan bekerja dengan MAUI. Untuk pemula mungkin baik untuk menjaga konfigurasi yang sangat minimalis.

Alih-alih menjadi yang pertama, saya menemukan masalah ini, dan saya pikir ini adalah diskusi yang bagus!

Memiliki aplikasi yang menyediakan beberapa layanan dan UI kecil (status, konfigurasi, dll.) masih merupakan hal yang umum. Setiap kali saya membangun aplikasi kecil yang menyediakan beberapa layanan, saya terus menemukan kebutuhan untuk memperluas ini dengan UI.

Saya sudah membangun sesuatu untuk membuat Windows Forms dan aplikasi WPF berjalan di host generik, seperti yang dapat dilihat dalam proyek ini: https://github.com/dapplo/Dapplo.Microsoft.Extensions.Hosting
Saya berharap untuk menggunakan ini untuk Greenshot, tetapi saat ini saya belum merilis apa pun dengannya.
Dalam sampel ada proyek WPF yang menggunakan ReactiveUI, MahApps dan Caliburn.Micro.

Tolong jangan membuat saya mendaftarkan pandangan secara manual di DI. Ambil yang terbaik dari blazer, bagus! Di blazor saya dapat dengan mudah menurunkan parameter atau menyuntikkan layanan ke dalam komponen. Tidak perlu mendaftarkan komponen itu sendiri. Saya hanya perlu mendaftarkan layanan. MVVM sangat bagus untuk halaman, tetapi saya tidak pernah merasa lebih produktif membuat tampilan komponen yang dihosting sendiri ini. Saya benci memulai proyek Xamarin, WPF, UWP baru karena hal-hal yang tidak ada di luar kotak. Sekali lagi, lihat Blazor, ini adalah cetak biru sejati untuk semua kerangka kerja GUI masa depan.

Saya tidak mengerti mengapa ada orang yang membuat dua pernyataan berturut-turut ini:

    services.AddTransient<IMainViewModel, MainViewModel>();
    services.AddTransient<MainPage>();

Apa yang salah dengan menyuntikkan layanan ke kode tampilan di belakang? Itu yang Anda inginkan 99,99% dari waktu.

Saya juga sangat menyukai Injeksi Properti di blazor daripada injeksi konstruktor, hanya karena lebih mudah.

Saya sebenarnya baru saja merilis perpustakaan yang melakukan ini untuk Xamarin.Forms https://github.com/hostly-org/hostly . Ini menggunakan implementasi kustom IHost, yang dapat dengan mudah diatur di titik masuk asli. misalnya di MainActivity Anda akan mengganti:

LoadApplication(new App());`

dengan:

new XamarinHostBuilder().UseApplication<App>()
   .UseStartup<Startup>()
   .UsePlatform(this)
   .Start();

itu juga memiliki beberapa barang tambahan bawaan seperti dukungan untuk mendaftarkan middleware dengan navigasi.

Saya mendukung penggunaan sistem DI yang berada di luar ruang nama System.Maui sehingga kode dapat dibagikan dengan proyek MAUI dan non-MAUI.

Di mana saya kurang yakin tentang penggunaan Microsoft.Extensions.DependencyInjection atau sesuatu yang didasarkan padanya sebagai sistem DI itu. Saya tidak akan berpura-pura menjadi ahli dalam hal ini – saya sendiri belum pernah menggunakan beberapa sistem DI modern. Namun, saya bertanya-tanya apakah orang lain telah membaca edisi kedua “Injeksi Ketergantungan. Prinsip, Praktik, dan Pola” oleh Steven van Deursen dan Mark Seemann. Mereka mencurahkan bagian di edisi kedua untuk melihat Autofac, Simple Injector, dan Microsoft.Extensions.DependencyInjection, memberikan pro & kontra, serta pendapat/kesimpulan mereka sendiri. Sementara saya dapat melihat beberapa manfaat menggunakan Microsoft.Extensions.DependencyInjection (terutama yang digunakan dalam skenario Microsoft lainnya) di dunia MAUI, saya ingin tahu apakah ada orang di sini yang memiliki pengalaman beberapa sistem DI dapat mengomentari kesimpulan penulis dan sejauh mana mereka berhubungan dengan dunia MAUI penggunaan seluler dan desktop?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

probonopd picture probonopd  ·  50Komentar

jsuarezruiz picture jsuarezruiz  ·  6Komentar

aspnetde picture aspnetde  ·  50Komentar

qcjxberin picture qcjxberin  ·  5Komentar

njsokalski picture njsokalski  ·  6Komentar