Repository files navigation

Forge.OData.Client

Your project needs to connect to an OData service? Just point our CLI at the endpoint, and everything gets generated for you—models, client, type-safe queries. No manual work. No boilerplate. Just instant OData integration.

⚡ Quick Start

1. Install the CLI Tool

dotnet tool install --global Forge.OData.CLI

2. Add an OData Client to Your Project

Navigate to your project and run:

dotnet odata add --endpoint https://services.odata.org/V4/TripPinServiceRW

3. Build and Code

dotnet build

That's it! You now have a fully functional, type-safe OData client ready to use:

usingvarhttpClient=newHttpClient();varclient=newTripPinServiceRWClient(httpClient);// Query with LINQ - it just works!varpeople=awaitclient.People.Where(p =>p.FirstName.StartsWith("R")).OrderBy(p =>p.LastName).Take(10).ToListAsync();foreach(varpersoninpeople){Console.WriteLine($"{person.FirstName}{person.LastName}");}

🎯 Why Forge.OData.Client?

Stop writing boilerplate. Stop manually creating DTOs. Stop fighting with HTTP requests and JSON parsing.

You have an OData API. You want to use it. The OData CLI does the heavy lifting:

  • Downloads the metadata from your OData service
  • Generates all model classes with proper types and attributes
  • Creates a type-safe client with IntelliSense support
  • Translates LINQ to OData queries automatically
  • Handles JSON serialization with optimized converters

All this happens during your normal dotnet build. No runtime reflection. No performance overhead.

📦 Installation

Install the Forge OData CLI as a global tool:

dotnet tool install --global Forge.OData.CLI

Or install it locally in your project for team consistency:

dotnet new tool-manifest # if you don't have one already
dotnet tool install --local Forge.OData.CLI

🚀 Usage Examples

Basic Usage: Connect to Any OData Service

# Simple: Just provide the endpoint
dotnet odata add --endpoint https://services.odata.org/V4/Northwind/Northwind.svc

The CLI will:

  1. Download the $metadata from the endpoint
  2. Generate a client class named NorthwindClient (derived from URL)
  3. Create all model classes for entities (Products, Orders, Customers, etc.)
  4. Configure your project automatically

Custom Client Name

# Give your client a meaningful name
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name CompanyDataService

Now you'll have a CompanyDataService class instead of a generic name.

Organize Clients in Subdirectories

# Keep your OData clients organized
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name InventoryService \
--output-path Services/OData

This creates:

  • File: Services/OData/InventoryService.cs
  • Namespace: YourProject.Services.OData

Custom Namespace

# Control the namespace for better organization
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name ProductCatalog \
--namespace MyCompany.External.Services

Multiple OData Services in One Project

# Add multiple services - they all work together
dotnet odata add --endpoint https://api.products.com/odata --client-name ProductService
dotnet odata add --endpoint https://api.orders.com/odata --client-name OrderService
dotnet odata add --endpoint https://api.customers.com/odata --client-name CustomerService

Each client is independent, and you can use them side by side in your application.

🔄 Keeping Metadata Up to Date

When the OData service changes (new entities, modified properties), just update:

dotnet odata update

This command:

  • Finds all OData clients in your project
  • Re-downloads metadata from their endpoints
  • Updates the metadata files
  • Rebuild to regenerate clients with the latest schema

Workflow example:

# Initial setup
dotnet odata add --endpoint https://api.example.com/odata --client-name ApiClient
dotnet build
# ... time passes, API changes ...# Update to latest schema
dotnet odata update
dotnet build # Regenerates with new metadata

💡 Real-World Example

Let's say you're building an app that needs to fetch data from the TripPin OData service:

# Step 1: Add the clientcd MyTravelApp
dotnet odata add \
--endpoint https://services.odata.org/V4/TripPinServiceRW \
--client-name TripPinService \
--output-path Services
# Step 2: Build
dotnet build

Now use it in your code:

usingMyTravelApp.Services;publicclassTravelService{privatereadonlyHttpClient_httpClient;publicTravelService(IHttpClientFactoryhttpClientFactory){_httpClient=httpClientFactory.CreateClient();}publicasyncTask<List<Person>>GetTravelersAsync(stringfirstNamePrefix){varclient=newTripPinService(_httpClient);// Type-safe LINQ queriesreturnawaitclient.People.Where(p =>p.FirstName.StartsWith(firstNamePrefix)).OrderBy(p =>p.LastName).ToListAsync();}publicasyncTask<Person>GetPersonWithTripsAsync(stringusername){varclient=newTripPinService(_httpClient);// Expand navigation propertiesvarpeople=awaitclient.People.Where(p =>p.UserName==username).Expand(p =>p.Trips).ToListAsync();returnpeople.FirstOrDefault();}}

That's it. No manual DTOs. No string-based queries. Just clean, type-safe code with full IntelliSense.

🔍 What You Get

When you run dotnet odata add, the tool generates:

1. Model Classes

publicclassProduct{[Key]publicintId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// ... all properties from metadata}

2. Optimized JSON Converters

Custom converters for each model that:

  • Deserialize JSON without reflection (faster!)
  • Handle nullable types correctly
  • Support all OData types

3. Type-Safe Client

publicpartialclassYourServiceClient{publicODataQueryable<Product>Products{get;}publicODataQueryable<Order>Orders{get;}publicODataQueryable<Customer>Customers{get;}// ... all entity sets from metadata}

4. LINQ Support

Write normal C# LINQ queries:

// This LINQ expression...varquery=client.Products.Where(p =>p.Price>10&&p.InStock).OrderBy(p =>p.Name).Skip(20).Take(10);// ...becomes this OData query automatically:// /Products?$filter=Price gt 10 and InStock eq true&$orderby=Name asc&$skip=20&$top=10

🎨 Supported LINQ Operations

LINQ ExpressionOData Query
.Where(p => p.Price > 10)$filter=Price gt 10
.Where(p => p.Name == "Test")$filter=Name eq 'Test'
.Where(p => p.InStock && p.Price < 100)$filter=InStock eq true and Price lt 100
.OrderBy(p => p.Name)$orderby=Name asc
.OrderByDescending(p => p.Price)$orderby=Price desc
.Skip(10)$skip=10
.Take(20)$top=20
.Select(p => new { p.Name, p.Price })$select=Name,Price
.Expand(o => o.Product)$expand=Product

String methods work too:

  • .Where(p => p.Name.StartsWith("A"))startswith(Name, 'A')
  • .Where(p => p.Name.EndsWith("Z"))endswith(Name, 'Z')
  • .Where(p => p.Name.Contains("mid"))contains(Name, 'mid')

🛠️ Advanced Features

Need more control? The tool supports advanced scenarios:

Attribute-Based Customization

After generating the initial client, you can customize it:

usingForge.OData.Attributes;namespaceMyApp.Services{[ODataClient(MetadataFile="ApiMetadata.xml",Endpoint="https://api.example.com/odata")]publicpartialclassApiClient{// Add your custom methodspublicasyncTask<Product?>GetFeaturedProductAsync(){varresults=awaitProducts.Where(p =>p.Featured).OrderByDescending(p =>p.Rating).Take(1).ToListAsync();returnresults.FirstOrDefault();}// Add custom propertiespublicstringServiceVersion=>"v2.0";}}

Working with Multiple Environments

// DevelopmentvardevClient=newApiClient(httpClient,"https://dev-api.example.com/odata");// ProductionvarprodClient=newApiClient(httpClient,"https://api.example.com/odata");

📚 More Information

  • Technical documentation: See CONTRIBUTE.md for detailed architecture, project structure, and contribution guidelines
  • Examples: Check the sample/ directory for working examples
  • Changelog: See CHANGELOG.md for version history

🤝 Contributing

We welcome contributions! Please see CONTRIBUTE.md for detailed information on:

  • Project architecture and structure
  • Development setup
  • Building and testing
  • Code generation workflow
  • Contribution guidelines

📄 License

MIT

About

OData Client with source generator

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Forge.OData.Client

Your project needs to connect to an OData service? Just point our CLI at the endpoint, and everything gets generated for you—models, client, type-safe queries. No manual work. No boilerplate. Just instant OData integration.

⚡ Quick Start

1. Install the CLI Tool

dotnet tool install --global Forge.OData.CLI

2. Add an OData Client to Your Project

Navigate to your project and run:

dotnet odata add --endpoint https://services.odata.org/V4/TripPinServiceRW

3. Build and Code

dotnet build

That's it! You now have a fully functional, type-safe OData client ready to use:

usingvarhttpClient=newHttpClient();varclient=newTripPinServiceRWClient(httpClient);// Query with LINQ - it just works!varpeople=awaitclient.People.Where(p =>p.FirstName.StartsWith("R")).OrderBy(p =>p.LastName).Take(10).ToListAsync();foreach(varpersoninpeople){Console.WriteLine($"{person.FirstName}{person.LastName}");}

🎯 Why Forge.OData.Client?

Stop writing boilerplate. Stop manually creating DTOs. Stop fighting with HTTP requests and JSON parsing.

You have an OData API. You want to use it. The OData CLI does the heavy lifting:

  • Downloads the metadata from your OData service
  • Generates all model classes with proper types and attributes
  • Creates a type-safe client with IntelliSense support
  • Translates LINQ to OData queries automatically
  • Handles JSON serialization with optimized converters

All this happens during your normal dotnet build. No runtime reflection. No performance overhead.

📦 Installation

Install the Forge OData CLI as a global tool:

dotnet tool install --global Forge.OData.CLI

Or install it locally in your project for team consistency:

dotnet new tool-manifest # if you don't have one already
dotnet tool install --local Forge.OData.CLI

🚀 Usage Examples

Basic Usage: Connect to Any OData Service

# Simple: Just provide the endpoint
dotnet odata add --endpoint https://services.odata.org/V4/Northwind/Northwind.svc

The CLI will:

  1. Download the $metadata from the endpoint
  2. Generate a client class named NorthwindClient (derived from URL)
  3. Create all model classes for entities (Products, Orders, Customers, etc.)
  4. Configure your project automatically

Custom Client Name

# Give your client a meaningful name
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name CompanyDataService

Now you'll have a CompanyDataService class instead of a generic name.

Organize Clients in Subdirectories

# Keep your OData clients organized
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name InventoryService \
--output-path Services/OData

This creates:

  • File: Services/OData/InventoryService.cs
  • Namespace: YourProject.Services.OData

Custom Namespace

# Control the namespace for better organization
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name ProductCatalog \
--namespace MyCompany.External.Services

Multiple OData Services in One Project

# Add multiple services - they all work together
dotnet odata add --endpoint https://api.products.com/odata --client-name ProductService
dotnet odata add --endpoint https://api.orders.com/odata --client-name OrderService
dotnet odata add --endpoint https://api.customers.com/odata --client-name CustomerService

Each client is independent, and you can use them side by side in your application.

🔄 Keeping Metadata Up to Date

When the OData service changes (new entities, modified properties), just update:

dotnet odata update

This command:

  • Finds all OData clients in your project
  • Re-downloads metadata from their endpoints
  • Updates the metadata files
  • Rebuild to regenerate clients with the latest schema

Workflow example:

# Initial setup
dotnet odata add --endpoint https://api.example.com/odata --client-name ApiClient
dotnet build
# ... time passes, API changes ...# Update to latest schema
dotnet odata update
dotnet build # Regenerates with new metadata

💡 Real-World Example

Let's say you're building an app that needs to fetch data from the TripPin OData service:

# Step 1: Add the clientcd MyTravelApp
dotnet odata add \
--endpoint https://services.odata.org/V4/TripPinServiceRW \
--client-name TripPinService \
--output-path Services
# Step 2: Build
dotnet build

Now use it in your code:

usingMyTravelApp.Services;publicclassTravelService{privatereadonlyHttpClient_httpClient;publicTravelService(IHttpClientFactoryhttpClientFactory){_httpClient=httpClientFactory.CreateClient();}publicasyncTask<List<Person>>GetTravelersAsync(stringfirstNamePrefix){varclient=newTripPinService(_httpClient);// Type-safe LINQ queriesreturnawaitclient.People.Where(p =>p.FirstName.StartsWith(firstNamePrefix)).OrderBy(p =>p.LastName).ToListAsync();}publicasyncTask<Person>GetPersonWithTripsAsync(stringusername){varclient=newTripPinService(_httpClient);// Expand navigation propertiesvarpeople=awaitclient.People.Where(p =>p.UserName==username).Expand(p =>p.Trips).ToListAsync();returnpeople.FirstOrDefault();}}

That's it. No manual DTOs. No string-based queries. Just clean, type-safe code with full IntelliSense.

🔍 What You Get

When you run dotnet odata add, the tool generates:

1. Model Classes

publicclassProduct{[Key]publicintId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// ... all properties from metadata}

2. Optimized JSON Converters

Custom converters for each model that:

  • Deserialize JSON without reflection (faster!)
  • Handle nullable types correctly
  • Support all OData types

3. Type-Safe Client

publicpartialclassYourServiceClient{publicODataQueryable<Product>Products{get;}publicODataQueryable<Order>Orders{get;}publicODataQueryable<Customer>Customers{get;}// ... all entity sets from metadata}

4. LINQ Support

Write normal C# LINQ queries:

// This LINQ expression...varquery=client.Products.Where(p =>p.Price>10&&p.InStock).OrderBy(p =>p.Name).Skip(20).Take(10);// ...becomes this OData query automatically:// /Products?$filter=Price gt 10 and InStock eq true&$orderby=Name asc&$skip=20&$top=10

🎨 Supported LINQ Operations

LINQ ExpressionOData Query
.Where(p => p.Price > 10)$filter=Price gt 10
.Where(p => p.Name == "Test")$filter=Name eq 'Test'
.Where(p => p.InStock && p.Price < 100)$filter=InStock eq true and Price lt 100
.OrderBy(p => p.Name)$orderby=Name asc
.OrderByDescending(p => p.Price)$orderby=Price desc
.Skip(10)$skip=10
.Take(20)$top=20
.Select(p => new { p.Name, p.Price })$select=Name,Price
.Expand(o => o.Product)$expand=Product

String methods work too:

  • .Where(p => p.Name.StartsWith("A"))startswith(Name, 'A')
  • .Where(p => p.Name.EndsWith("Z"))endswith(Name, 'Z')
  • .Where(p => p.Name.Contains("mid"))contains(Name, 'mid')

🛠️ Advanced Features

Need more control? The tool supports advanced scenarios:

Attribute-Based Customization

After generating the initial client, you can customize it:

usingForge.OData.Attributes;namespaceMyApp.Services{[ODataClient(MetadataFile="ApiMetadata.xml",Endpoint="https://api.example.com/odata")]publicpartialclassApiClient{// Add your custom methodspublicasyncTask<Product?>GetFeaturedProductAsync(){varresults=awaitProducts.Where(p =>p.Featured).OrderByDescending(p =>p.Rating).Take(1).ToListAsync();returnresults.FirstOrDefault();}// Add custom propertiespublicstringServiceVersion=>"v2.0";}}

Working with Multiple Environments

// DevelopmentvardevClient=newApiClient(httpClient,"https://dev-api.example.com/odata");// ProductionvarprodClient=newApiClient(httpClient,"https://api.example.com/odata");

📚 More Information

  • Technical documentation: See CONTRIBUTE.md for detailed architecture, project structure, and contribution guidelines
  • Examples: Check the sample/ directory for working examples
  • Changelog: See CHANGELOG.md for version history

🤝 Contributing

We welcome contributions! Please see CONTRIBUTE.md for detailed information on:

  • Project architecture and structure
  • Development setup
  • Building and testing
  • Code generation workflow
  • Contribution guidelines

📄 License

MIT

About

OData Client with source generator

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Forge.OData.Client

Your project needs to connect to an OData service? Just point our CLI at the endpoint, and everything gets generated for you—models, client, type-safe queries. No manual work. No boilerplate. Just instant OData integration.

⚡ Quick Start

1. Install the CLI Tool

dotnet tool install --global Forge.OData.CLI

2. Add an OData Client to Your Project

Navigate to your project and run:

dotnet odata add --endpoint https://services.odata.org/V4/TripPinServiceRW

3. Build and Code

dotnet build

That's it! You now have a fully functional, type-safe OData client ready to use:

usingvarhttpClient=newHttpClient();varclient=newTripPinServiceRWClient(httpClient);// Query with LINQ - it just works!varpeople=awaitclient.People.Where(p =>p.FirstName.StartsWith("R")).OrderBy(p =>p.LastName).Take(10).ToListAsync();foreach(varpersoninpeople){Console.WriteLine($"{person.FirstName}{person.LastName}");}

🎯 Why Forge.OData.Client?

Stop writing boilerplate. Stop manually creating DTOs. Stop fighting with HTTP requests and JSON parsing.

You have an OData API. You want to use it. The OData CLI does the heavy lifting:

  • Downloads the metadata from your OData service
  • Generates all model classes with proper types and attributes
  • Creates a type-safe client with IntelliSense support
  • Translates LINQ to OData queries automatically
  • Handles JSON serialization with optimized converters

All this happens during your normal dotnet build. No runtime reflection. No performance overhead.

📦 Installation

Install the Forge OData CLI as a global tool:

dotnet tool install --global Forge.OData.CLI

Or install it locally in your project for team consistency:

dotnet new tool-manifest # if you don't have one already
dotnet tool install --local Forge.OData.CLI

🚀 Usage Examples

Basic Usage: Connect to Any OData Service

# Simple: Just provide the endpoint
dotnet odata add --endpoint https://services.odata.org/V4/Northwind/Northwind.svc

The CLI will:

  1. Download the $metadata from the endpoint
  2. Generate a client class named NorthwindClient (derived from URL)
  3. Create all model classes for entities (Products, Orders, Customers, etc.)
  4. Configure your project automatically

Custom Client Name

# Give your client a meaningful name
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name CompanyDataService

Now you'll have a CompanyDataService class instead of a generic name.

Organize Clients in Subdirectories

# Keep your OData clients organized
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name InventoryService \
--output-path Services/OData

This creates:

  • File: Services/OData/InventoryService.cs
  • Namespace: YourProject.Services.OData

Custom Namespace

# Control the namespace for better organization
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name ProductCatalog \
--namespace MyCompany.External.Services

Multiple OData Services in One Project

# Add multiple services - they all work together
dotnet odata add --endpoint https://api.products.com/odata --client-name ProductService
dotnet odata add --endpoint https://api.orders.com/odata --client-name OrderService
dotnet odata add --endpoint https://api.customers.com/odata --client-name CustomerService

Each client is independent, and you can use them side by side in your application.

🔄 Keeping Metadata Up to Date

When the OData service changes (new entities, modified properties), just update:

dotnet odata update

This command:

  • Finds all OData clients in your project
  • Re-downloads metadata from their endpoints
  • Updates the metadata files
  • Rebuild to regenerate clients with the latest schema

Workflow example:

# Initial setup
dotnet odata add --endpoint https://api.example.com/odata --client-name ApiClient
dotnet build
# ... time passes, API changes ...# Update to latest schema
dotnet odata update
dotnet build # Regenerates with new metadata

💡 Real-World Example

Let's say you're building an app that needs to fetch data from the TripPin OData service:

# Step 1: Add the clientcd MyTravelApp
dotnet odata add \
--endpoint https://services.odata.org/V4/TripPinServiceRW \
--client-name TripPinService \
--output-path Services
# Step 2: Build
dotnet build

Now use it in your code:

usingMyTravelApp.Services;publicclassTravelService{privatereadonlyHttpClient_httpClient;publicTravelService(IHttpClientFactoryhttpClientFactory){_httpClient=httpClientFactory.CreateClient();}publicasyncTask<List<Person>>GetTravelersAsync(stringfirstNamePrefix){varclient=newTripPinService(_httpClient);// Type-safe LINQ queriesreturnawaitclient.People.Where(p =>p.FirstName.StartsWith(firstNamePrefix)).OrderBy(p =>p.LastName).ToListAsync();}publicasyncTask<Person>GetPersonWithTripsAsync(stringusername){varclient=newTripPinService(_httpClient);// Expand navigation propertiesvarpeople=awaitclient.People.Where(p =>p.UserName==username).Expand(p =>p.Trips).ToListAsync();returnpeople.FirstOrDefault();}}

That's it. No manual DTOs. No string-based queries. Just clean, type-safe code with full IntelliSense.

🔍 What You Get

When you run dotnet odata add, the tool generates:

1. Model Classes

publicclassProduct{[Key]publicintId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// ... all properties from metadata}

2. Optimized JSON Converters

Custom converters for each model that:

  • Deserialize JSON without reflection (faster!)
  • Handle nullable types correctly
  • Support all OData types

3. Type-Safe Client

publicpartialclassYourServiceClient{publicODataQueryable<Product>Products{get;}publicODataQueryable<Order>Orders{get;}publicODataQueryable<Customer>Customers{get;}// ... all entity sets from metadata}

4. LINQ Support

Write normal C# LINQ queries:

// This LINQ expression...varquery=client.Products.Where(p =>p.Price>10&&p.InStock).OrderBy(p =>p.Name).Skip(20).Take(10);// ...becomes this OData query automatically:// /Products?$filter=Price gt 10 and InStock eq true&$orderby=Name asc&$skip=20&$top=10

🎨 Supported LINQ Operations

LINQ ExpressionOData Query
.Where(p => p.Price > 10)$filter=Price gt 10
.Where(p => p.Name == "Test")$filter=Name eq 'Test'
.Where(p => p.InStock && p.Price < 100)$filter=InStock eq true and Price lt 100
.OrderBy(p => p.Name)$orderby=Name asc
.OrderByDescending(p => p.Price)$orderby=Price desc
.Skip(10)$skip=10
.Take(20)$top=20
.Select(p => new { p.Name, p.Price })$select=Name,Price
.Expand(o => o.Product)$expand=Product

String methods work too:

  • .Where(p => p.Name.StartsWith("A"))startswith(Name, 'A')
  • .Where(p => p.Name.EndsWith("Z"))endswith(Name, 'Z')
  • .Where(p => p.Name.Contains("mid"))contains(Name, 'mid')

🛠️ Advanced Features

Need more control? The tool supports advanced scenarios:

Attribute-Based Customization

After generating the initial client, you can customize it:

usingForge.OData.Attributes;namespaceMyApp.Services{[ODataClient(MetadataFile="ApiMetadata.xml",Endpoint="https://api.example.com/odata")]publicpartialclassApiClient{// Add your custom methodspublicasyncTask<Product?>GetFeaturedProductAsync(){varresults=awaitProducts.Where(p =>p.Featured).OrderByDescending(p =>p.Rating).Take(1).ToListAsync();returnresults.FirstOrDefault();}// Add custom propertiespublicstringServiceVersion=>"v2.0";}}

Working with Multiple Environments

// DevelopmentvardevClient=newApiClient(httpClient,"https://dev-api.example.com/odata");// ProductionvarprodClient=newApiClient(httpClient,"https://api.example.com/odata");

📚 More Information

  • Technical documentation: See CONTRIBUTE.md for detailed architecture, project structure, and contribution guidelines
  • Examples: Check the sample/ directory for working examples
  • Changelog: See CHANGELOG.md for version history

🤝 Contributing

We welcome contributions! Please see CONTRIBUTE.md for detailed information on:

  • Project architecture and structure
  • Development setup
  • Building and testing
  • Code generation workflow
  • Contribution guidelines

📄 License

MIT

About

OData Client with source generator

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Forge.OData.Client

Your project needs to connect to an OData service? Just point our CLI at the endpoint, and everything gets generated for you—models, client, type-safe queries. No manual work. No boilerplate. Just instant OData integration.

⚡ Quick Start

1. Install the CLI Tool

dotnet tool install --global Forge.OData.CLI

2. Add an OData Client to Your Project

Navigate to your project and run:

dotnet odata add --endpoint https://services.odata.org/V4/TripPinServiceRW

3. Build and Code

dotnet build

That's it! You now have a fully functional, type-safe OData client ready to use:

usingvarhttpClient=newHttpClient();varclient=newTripPinServiceRWClient(httpClient);// Query with LINQ - it just works!varpeople=awaitclient.People.Where(p =>p.FirstName.StartsWith("R")).OrderBy(p =>p.LastName).Take(10).ToListAsync();foreach(varpersoninpeople){Console.WriteLine($"{person.FirstName}{person.LastName}");}

🎯 Why Forge.OData.Client?

Stop writing boilerplate. Stop manually creating DTOs. Stop fighting with HTTP requests and JSON parsing.

You have an OData API. You want to use it. The OData CLI does the heavy lifting:

  • Downloads the metadata from your OData service
  • Generates all model classes with proper types and attributes
  • Creates a type-safe client with IntelliSense support
  • Translates LINQ to OData queries automatically
  • Handles JSON serialization with optimized converters

All this happens during your normal dotnet build. No runtime reflection. No performance overhead.

📦 Installation

Install the Forge OData CLI as a global tool:

dotnet tool install --global Forge.OData.CLI

Or install it locally in your project for team consistency:

dotnet new tool-manifest # if you don't have one already
dotnet tool install --local Forge.OData.CLI

🚀 Usage Examples

Basic Usage: Connect to Any OData Service

# Simple: Just provide the endpoint
dotnet odata add --endpoint https://services.odata.org/V4/Northwind/Northwind.svc

The CLI will:

  1. Download the $metadata from the endpoint
  2. Generate a client class named NorthwindClient (derived from URL)
  3. Create all model classes for entities (Products, Orders, Customers, etc.)
  4. Configure your project automatically

Custom Client Name

# Give your client a meaningful name
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name CompanyDataService

Now you'll have a CompanyDataService class instead of a generic name.

Organize Clients in Subdirectories

# Keep your OData clients organized
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name InventoryService \
--output-path Services/OData

This creates:

  • File: Services/OData/InventoryService.cs
  • Namespace: YourProject.Services.OData

Custom Namespace

# Control the namespace for better organization
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name ProductCatalog \
--namespace MyCompany.External.Services

Multiple OData Services in One Project

# Add multiple services - they all work together
dotnet odata add --endpoint https://api.products.com/odata --client-name ProductService
dotnet odata add --endpoint https://api.orders.com/odata --client-name OrderService
dotnet odata add --endpoint https://api.customers.com/odata --client-name CustomerService

Each client is independent, and you can use them side by side in your application.

🔄 Keeping Metadata Up to Date

When the OData service changes (new entities, modified properties), just update:

dotnet odata update

This command:

  • Finds all OData clients in your project
  • Re-downloads metadata from their endpoints
  • Updates the metadata files
  • Rebuild to regenerate clients with the latest schema

Workflow example:

# Initial setup
dotnet odata add --endpoint https://api.example.com/odata --client-name ApiClient
dotnet build
# ... time passes, API changes ...# Update to latest schema
dotnet odata update
dotnet build # Regenerates with new metadata

💡 Real-World Example

Let's say you're building an app that needs to fetch data from the TripPin OData service:

# Step 1: Add the clientcd MyTravelApp
dotnet odata add \
--endpoint https://services.odata.org/V4/TripPinServiceRW \
--client-name TripPinService \
--output-path Services
# Step 2: Build
dotnet build

Now use it in your code:

usingMyTravelApp.Services;publicclassTravelService{privatereadonlyHttpClient_httpClient;publicTravelService(IHttpClientFactoryhttpClientFactory){_httpClient=httpClientFactory.CreateClient();}publicasyncTask<List<Person>>GetTravelersAsync(stringfirstNamePrefix){varclient=newTripPinService(_httpClient);// Type-safe LINQ queriesreturnawaitclient.People.Where(p =>p.FirstName.StartsWith(firstNamePrefix)).OrderBy(p =>p.LastName).ToListAsync();}publicasyncTask<Person>GetPersonWithTripsAsync(stringusername){varclient=newTripPinService(_httpClient);// Expand navigation propertiesvarpeople=awaitclient.People.Where(p =>p.UserName==username).Expand(p =>p.Trips).ToListAsync();returnpeople.FirstOrDefault();}}

That's it. No manual DTOs. No string-based queries. Just clean, type-safe code with full IntelliSense.

🔍 What You Get

When you run dotnet odata add, the tool generates:

1. Model Classes

publicclassProduct{[Key]publicintId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// ... all properties from metadata}

2. Optimized JSON Converters

Custom converters for each model that:

  • Deserialize JSON without reflection (faster!)
  • Handle nullable types correctly
  • Support all OData types

3. Type-Safe Client

publicpartialclassYourServiceClient{publicODataQueryable<Product>Products{get;}publicODataQueryable<Order>Orders{get;}publicODataQueryable<Customer>Customers{get;}// ... all entity sets from metadata}

4. LINQ Support

Write normal C# LINQ queries:

// This LINQ expression...varquery=client.Products.Where(p =>p.Price>10&&p.InStock).OrderBy(p =>p.Name).Skip(20).Take(10);// ...becomes this OData query automatically:// /Products?$filter=Price gt 10 and InStock eq true&$orderby=Name asc&$skip=20&$top=10

🎨 Supported LINQ Operations

LINQ ExpressionOData Query
.Where(p => p.Price > 10)$filter=Price gt 10
.Where(p => p.Name == "Test")$filter=Name eq 'Test'
.Where(p => p.InStock && p.Price < 100)$filter=InStock eq true and Price lt 100
.OrderBy(p => p.Name)$orderby=Name asc
.OrderByDescending(p => p.Price)$orderby=Price desc
.Skip(10)$skip=10
.Take(20)$top=20
.Select(p => new { p.Name, p.Price })$select=Name,Price
.Expand(o => o.Product)$expand=Product

String methods work too:

  • .Where(p => p.Name.StartsWith("A"))startswith(Name, 'A')
  • .Where(p => p.Name.EndsWith("Z"))endswith(Name, 'Z')
  • .Where(p => p.Name.Contains("mid"))contains(Name, 'mid')

🛠️ Advanced Features

Need more control? The tool supports advanced scenarios:

Attribute-Based Customization

After generating the initial client, you can customize it:

usingForge.OData.Attributes;namespaceMyApp.Services{[ODataClient(MetadataFile="ApiMetadata.xml",Endpoint="https://api.example.com/odata")]publicpartialclassApiClient{// Add your custom methodspublicasyncTask<Product?>GetFeaturedProductAsync(){varresults=awaitProducts.Where(p =>p.Featured).OrderByDescending(p =>p.Rating).Take(1).ToListAsync();returnresults.FirstOrDefault();}// Add custom propertiespublicstringServiceVersion=>"v2.0";}}

Working with Multiple Environments

// DevelopmentvardevClient=newApiClient(httpClient,"https://dev-api.example.com/odata");// ProductionvarprodClient=newApiClient(httpClient,"https://api.example.com/odata");

📚 More Information

  • Technical documentation: See CONTRIBUTE.md for detailed architecture, project structure, and contribution guidelines
  • Examples: Check the sample/ directory for working examples
  • Changelog: See CHANGELOG.md for version history

🤝 Contributing

We welcome contributions! Please see CONTRIBUTE.md for detailed information on:

  • Project architecture and structure
  • Development setup
  • Building and testing
  • Code generation workflow
  • Contribution guidelines

📄 License

MIT

About

OData Client with source generator

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Forge.OData.Client

Your project needs to connect to an OData service? Just point our CLI at the endpoint, and everything gets generated for you—models, client, type-safe queries. No manual work. No boilerplate. Just instant OData integration.

⚡ Quick Start

1. Install the CLI Tool

dotnet tool install --global Forge.OData.CLI

2. Add an OData Client to Your Project

Navigate to your project and run:

dotnet odata add --endpoint https://services.odata.org/V4/TripPinServiceRW

3. Build and Code

dotnet build

That's it! You now have a fully functional, type-safe OData client ready to use:

usingvarhttpClient=newHttpClient();varclient=newTripPinServiceRWClient(httpClient);// Query with LINQ - it just works!varpeople=awaitclient.People.Where(p =>p.FirstName.StartsWith("R")).OrderBy(p =>p.LastName).Take(10).ToListAsync();foreach(varpersoninpeople){Console.WriteLine($"{person.FirstName}{person.LastName}");}

🎯 Why Forge.OData.Client?

Stop writing boilerplate. Stop manually creating DTOs. Stop fighting with HTTP requests and JSON parsing.

You have an OData API. You want to use it. The OData CLI does the heavy lifting:

  • Downloads the metadata from your OData service
  • Generates all model classes with proper types and attributes
  • Creates a type-safe client with IntelliSense support
  • Translates LINQ to OData queries automatically
  • Handles JSON serialization with optimized converters

All this happens during your normal dotnet build. No runtime reflection. No performance overhead.

📦 Installation

Install the Forge OData CLI as a global tool:

dotnet tool install --global Forge.OData.CLI

Or install it locally in your project for team consistency:

dotnet new tool-manifest # if you don't have one already
dotnet tool install --local Forge.OData.CLI

🚀 Usage Examples

Basic Usage: Connect to Any OData Service

# Simple: Just provide the endpoint
dotnet odata add --endpoint https://services.odata.org/V4/Northwind/Northwind.svc

The CLI will:

  1. Download the $metadata from the endpoint
  2. Generate a client class named NorthwindClient (derived from URL)
  3. Create all model classes for entities (Products, Orders, Customers, etc.)
  4. Configure your project automatically

Custom Client Name

# Give your client a meaningful name
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name CompanyDataService

Now you'll have a CompanyDataService class instead of a generic name.

Organize Clients in Subdirectories

# Keep your OData clients organized
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name InventoryService \
--output-path Services/OData

This creates:

  • File: Services/OData/InventoryService.cs
  • Namespace: YourProject.Services.OData

Custom Namespace

# Control the namespace for better organization
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name ProductCatalog \
--namespace MyCompany.External.Services

Multiple OData Services in One Project

# Add multiple services - they all work together
dotnet odata add --endpoint https://api.products.com/odata --client-name ProductService
dotnet odata add --endpoint https://api.orders.com/odata --client-name OrderService
dotnet odata add --endpoint https://api.customers.com/odata --client-name CustomerService

Each client is independent, and you can use them side by side in your application.

🔄 Keeping Metadata Up to Date

When the OData service changes (new entities, modified properties), just update:

dotnet odata update

This command:

  • Finds all OData clients in your project
  • Re-downloads metadata from their endpoints
  • Updates the metadata files
  • Rebuild to regenerate clients with the latest schema

Workflow example:

# Initial setup
dotnet odata add --endpoint https://api.example.com/odata --client-name ApiClient
dotnet build
# ... time passes, API changes ...# Update to latest schema
dotnet odata update
dotnet build # Regenerates with new metadata

💡 Real-World Example

Let's say you're building an app that needs to fetch data from the TripPin OData service:

# Step 1: Add the clientcd MyTravelApp
dotnet odata add \
--endpoint https://services.odata.org/V4/TripPinServiceRW \
--client-name TripPinService \
--output-path Services
# Step 2: Build
dotnet build

Now use it in your code:

usingMyTravelApp.Services;publicclassTravelService{privatereadonlyHttpClient_httpClient;publicTravelService(IHttpClientFactoryhttpClientFactory){_httpClient=httpClientFactory.CreateClient();}publicasyncTask<List<Person>>GetTravelersAsync(stringfirstNamePrefix){varclient=newTripPinService(_httpClient);// Type-safe LINQ queriesreturnawaitclient.People.Where(p =>p.FirstName.StartsWith(firstNamePrefix)).OrderBy(p =>p.LastName).ToListAsync();}publicasyncTask<Person>GetPersonWithTripsAsync(stringusername){varclient=newTripPinService(_httpClient);// Expand navigation propertiesvarpeople=awaitclient.People.Where(p =>p.UserName==username).Expand(p =>p.Trips).ToListAsync();returnpeople.FirstOrDefault();}}

That's it. No manual DTOs. No string-based queries. Just clean, type-safe code with full IntelliSense.

🔍 What You Get

When you run dotnet odata add, the tool generates:

1. Model Classes

publicclassProduct{[Key]publicintId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// ... all properties from metadata}

2. Optimized JSON Converters

Custom converters for each model that:

  • Deserialize JSON without reflection (faster!)
  • Handle nullable types correctly
  • Support all OData types

3. Type-Safe Client

publicpartialclassYourServiceClient{publicODataQueryable<Product>Products{get;}publicODataQueryable<Order>Orders{get;}publicODataQueryable<Customer>Customers{get;}// ... all entity sets from metadata}

4. LINQ Support

Write normal C# LINQ queries:

// This LINQ expression...varquery=client.Products.Where(p =>p.Price>10&&p.InStock).OrderBy(p =>p.Name).Skip(20).Take(10);// ...becomes this OData query automatically:// /Products?$filter=Price gt 10 and InStock eq true&$orderby=Name asc&$skip=20&$top=10

🎨 Supported LINQ Operations

LINQ ExpressionOData Query
.Where(p => p.Price > 10)$filter=Price gt 10
.Where(p => p.Name == "Test")$filter=Name eq 'Test'
.Where(p => p.InStock && p.Price < 100)$filter=InStock eq true and Price lt 100
.OrderBy(p => p.Name)$orderby=Name asc
.OrderByDescending(p => p.Price)$orderby=Price desc
.Skip(10)$skip=10
.Take(20)$top=20
.Select(p => new { p.Name, p.Price })$select=Name,Price
.Expand(o => o.Product)$expand=Product

String methods work too:

  • .Where(p => p.Name.StartsWith("A"))startswith(Name, 'A')
  • .Where(p => p.Name.EndsWith("Z"))endswith(Name, 'Z')
  • .Where(p => p.Name.Contains("mid"))contains(Name, 'mid')

🛠️ Advanced Features

Need more control? The tool supports advanced scenarios:

Attribute-Based Customization

After generating the initial client, you can customize it:

usingForge.OData.Attributes;namespaceMyApp.Services{[ODataClient(MetadataFile="ApiMetadata.xml",Endpoint="https://api.example.com/odata")]publicpartialclassApiClient{// Add your custom methodspublicasyncTask<Product?>GetFeaturedProductAsync(){varresults=awaitProducts.Where(p =>p.Featured).OrderByDescending(p =>p.Rating).Take(1).ToListAsync();returnresults.FirstOrDefault();}// Add custom propertiespublicstringServiceVersion=>"v2.0";}}

Working with Multiple Environments

// DevelopmentvardevClient=newApiClient(httpClient,"https://dev-api.example.com/odata");// ProductionvarprodClient=newApiClient(httpClient,"https://api.example.com/odata");

📚 More Information

  • Technical documentation: See CONTRIBUTE.md for detailed architecture, project structure, and contribution guidelines
  • Examples: Check the sample/ directory for working examples
  • Changelog: See CHANGELOG.md for version history

🤝 Contributing

We welcome contributions! Please see CONTRIBUTE.md for detailed information on:

  • Project architecture and structure
  • Development setup
  • Building and testing
  • Code generation workflow
  • Contribution guidelines

📄 License

MIT

About

OData Client with source generator

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Forge.OData.Client

Your project needs to connect to an OData service? Just point our CLI at the endpoint, and everything gets generated for you—models, client, type-safe queries. No manual work. No boilerplate. Just instant OData integration.

⚡ Quick Start

1. Install the CLI Tool

dotnet tool install --global Forge.OData.CLI

2. Add an OData Client to Your Project

Navigate to your project and run:

dotnet odata add --endpoint https://services.odata.org/V4/TripPinServiceRW

3. Build and Code

dotnet build

That's it! You now have a fully functional, type-safe OData client ready to use:

usingvarhttpClient=newHttpClient();varclient=newTripPinServiceRWClient(httpClient);// Query with LINQ - it just works!varpeople=awaitclient.People.Where(p =>p.FirstName.StartsWith("R")).OrderBy(p =>p.LastName).Take(10).ToListAsync();foreach(varpersoninpeople){Console.WriteLine($"{person.FirstName}{person.LastName}");}

🎯 Why Forge.OData.Client?

Stop writing boilerplate. Stop manually creating DTOs. Stop fighting with HTTP requests and JSON parsing.

You have an OData API. You want to use it. The OData CLI does the heavy lifting:

  • Downloads the metadata from your OData service
  • Generates all model classes with proper types and attributes
  • Creates a type-safe client with IntelliSense support
  • Translates LINQ to OData queries automatically
  • Handles JSON serialization with optimized converters

All this happens during your normal dotnet build. No runtime reflection. No performance overhead.

📦 Installation

Install the Forge OData CLI as a global tool:

dotnet tool install --global Forge.OData.CLI

Or install it locally in your project for team consistency:

dotnet new tool-manifest # if you don't have one already
dotnet tool install --local Forge.OData.CLI

🚀 Usage Examples

Basic Usage: Connect to Any OData Service

# Simple: Just provide the endpoint
dotnet odata add --endpoint https://services.odata.org/V4/Northwind/Northwind.svc

The CLI will:

  1. Download the $metadata from the endpoint
  2. Generate a client class named NorthwindClient (derived from URL)
  3. Create all model classes for entities (Products, Orders, Customers, etc.)
  4. Configure your project automatically

Custom Client Name

# Give your client a meaningful name
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name CompanyDataService

Now you'll have a CompanyDataService class instead of a generic name.

Organize Clients in Subdirectories

# Keep your OData clients organized
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name InventoryService \
--output-path Services/OData

This creates:

  • File: Services/OData/InventoryService.cs
  • Namespace: YourProject.Services.OData

Custom Namespace

# Control the namespace for better organization
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name ProductCatalog \
--namespace MyCompany.External.Services

Multiple OData Services in One Project

# Add multiple services - they all work together
dotnet odata add --endpoint https://api.products.com/odata --client-name ProductService
dotnet odata add --endpoint https://api.orders.com/odata --client-name OrderService
dotnet odata add --endpoint https://api.customers.com/odata --client-name CustomerService

Each client is independent, and you can use them side by side in your application.

🔄 Keeping Metadata Up to Date

When the OData service changes (new entities, modified properties), just update:

dotnet odata update

This command:

  • Finds all OData clients in your project
  • Re-downloads metadata from their endpoints
  • Updates the metadata files
  • Rebuild to regenerate clients with the latest schema

Workflow example:

# Initial setup
dotnet odata add --endpoint https://api.example.com/odata --client-name ApiClient
dotnet build
# ... time passes, API changes ...# Update to latest schema
dotnet odata update
dotnet build # Regenerates with new metadata

💡 Real-World Example

Let's say you're building an app that needs to fetch data from the TripPin OData service:

# Step 1: Add the clientcd MyTravelApp
dotnet odata add \
--endpoint https://services.odata.org/V4/TripPinServiceRW \
--client-name TripPinService \
--output-path Services
# Step 2: Build
dotnet build

Now use it in your code:

usingMyTravelApp.Services;publicclassTravelService{privatereadonlyHttpClient_httpClient;publicTravelService(IHttpClientFactoryhttpClientFactory){_httpClient=httpClientFactory.CreateClient();}publicasyncTask<List<Person>>GetTravelersAsync(stringfirstNamePrefix){varclient=newTripPinService(_httpClient);// Type-safe LINQ queriesreturnawaitclient.People.Where(p =>p.FirstName.StartsWith(firstNamePrefix)).OrderBy(p =>p.LastName).ToListAsync();}publicasyncTask<Person>GetPersonWithTripsAsync(stringusername){varclient=newTripPinService(_httpClient);// Expand navigation propertiesvarpeople=awaitclient.People.Where(p =>p.UserName==username).Expand(p =>p.Trips).ToListAsync();returnpeople.FirstOrDefault();}}

That's it. No manual DTOs. No string-based queries. Just clean, type-safe code with full IntelliSense.

🔍 What You Get

When you run dotnet odata add, the tool generates:

1. Model Classes

publicclassProduct{[Key]publicintId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// ... all properties from metadata}

2. Optimized JSON Converters

Custom converters for each model that:

  • Deserialize JSON without reflection (faster!)
  • Handle nullable types correctly
  • Support all OData types

3. Type-Safe Client

publicpartialclassYourServiceClient{publicODataQueryable<Product>Products{get;}publicODataQueryable<Order>Orders{get;}publicODataQueryable<Customer>Customers{get;}// ... all entity sets from metadata}

4. LINQ Support

Write normal C# LINQ queries:

// This LINQ expression...varquery=client.Products.Where(p =>p.Price>10&&p.InStock).OrderBy(p =>p.Name).Skip(20).Take(10);// ...becomes this OData query automatically:// /Products?$filter=Price gt 10 and InStock eq true&$orderby=Name asc&$skip=20&$top=10

🎨 Supported LINQ Operations

LINQ ExpressionOData Query
.Where(p => p.Price > 10)$filter=Price gt 10
.Where(p => p.Name == "Test")$filter=Name eq 'Test'
.Where(p => p.InStock && p.Price < 100)$filter=InStock eq true and Price lt 100
.OrderBy(p => p.Name)$orderby=Name asc
.OrderByDescending(p => p.Price)$orderby=Price desc
.Skip(10)$skip=10
.Take(20)$top=20
.Select(p => new { p.Name, p.Price })$select=Name,Price
.Expand(o => o.Product)$expand=Product

String methods work too:

  • .Where(p => p.Name.StartsWith("A"))startswith(Name, 'A')
  • .Where(p => p.Name.EndsWith("Z"))endswith(Name, 'Z')
  • .Where(p => p.Name.Contains("mid"))contains(Name, 'mid')

🛠️ Advanced Features

Need more control? The tool supports advanced scenarios:

Attribute-Based Customization

After generating the initial client, you can customize it:

usingForge.OData.Attributes;namespaceMyApp.Services{[ODataClient(MetadataFile="ApiMetadata.xml",Endpoint="https://api.example.com/odata")]publicpartialclassApiClient{// Add your custom methodspublicasyncTask<Product?>GetFeaturedProductAsync(){varresults=awaitProducts.Where(p =>p.Featured).OrderByDescending(p =>p.Rating).Take(1).ToListAsync();returnresults.FirstOrDefault();}// Add custom propertiespublicstringServiceVersion=>"v2.0";}}

Working with Multiple Environments

// DevelopmentvardevClient=newApiClient(httpClient,"https://dev-api.example.com/odata");// ProductionvarprodClient=newApiClient(httpClient,"https://api.example.com/odata");

📚 More Information

  • Technical documentation: See CONTRIBUTE.md for detailed architecture, project structure, and contribution guidelines
  • Examples: Check the sample/ directory for working examples
  • Changelog: See CHANGELOG.md for version history

🤝 Contributing

We welcome contributions! Please see CONTRIBUTE.md for detailed information on:

  • Project architecture and structure
  • Development setup
  • Building and testing
  • Code generation workflow
  • Contribution guidelines

📄 License

MIT

About

OData Client with source generator

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Forge.OData.Client

Your project needs to connect to an OData service? Just point our CLI at the endpoint, and everything gets generated for you—models, client, type-safe queries. No manual work. No boilerplate. Just instant OData integration.

⚡ Quick Start

1. Install the CLI Tool

dotnet tool install --global Forge.OData.CLI

2. Add an OData Client to Your Project

Navigate to your project and run:

dotnet odata add --endpoint https://services.odata.org/V4/TripPinServiceRW

3. Build and Code

dotnet build

That's it! You now have a fully functional, type-safe OData client ready to use:

usingvarhttpClient=newHttpClient();varclient=newTripPinServiceRWClient(httpClient);// Query with LINQ - it just works!varpeople=awaitclient.People.Where(p =>p.FirstName.StartsWith("R")).OrderBy(p =>p.LastName).Take(10).ToListAsync();foreach(varpersoninpeople){Console.WriteLine($"{person.FirstName}{person.LastName}");}

🎯 Why Forge.OData.Client?

Stop writing boilerplate. Stop manually creating DTOs. Stop fighting with HTTP requests and JSON parsing.

You have an OData API. You want to use it. The OData CLI does the heavy lifting:

  • Downloads the metadata from your OData service
  • Generates all model classes with proper types and attributes
  • Creates a type-safe client with IntelliSense support
  • Translates LINQ to OData queries automatically
  • Handles JSON serialization with optimized converters

All this happens during your normal dotnet build. No runtime reflection. No performance overhead.

📦 Installation

Install the Forge OData CLI as a global tool:

dotnet tool install --global Forge.OData.CLI

Or install it locally in your project for team consistency:

dotnet new tool-manifest # if you don't have one already
dotnet tool install --local Forge.OData.CLI

🚀 Usage Examples

Basic Usage: Connect to Any OData Service

# Simple: Just provide the endpoint
dotnet odata add --endpoint https://services.odata.org/V4/Northwind/Northwind.svc

The CLI will:

  1. Download the $metadata from the endpoint
  2. Generate a client class named NorthwindClient (derived from URL)
  3. Create all model classes for entities (Products, Orders, Customers, etc.)
  4. Configure your project automatically

Custom Client Name

# Give your client a meaningful name
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name CompanyDataService

Now you'll have a CompanyDataService class instead of a generic name.

Organize Clients in Subdirectories

# Keep your OData clients organized
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name InventoryService \
--output-path Services/OData

This creates:

  • File: Services/OData/InventoryService.cs
  • Namespace: YourProject.Services.OData

Custom Namespace

# Control the namespace for better organization
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name ProductCatalog \
--namespace MyCompany.External.Services

Multiple OData Services in One Project

# Add multiple services - they all work together
dotnet odata add --endpoint https://api.products.com/odata --client-name ProductService
dotnet odata add --endpoint https://api.orders.com/odata --client-name OrderService
dotnet odata add --endpoint https://api.customers.com/odata --client-name CustomerService

Each client is independent, and you can use them side by side in your application.

🔄 Keeping Metadata Up to Date

When the OData service changes (new entities, modified properties), just update:

dotnet odata update

This command:

  • Finds all OData clients in your project
  • Re-downloads metadata from their endpoints
  • Updates the metadata files
  • Rebuild to regenerate clients with the latest schema

Workflow example:

# Initial setup
dotnet odata add --endpoint https://api.example.com/odata --client-name ApiClient
dotnet build
# ... time passes, API changes ...# Update to latest schema
dotnet odata update
dotnet build # Regenerates with new metadata

💡 Real-World Example

Let's say you're building an app that needs to fetch data from the TripPin OData service:

# Step 1: Add the clientcd MyTravelApp
dotnet odata add \
--endpoint https://services.odata.org/V4/TripPinServiceRW \
--client-name TripPinService \
--output-path Services
# Step 2: Build
dotnet build

Now use it in your code:

usingMyTravelApp.Services;publicclassTravelService{privatereadonlyHttpClient_httpClient;publicTravelService(IHttpClientFactoryhttpClientFactory){_httpClient=httpClientFactory.CreateClient();}publicasyncTask<List<Person>>GetTravelersAsync(stringfirstNamePrefix){varclient=newTripPinService(_httpClient);// Type-safe LINQ queriesreturnawaitclient.People.Where(p =>p.FirstName.StartsWith(firstNamePrefix)).OrderBy(p =>p.LastName).ToListAsync();}publicasyncTask<Person>GetPersonWithTripsAsync(stringusername){varclient=newTripPinService(_httpClient);// Expand navigation propertiesvarpeople=awaitclient.People.Where(p =>p.UserName==username).Expand(p =>p.Trips).ToListAsync();returnpeople.FirstOrDefault();}}

That's it. No manual DTOs. No string-based queries. Just clean, type-safe code with full IntelliSense.

🔍 What You Get

When you run dotnet odata add, the tool generates:

1. Model Classes

publicclassProduct{[Key]publicintId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// ... all properties from metadata}

2. Optimized JSON Converters

Custom converters for each model that:

  • Deserialize JSON without reflection (faster!)
  • Handle nullable types correctly
  • Support all OData types

3. Type-Safe Client

publicpartialclassYourServiceClient{publicODataQueryable<Product>Products{get;}publicODataQueryable<Order>Orders{get;}publicODataQueryable<Customer>Customers{get;}// ... all entity sets from metadata}

4. LINQ Support

Write normal C# LINQ queries:

// This LINQ expression...varquery=client.Products.Where(p =>p.Price>10&&p.InStock).OrderBy(p =>p.Name).Skip(20).Take(10);// ...becomes this OData query automatically:// /Products?$filter=Price gt 10 and InStock eq true&$orderby=Name asc&$skip=20&$top=10

🎨 Supported LINQ Operations

LINQ ExpressionOData Query
.Where(p => p.Price > 10)$filter=Price gt 10
.Where(p => p.Name == "Test")$filter=Name eq 'Test'
.Where(p => p.InStock && p.Price < 100)$filter=InStock eq true and Price lt 100
.OrderBy(p => p.Name)$orderby=Name asc
.OrderByDescending(p => p.Price)$orderby=Price desc
.Skip(10)$skip=10
.Take(20)$top=20
.Select(p => new { p.Name, p.Price })$select=Name,Price
.Expand(o => o.Product)$expand=Product

String methods work too:

  • .Where(p => p.Name.StartsWith("A"))startswith(Name, 'A')
  • .Where(p => p.Name.EndsWith("Z"))endswith(Name, 'Z')
  • .Where(p => p.Name.Contains("mid"))contains(Name, 'mid')

🛠️ Advanced Features

Need more control? The tool supports advanced scenarios:

Attribute-Based Customization

After generating the initial client, you can customize it:

usingForge.OData.Attributes;namespaceMyApp.Services{[ODataClient(MetadataFile="ApiMetadata.xml",Endpoint="https://api.example.com/odata")]publicpartialclassApiClient{// Add your custom methodspublicasyncTask<Product?>GetFeaturedProductAsync(){varresults=awaitProducts.Where(p =>p.Featured).OrderByDescending(p =>p.Rating).Take(1).ToListAsync();returnresults.FirstOrDefault();}// Add custom propertiespublicstringServiceVersion=>"v2.0";}}

Working with Multiple Environments

// DevelopmentvardevClient=newApiClient(httpClient,"https://dev-api.example.com/odata");// ProductionvarprodClient=newApiClient(httpClient,"https://api.example.com/odata");

📚 More Information

  • Technical documentation: See CONTRIBUTE.md for detailed architecture, project structure, and contribution guidelines
  • Examples: Check the sample/ directory for working examples
  • Changelog: See CHANGELOG.md for version history

🤝 Contributing

We welcome contributions! Please see CONTRIBUTE.md for detailed information on:

  • Project architecture and structure
  • Development setup
  • Building and testing
  • Code generation workflow
  • Contribution guidelines

📄 License

MIT

About

OData Client with source generator

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Forge.OData.Client

Your project needs to connect to an OData service? Just point our CLI at the endpoint, and everything gets generated for you—models, client, type-safe queries. No manual work. No boilerplate. Just instant OData integration.

⚡ Quick Start

1. Install the CLI Tool

dotnet tool install --global Forge.OData.CLI

2. Add an OData Client to Your Project

Navigate to your project and run:

dotnet odata add --endpoint https://services.odata.org/V4/TripPinServiceRW

3. Build and Code

dotnet build

That's it! You now have a fully functional, type-safe OData client ready to use:

usingvarhttpClient=newHttpClient();varclient=newTripPinServiceRWClient(httpClient);// Query with LINQ - it just works!varpeople=awaitclient.People.Where(p =>p.FirstName.StartsWith("R")).OrderBy(p =>p.LastName).Take(10).ToListAsync();foreach(varpersoninpeople){Console.WriteLine($"{person.FirstName}{person.LastName}");}

🎯 Why Forge.OData.Client?

Stop writing boilerplate. Stop manually creating DTOs. Stop fighting with HTTP requests and JSON parsing.

You have an OData API. You want to use it. The OData CLI does the heavy lifting:

  • Downloads the metadata from your OData service
  • Generates all model classes with proper types and attributes
  • Creates a type-safe client with IntelliSense support
  • Translates LINQ to OData queries automatically
  • Handles JSON serialization with optimized converters

All this happens during your normal dotnet build. No runtime reflection. No performance overhead.

📦 Installation

Install the Forge OData CLI as a global tool:

dotnet tool install --global Forge.OData.CLI

Or install it locally in your project for team consistency:

dotnet new tool-manifest # if you don't have one already
dotnet tool install --local Forge.OData.CLI

🚀 Usage Examples

Basic Usage: Connect to Any OData Service

# Simple: Just provide the endpoint
dotnet odata add --endpoint https://services.odata.org/V4/Northwind/Northwind.svc

The CLI will:

  1. Download the $metadata from the endpoint
  2. Generate a client class named NorthwindClient (derived from URL)
  3. Create all model classes for entities (Products, Orders, Customers, etc.)
  4. Configure your project automatically

Custom Client Name

# Give your client a meaningful name
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name CompanyDataService

Now you'll have a CompanyDataService class instead of a generic name.

Organize Clients in Subdirectories

# Keep your OData clients organized
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name InventoryService \
--output-path Services/OData

This creates:

  • File: Services/OData/InventoryService.cs
  • Namespace: YourProject.Services.OData

Custom Namespace

# Control the namespace for better organization
dotnet odata add \
--endpoint https://api.example.com/odata \
--client-name ProductCatalog \
--namespace MyCompany.External.Services

Multiple OData Services in One Project

# Add multiple services - they all work together
dotnet odata add --endpoint https://api.products.com/odata --client-name ProductService
dotnet odata add --endpoint https://api.orders.com/odata --client-name OrderService
dotnet odata add --endpoint https://api.customers.com/odata --client-name CustomerService

Each client is independent, and you can use them side by side in your application.

🔄 Keeping Metadata Up to Date

When the OData service changes (new entities, modified properties), just update:

dotnet odata update

This command:

  • Finds all OData clients in your project
  • Re-downloads metadata from their endpoints
  • Updates the metadata files
  • Rebuild to regenerate clients with the latest schema

Workflow example:

# Initial setup
dotnet odata add --endpoint https://api.example.com/odata --client-name ApiClient
dotnet build
# ... time passes, API changes ...# Update to latest schema
dotnet odata update
dotnet build # Regenerates with new metadata

💡 Real-World Example

Let's say you're building an app that needs to fetch data from the TripPin OData service:

# Step 1: Add the clientcd MyTravelApp
dotnet odata add \
--endpoint https://services.odata.org/V4/TripPinServiceRW \
--client-name TripPinService \
--output-path Services
# Step 2: Build
dotnet build

Now use it in your code:

usingMyTravelApp.Services;publicclassTravelService{privatereadonlyHttpClient_httpClient;publicTravelService(IHttpClientFactoryhttpClientFactory){_httpClient=httpClientFactory.CreateClient();}publicasyncTask<List<Person>>GetTravelersAsync(stringfirstNamePrefix){varclient=newTripPinService(_httpClient);// Type-safe LINQ queriesreturnawaitclient.People.Where(p =>p.FirstName.StartsWith(firstNamePrefix)).OrderBy(p =>p.LastName).ToListAsync();}publicasyncTask<Person>GetPersonWithTripsAsync(stringusername){varclient=newTripPinService(_httpClient);// Expand navigation propertiesvarpeople=awaitclient.People.Where(p =>p.UserName==username).Expand(p =>p.Trips).ToListAsync();returnpeople.FirstOrDefault();}}

That's it. No manual DTOs. No string-based queries. Just clean, type-safe code with full IntelliSense.

🔍 What You Get

When you run dotnet odata add, the tool generates:

1. Model Classes

publicclassProduct{[Key]publicintId{get;set;}publicstringName{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// ... all properties from metadata}

2. Optimized JSON Converters

Custom converters for each model that:

  • Deserialize JSON without reflection (faster!)
  • Handle nullable types correctly
  • Support all OData types

3. Type-Safe Client

publicpartialclassYourServiceClient{publicODataQueryable<Product>Products{get;}publicODataQueryable<Order>Orders{get;}publicODataQueryable<Customer>Customers{get;}// ... all entity sets from metadata}

4. LINQ Support

Write normal C# LINQ queries:

// This LINQ expression...varquery=client.Products.Where(p =>p.Price>10&&p.InStock).OrderBy(p =>p.Name).Skip(20).Take(10);// ...becomes this OData query automatically:// /Products?$filter=Price gt 10 and InStock eq true&$orderby=Name asc&$skip=20&$top=10

🎨 Supported LINQ Operations

LINQ ExpressionOData Query
.Where(p => p.Price > 10)$filter=Price gt 10
.Where(p => p.Name == "Test")$filter=Name eq 'Test'
.Where(p => p.InStock && p.Price < 100)$filter=InStock eq true and Price lt 100
.OrderBy(p => p.Name)$orderby=Name asc
.OrderByDescending(p => p.Price)$orderby=Price desc
.Skip(10)$skip=10
.Take(20)$top=20
.Select(p => new { p.Name, p.Price })$select=Name,Price
.Expand(o => o.Product)$expand=Product

String methods work too:

  • .Where(p => p.Name.StartsWith("A"))startswith(Name, 'A')
  • .Where(p => p.Name.EndsWith("Z"))endswith(Name, 'Z')
  • .Where(p => p.Name.Contains("mid"))contains(Name, 'mid')

🛠️ Advanced Features

Need more control? The tool supports advanced scenarios:

Attribute-Based Customization

After generating the initial client, you can customize it:

usingForge.OData.Attributes;namespaceMyApp.Services{[ODataClient(MetadataFile="ApiMetadata.xml",Endpoint="https://api.example.com/odata")]publicpartialclassApiClient{// Add your custom methodspublicasyncTask<Product?>GetFeaturedProductAsync(){varresults=awaitProducts.Where(p =>p.Featured).OrderByDescending(p =>p.Rating).Take(1).ToListAsync();returnresults.FirstOrDefault();}// Add custom propertiespublicstringServiceVersion=>"v2.0";}}

Working with Multiple Environments

// DevelopmentvardevClient=newApiClient(httpClient,"https://dev-api.example.com/odata");// ProductionvarprodClient=newApiClient(httpClient,"https://api.example.com/odata");

📚 More Information

  • Technical documentation: See CONTRIBUTE.md for detailed architecture, project structure, and contribution guidelines
  • Examples: Check the sample/ directory for working examples
  • Changelog: See CHANGELOG.md for version history

🤝 Contributing

We welcome contributions! Please see CONTRIBUTE.md for detailed information on:

  • Project architecture and structure
  • Development setup
  • Building and testing
  • Code generation workflow
  • Contribution guidelines

📄 License

MIT

About

OData Client with source generator

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages