@@ -100,6 +100,201 @@ Client initialization is asynchronous because:
1001003 . ** Thread Safety** : Async operations don't block thread pool threads, improving application responsiveness
1011014 . ** Error Handling** : Connection and authentication issues surface immediately during client creation, not later during operations
102102
103+ ## Initialization Lifecycle
104+
105+ ### Builder Pattern (Recommended)
106+
107+ When using ` WeaviateClientBuilder ` or ` Connect ` helpers, initialization happens automatically:
108+
109+ ``` csharp
110+ // Client is fully initialized before being returned
111+ var client = await WeaviateClientBuilder .Local ().BuildAsync ();
112+ // ✅ RestClient and GrpcClient are ready to use
113+ var results = await client .Collections .Get (" Article" ).Query .FetchObjects ();
114+ ```
115+
116+ ** Key Points:**
117+ - ` BuildAsync() ` calls ` InitializeAsync() ` internally before returning
118+ - The client is ** always fully initialized** when you receive it
119+ - No manual initialization needed
120+
121+ ### Dependency Injection Pattern
122+
123+ When using dependency injection, there are two modes:
124+
125+ #### Eager Initialization (Default - Recommended)
126+
127+ ``` csharp
128+ services .AddWeaviateLocal (
129+ hostname : " localhost" ,
130+ eagerInitialization : true // Default
131+ );
132+ ```
133+
134+ ** How it works:**
135+ - A hosted service (` WeaviateInitializationService ` ) runs on application startup
136+ - Calls ` InitializeAsync() ` automatically before your app serves requests
137+ - The client is ** always initialized** when injected into services
138+
139+ ``` csharp
140+ public class MyService
141+ {
142+ private readonly WeaviateClient _client ;
143+
144+ public MyService (WeaviateClient client )
145+ {
146+ // ✅ Client is already initialized by the hosted service
147+ _client = client ;
148+ }
149+
150+ public async Task DoWork ()
151+ {
152+ // ✅ Safe to use immediately
153+ var results = await _client .Collections .Get (" Article" ).Query .FetchObjects ();
154+ }
155+ }
156+ ```
157+
158+ #### Lazy Initialization (Manual Control)
159+
160+ ``` csharp
161+ services .AddWeaviateLocal (
162+ hostname : " localhost" ,
163+ eagerInitialization : false // Opt-in to lazy initialization
164+ );
165+ ```
166+
167+ ** How it works:**
168+ - Client is created but NOT initialized
169+ - You must call ` InitializeAsync() ` before first use
170+ - Useful for scenarios where you want to control when initialization happens
171+
172+ ``` csharp
173+ public class MyService
174+ {
175+ private readonly WeaviateClient _client ;
176+
177+ public MyService (WeaviateClient client )
178+ {
179+ // ⚠️ Client is NOT yet initialized
180+ _client = client ;
181+ }
182+
183+ public async Task Initialize ()
184+ {
185+ // ✅ Manually trigger initialization
186+ await _client .InitializeAsync ();
187+ }
188+
189+ public async Task DoWork ()
190+ {
191+ // Check if initialized
192+ if (! _client .IsInitialized )
193+ {
194+ await _client .InitializeAsync ();
195+ }
196+
197+ var results = await _client .Collections .Get (" Article" ).Query .FetchObjects ();
198+ }
199+ }
200+ ```
201+
202+ ### Checking Initialization Status
203+
204+ Use the ` IsInitialized ` property to check if the client is ready:
205+
206+ ``` csharp
207+ if (client .IsInitialized )
208+ {
209+ // Safe to use RestClient and GrpcClient
210+ var results = await client .Collections .Get (" Article" ).Query .FetchObjects ();
211+ }
212+ else
213+ {
214+ // Must call InitializeAsync() first
215+ await client .InitializeAsync ();
216+ }
217+ ```
218+
219+ ** What happens during initialization:**
220+ 1 . Token service is created (for authentication)
221+ 2 . REST client is configured
222+ 3 . Server metadata is fetched (validates auth, gets gRPC settings)
223+ 4 . gRPC client is configured with server metadata
224+ 5 . ` RestClient ` and ` GrpcClient ` properties are populated
225+
226+ ### Important: When is the Client Ready?
227+
228+ | Pattern | When Ready | RestClient/GrpcClient Available |
229+ | ---------| -----------| --------------------------------|
230+ | ` await BuildAsync() ` | Immediately after return | ✅ Yes |
231+ | DI Eager (default) | Before app starts serving | ✅ Yes |
232+ | DI Lazy | After calling ` InitializeAsync() ` | ⚠️ Only after init |
233+
234+ ** ⚠️ Using uninitialized client causes ` NullReferenceException ` :**
235+
236+ ``` csharp
237+ // ❌ BAD: Lazy DI without calling InitializeAsync()
238+ var client = serviceProvider .GetService <WeaviateClient >();
239+ var results = await client .Collections .Get (" Article" ).Query .FetchObjects (); // NullReferenceException!
240+
241+ // ✅ GOOD: Check and initialize if needed
242+ var client = serviceProvider .GetService <WeaviateClient >();
243+ if (! client .IsInitialized )
244+ {
245+ await client .InitializeAsync ();
246+ }
247+ var results = await client .Collections .Get (" Article" ).Query .FetchObjects ();
248+ ```
249+
250+ ### Automatic Initialization Guards
251+
252+ ** ✨ Safety Feature:** All public async methods in the Weaviate client automatically ensure initialization before executing. This provides a safety net against accidental use of uninitialized clients.
253+
254+ ** How it works:**
255+
256+ When you call any async method on the client (like ` Collections.Create() ` , ` Cluster.Replicate() ` , ` Alias.Get() ` , etc.), the client automatically calls ` EnsureInitializedAsync() ` internally before performing the operation. If the client isn't initialized yet:
257+
258+ - ** With eager initialization (default)** : The guard passes immediately since the client is already initialized
259+ - ** With lazy initialization** : The guard triggers initialization automatically on first use
260+
261+ ``` csharp
262+ // Lazy DI - initialization happens automatically on first call
263+ services .AddWeaviateLocal (hostname : " localhost" , eagerInitialization : false );
264+
265+ // Later in your code...
266+ var client = serviceProvider .GetService <WeaviateClient >();
267+
268+ // ✅ This works! The client auto-initializes on first use
269+ var collections = await client .Collections .List (); // Initialization happens here automatically
270+ ```
271+
272+ ** Performance Impact:**
273+
274+ - ** Eager initialization (default)** : No overhead - guards pass through immediately
275+ - ** Lazy initialization** : Minimal overhead - first call triggers initialization, subsequent calls pass through
276+ - Guards use ` Lazy<Task> ` internally, ensuring initialization happens exactly once even with concurrent calls
277+
278+ ** When guards help:**
279+
280+ - Forgetting to call ` InitializeAsync() ` in lazy initialization scenarios
281+ - Race conditions where multiple threads access an uninitialized client
282+ - Unit tests that don't properly set up client initialization
283+
284+ ** What's protected:**
285+
286+ - ✅ ` client.Collections.* ` - All collection operations
287+ - ✅ ` client.Cluster.* ` - All cluster operations
288+ - ✅ ` client.Alias.* ` - All alias operations
289+ - ✅ ` client.Users.* ` , ` client.Roles.* ` , ` client.Groups.* ` - All auth operations
290+ - ✅ Collection-level operations like ` collection.Delete() ` , ` collection.Iterator() `
291+ - ✅ All async methods that access REST or gRPC clients
292+
293+ ** What's not protected:**
294+
295+ - ❌ Synchronous property accessors (design limitation - properties can't be async)
296+ - ❌ Direct access to internal clients (not part of public API)
297+
103298## Connection Configuration
104299
105300### Local Development
0 commit comments