Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

EF Core-like CouchDB experience for .NET!

License

NotificationsYou must be signed in to change notification settings

dhirensham/couchdb-net

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Downloads

CouchDB.NET

EF Core-like CouchDB experience for .NET!

LINQ queries

C# query example:

// SetuppublicclassMyDeathStarContext:CouchContext{publicCouchDatabase<Rebel>Rebels{get;set;}publicCouchDatabase<Clone>Clones{get;set;}protectedoverridevoidOnConfiguring(CouchOptionsBuilderoptionsBuilder){optionsBuilder.UseEndpoint("http://localhost:5984/").EnsureDatabaseExists().UseBasicAuthentication(username:"anakin",password:"empirerule");}}// Usageawaitusingvarcontext=newMyDeathStarContext();varskywalkers=awaitcontext.Rebels.Where(r=>r.Surname=="Skywalker"&&(r.Battles.All(b=>b.Planet=="Naboo")||r.Battles.Any(b=>b.Planet=="Death Star"))).OrderByDescending(r=>r.Name).ThenByDescending(r=>r.Age).Take(2).Select(        r=>r.Name,        r=>r.Age}).ToListAsync();

The produced Mango JSON:

{"selector": {"$and": [      {"surname":"Skywalker"      },      {"$or": [          {"battles": {"$allMatch": {"planet":"Naboo"              }            }          },          {"battles": {"$elemMatch": {"planet":"Death Star"              }            }          }        ]      }    ]  },"sort": [    {"name":"desc" },    {"age":"desc" }  ],"limit":2,"fields": ["name","age"  ]}

Index

Getting started

  • Install it from NuGet:https://www.nuget.org/packages/CouchDB.NET
  • Create a context or a client, where localhost will be the IP address and 5984 is CouchDB standard tcp port:
    awaitusingvarcontext=newMyDeathStarContext(builder=>{});// orawaitusing(varclient=newCouchClient("http://localhost:5984", builder=>{})){}
  • Create a document class:
    publicclassRebel:CouchDocument
  • Get a database reference:
    varrebels=context.Rebels;// orvarrebels=client.GetDatabase<Rebel>();
  • Query the database
    varskywalkers=awaitrebels.Where(r=>r.Surname=="Skywalker").ToListAsync();

Queries

The database class exposes all the implemented LINQ methods like Where and OrderBy,those methods returns an IQueryable.

LINQ are supported natively to the following is possible:

varskywalkers=fromrincontext.Rebelswherer.Surname=="Skywalker"selectr;

Selector

The selector is created when the method Where (IQueryable) is called.If the Where method is not called in the expression, it will at an empty selector.

Combinations

MangoC#
$and&&
$or||
$not!
$nor!( || )
$alla.Contains(x)
$alla.Contains(list)
$elemMatcha.Any(condition)
$allMatcha.All(condition)

Conditions

MangoC#
$lt<
$lte<=
$eq (implicit)==
$ne!=
$gte>=
$gt>
$existso.FieldExists(s)
$typeo.IsCouchType(...)
$ino.In(list)
$nin!o.In(list)
$sizea.Count == x
$modn % x = y
$regexs.IsMatch(rx)

Native methods

MangoC#
limitTake(n)
skipSkip(n)
sortOrderBy(..)
sortOrderBy(..).ThenBy()
sortOrderByDescending(..)
sortOrderByDescending(..).ThenByDescending()
fieldsSelect(x => x.Prop1, x => x.Prop2)
fieldsConvert<SourceType, SimplerType>()
use_indexUseIndex("design_document")
use_indexUseIndex(new [] { "design_document", "index_name" })
rWithReadQuorum(n)
bookmarkUseBookmark(s)
updateWithoutIndexUpdate()
stableFromStable()
execution_statsIncludeExecutionStats()
conflictsIncludeConflicts()

Composite methods

Some methods that are not directly supported by CouchDB are converted to a composition of supported ones!

InputOutput
Min(d => d.Property)OrderBy(d => d.Property).Take(1).Select(d => d.Property).Min()
Max(d => d.Property)OrderByDescending(d => d.Property).Take(1).Select(d => d.Property).Max()
Sum(d => d.Property)Select(d => d.Property).Sum()
Average(d => d.Property)Select(d => d.Property).Average()
Any()Take(1).Any()
Any(d => condition)Where(d => condition).Take(1).Any()
All(d => condition)Where(d => !condition).Take(1).Any()
Single()Take(2).Single()
SingleOrDefault()Take(2).SingleOrDefault()
Single(d => condition)Where(d => condition).Take(2).Single()
SingleOrDefault(d => condition)Where(d => condition).Take(2).SingleOrDefault()
First()Take(1).First()
FirstOrDefault()Take(1).FirstOrDefault()
First(d => condition)Where(d => condition).Take(1).First()
FirstOrDefault(d => condition)Where(d => condition).Take(1).FirstOrDefault()
Last()Where(d => Last()
LastOrDefault()LastOrDefault()
Last(d => condition)Where(d => condition).Last()
LastOrDefault(d => condition)Where(d => condition).LastOrDefault()

INFO: AlsoSelect(d => d.Property),Min andMax are supported.

WARN: Since Max and Min usesort, anindex must be created.

All other IQueryables methods

Since v2.0IQueryable methods that are not natively supported will throw an exception.

Client operations

// CRUD from class name (rebels)varrebels=client.GetDatabase<Rebel>();varrebels=awaitclient.GetOrCreateDatabaseAsync<Rebel>();varrebels=awaitclient.CreateDatabaseAsync<Rebel>();awaitclient.DeleteDatabaseAsync<Rebel>();// CRUD specific namevarrebels=client.GetDatabase<Rebel>("naboo_rebels");varrebels=client.GetOrCreateDatabaseAsync<Rebel>("naboo_rebels");varrebels=awaitclient.CreateDatabaseAsync<Rebel>("naboo_rebels");awaitclient.DeleteDatabaseAsync("naboo_rebels");// UtilsvarisRunning=awaitclient.IsUpAsync();vardatabases=awaitclient.GetDatabasesNamesAsync();vartasks=awaitclient.GetActiveTasksAsync();

Database operations

// CRUDawaitrebels.AddAsync(rebel);awaitrebels.AddOrUpdateAsync(rebel);awaitrebels.RemoveAsync(rebel);varrebel=awaitrebels.FindAsync(id);varrebel=awaitrebels.FindAsync(id,withConflicts:true);varlist=awaitrebels.FindManyAsync(ids);varlist=awaitrebels.QueryAsync(someMangoJson);varlist=awaitrebels.QueryAsync(someMangoObject);// Bulkawaitrebels.AddOrUpdateRangeAsync(moreRebels);awaitrebels.DeleteRangeAsync(ids);awaitrebels.DeleteRangeAsync(moreRebels);// Utilsawaitrebels.CompactAsync();varinfo=awaitrebels.GetInfoAsync();// Securityawaitrebels.Security.SetInfoAsync(securityInfo);varsecurityInfo=awaitrebels.Security.GetInfoAsync();

Authentication

// Basic.UseBasicAuthentication("root","relax")// Cookie.UseCookieAuthentication("root","relax").UseCookieAuthentication("root","relax",cookieDuration)// Proxy.UseProxyAuthentication("root",new[]{"role1","role2"})// JTW.UseJwtAuthentication("token").UseJwtAuthentication(async()=>awaitNewTokenAsync())

Options

The second parameter of the client constructor is a function to configure CouchSettings fluently.

publicclassMyDeathStarContext:CouchContext{/* ... */protectedoverridevoidOnConfiguring(CouchOptionsBuilderoptionsBuilder){optionsBuilder.UseBasicAuthentication("root","relax").DisableEntitisPluralization()        ...}}// orvarclient=newCouchClient("http://localhost:5984", builder=>builder.UseBasicAuthentication("root","relax").DisableEntitisPluralization()    ...)
MethodDescription
UseBasicAuthenticationEnables basic authentication.
UseCookieAuthenticationEnables cookie authentication.
IgnoreCertificateValidationRemoves any SSL certificate validation.
ConfigureCertificateValidationSets a custom SSL validation rule.
DisableDocumentPluralizationDisables documents pluralization in requests.
SetDocumentCaseSets the format case for documents.
SetPropertyCaseSets the format case for properties.
SetNullValueHandlingSets how to handle null values.
DisableLogOutOnDisposeDisables log out on client dispose.
  • DocumentCaseTypes: None, UnderscoreCase(default), DashCase, KebabCase.
  • PropertyCaseTypes: None, CamelCase(default), PascalCase, UnderscoreCase, DashCase, KebabCase.

Custom JSON values

If you need custom values for documents and properties, it's possible to use JsonObject and JsonProperty attributes.

[JsonObject("custom-rebels")]publicclassOtherRebel:Rebel[JsonProperty("rebel_bith_date")]public DateTime BirthDate{get;set;}

Attachments

The driver fully support attachments, you can list, create, delete and download them.

// Get a documentvarluke=newRebel{Name="Luke",Age=19};// Add in memoryvarpathToDocument=@".\luke.txt"luke.Attachments.AddOrUpdate(pathToDocument,MediaTypeNames.Text.Plain);// Delete in memoryluke.Attachments.Delete("yoda.txt");// Saveluke=awaitrebels.CreateOrUpdateAsync(luke);// GetCouchAttachmentlukeTxt=luke.Attachments["luke.txt"];// Iterateforeach(CouchAttachmentattachmentinluke.Attachments){   ...}// DownloadstringdownloadFilePath=awaitrebels.DownloadAttachment(attachment,downloadFolderPath,"luke-downloaded.txt");//orStreamresponseStream=awaitrebels.DownloadAttachmentAsStreamAsync(attachment);

Revisions

The options forFindAsync(..) andAddOrUpdateAsync(..) support passing revision:

await_rebels.FindAsync("1",newFindOptions{Rev="1-xxx"});await_rebels.AddOrUpdateAsync(r,newAddOrUpdateOptions{Rev="1-xxx"});

For attachements revisions are supported byCouchAttachment class which is passingDocumentRev toDownloadAttachmentAsync(..) andDownloadAttachmentAsStreamAsync(..).

DB Changes Feed

The followingfeed modes are supported:normal,longpool andcontinuous.Also alloptions andfilter types are supported.

Continuous mode is probably the most useful and it's implemented with the newIAsyncEnumerable.

vartokenSource=newCancellationTokenSource();awaitforeach(varchangein_rebels.GetContinuousChangesAsync(options:null,filter:null,tokenSource.Token)){if(/* ... */){tokenSource.Cancel();}}

Feed Options

// Examplevaroptions=newChangesFeedOptions{Descending=true,Limit=10,Since="now",IncludeDocs=true};ChangesFeedResponse<Rebel>changes=awaitGetChangesAsync(options);

Feed Filter

// _doc_idsvarfilter=ChangesFeedFilter.DocumentIds(new[]{"luke","leia"});// _selectorvarfilter=ChangesFeedFilter.Selector<Rebel>(rebel=>rebel.Age==19);// _designvarfilter=ChangesFeedFilter.Design();// _viewvarfilter=ChangesFeedFilter.View(view);// UseChangesFeedResponse<Rebel>changes=awaitGetChangesAsync(options:null,filter);

Indexing

It is possible to create indexes to use when querying.

// Basic index creationawait_rebels.CreateIndexAsync("rebels_index", b=>b.IndexBy(r=>r.Surname)).ThenBy(r=>r.Name));// Descending index creationawait_rebels.CreateIndexAsync("rebels_index", b=>b.IndexByDescending(r=>r.Surname)).ThenByDescending(r=>r.Name));

Index Options

// Specifies the design document and/or whether a JSON index is partitioned or globalawait_rebels.CreateIndexAsync("rebels_index", b=>b.IndexBy(r=>r.Surname),newIndexOptions(){DesignDocument="surnames_ddoc",Partitioned=true});

Partial Indexes

// Create an index which excludes documents at index timeawait_rebels.CreateIndexAsync("skywalkers_index", b=>b.IndexBy(r=>r.Name).Where(r=>r.Surname=="Skywalker");

Indexes operations

// Get the list of indexesvarindexes=await_rebels.GetIndexesAsync();// Delete an indexesawait_rebels.DeleteIndexAsync(indexes[0]);// orawait_rebels.DeleteIndexAsync("surnames_ddoc",name:"surnames");

CouchContext Index Configuration

Finally it's possible to configure indexes on theCouchContext.

publicclassMyDeathStarContext:CouchContext{publicCouchDatabase<Rebel>Rebels{get;set;}// OnConfiguring(CouchOptionsBuilder optionsBuilder) { ... }protectedoverridevoidOnDatabaseCreating(CouchDatabaseBuilderdatabaseBuilder){databaseBuilder.Document<Rebel>().HasIndex("rebel_surnames_index", b=>b.IndexBy(b=>b.Surname));}}

Database Splitting

It is possible to use the same database for multiple types:

publicclassMyDeathStarContext:CouchContext{publicCouchDatabase<Rebel>Rebels{get;set;}publicCouchDatabase<Vehicle>Vehicles{get;set;}// OnConfiguring(CouchOptionsBuilder optionsBuilder) { ... }protectedoverridevoidOnDatabaseCreating(CouchDatabaseBuilderdatabaseBuilder){databaseBuilder.Document<Rebel>().ToDatabase("troops");databaseBuilder.Document<Vehicle>().ToDatabase("troops");}}

When multipleCouchDatabase point to the samedatabase, asplit_discriminator field is added on document creation.

When querying, a filter bysplit_discriminator is added automatically.

The field name can be overriden with theWithDatabaseSplitDiscriminator.

If you are not usingCouchContext, you can still use the database split feature:

varrebels=client.GetDatabase<Rebel>("troops",nameof(Rebel));varvehicles=client.GetDatabase<Vehicle>("troops",nameof(Vehicle));

Views

It's possible to query a view with the following:

varoptions=newCouchViewOptions<string[]>{StartKey=new[]{"Luke","Skywalker"},IncludeDocs=true};varviewRows=await_rebels.GetViewAsync<string[],RebelView>("jedi","by_name",options);// ORvardetails=await_rebels.GetDetailedViewAsync<int,BattleView>("battle","by_name",options);

You can also query a view with multiple options to get multiple results:

varlukeOptions=newCouchViewOptions<string[]>{Key=new[]{"Luke","Skywalker"},IncludeDocs=true};varyodaOptions=newCouchViewOptions<string[]>{Key=new[]{"Yoda"},IncludeDocs=true};varqueries=new[]{lukeOptions,yodaOptions};varresults=await_rebels.GetViewQueryAsync<string[],RebelView>("jedi","by_name",queries);varlukeRows=results[0];varyodaRows=results[1];// ORvardetails=await_rebels.GetDetailedViewQueryAsync<string[],RebelView>("jedi","by_name",queries);varlukeDetails=details[0];varyodaDetails=details[1];

Local (non-replicating) Documents

The Local (non-replicating) document interface allows you to create local documents that are not replicated to other databases.

vardocId="settings";varsettings=newRebelSettings{Id=docId,IsActive=true};// Createawait_rebels.LocalDocuments.CreateOrUpdateAsync(settings);// Get by IDsettings=await_rebels.LocalDocuments.GetAsync<RebelSettings>(docId);// Get allvardocs=awaitlocal.GetAsync();// SearchvarsearchOpt=newLocalDocumentsOptions{Descending=true,Limit=10,Conflicts=true};vardocs=awaitlocal.GetAsync(searchOpt);

Bookmark and Execution stats

If bookmark and execution stats must be retrieved, callToCouchList orToCouchListAsync.

varallRebels=awaitrebels.ToCouchListAsync();foreach(varrinallRebels){    ...}varb=allRebels.Bookmark;varex=allRebels.ExecutionStats;// .IncludeExecutionStats() must be called

Users

The driver natively support the_users database.

varusers=client.GetUsersDatabase();varluke=awaitusers.CreateAsync(newCouchUser(name:"luke",password:"lasersword"));

It's possible to extendCouchUser for store custom info.

varusers=client.GetUsersDatabase<CustomUser>();varluke=awaitusers.CreateAsync(newCustomUser(name:"luke",password:"lasersword"));

To change password:

luke=awaitusers.ChangeUserPassword(luke,"r2d2");

Replication

The driver provides the ability to configure and cancel replication between databases.

if(awaitclient.ReplicateAsync("anakin","jedi",newCouchReplication(){Continuous=true})){awaitclient.RemoveReplicationAsync("anakin","jedi",newCouchReplication(){Continuous=true});}

It is also possible to specify a selector to apply to the replication

awaitclient.ReplicateAsync("stormtroopers","deathstar",newCouchReplication(){Continuous=true,Selector=new{designation="FN-2187"}}));

Credentials can be specified as follows

awaitclient.ReplicateAsync("luke","jedi",newCouchReplication(){SourceCredentials=newCouchReplicationBasicCredentials()username:"luke",password:"r2d2")}));

Dependency Injection

As always you can leverage all the benefits of Dependency Injection.

Info: The context will be registered as asingleton.

  • Create aCouchContext with a constructor like the following:
publicclassMyDeathStarContext:CouchContext{publicCouchDatabase<Rebel>Rebels{get;set;}publicMyDeathStarContext(CouchOptions<MyDeathStarContext>options):base(options){}}
  • Register the context via any of supported containers (see appropriate section section below)

  • Inject the context:

// RebelsControllerpublicclassRebelsController:Controller{privatereadonlyMyDeathStarContext_context;publicRebelsController(MyDeathStarContextcontext){_context=context;}}

Microsoft container

// ConfigureServicesservices.AddCouchContext<MyDeathStarContext>(builder=>builder.UseEndpoint("http://localhost:5984").UseBasicAuthentication(username:"admin",password:"admin"));

Autofac container

// ConfigureServicesvarcontainerBuilder=newContainerBuilder();containerBuilder.AddCouchContext<MyDeathStarContext>(optionsBuilder=>optionsBuilder.UseEndpoint("http://localhost:5984").UseBasicAuthentication(username:"admin",password:"admin"));

Advanced

If requests have to be modified before each call, it's possible to override OnBeforeCallAsync.

protectedvirtualTaskOnBeforeCallAsync(HttpCallcall)

Also, the configurator hasConfigureFlurlClient to set custom HTTP client options.

Contributors

Ben Origas: Features, ideas and tests like SSL custom validation, multi queryable, async deadlock, cookie authentication and many others.

n9: Proxy authentication, some bug fixes, suggestions and the great feedback on the changes feed feature!

Marc: NullValueHandling, bug fixes and suggestions!

Panos: Help with Views and Table splitting.

Benjamin Höglinger-Stelzer,mwasson74,Emre ÇAĞLAR: Attachments improvements and fixes.

Dhiren Sham: Implementing replication.

Dmitriy Larionov: Revisions improvements.

About

EF Core-like CouchDB experience for .NET!

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C#98.3%
  • HTML1.5%
  • Other0.2%

[8]ページ先頭

©2009-2025 Movatter.jp