tumtumtum/Shaolinq(新しいタブで開きます)
主要言語: C#
ORM for .NET with full LINQ support for Postgres, Sqlite, MySql and SqlServer
- star 数
- 132
- watcher 数
- 16
- fork 数
- 21
- issue 数
- 43
- 最終更新
- 2021/08/05
主要言語: C#
ORM for .NET with full LINQ support for Postgres, Sqlite, MySql and SqlServer
Shaolinq is a powerful ORM and Linq provider for C# and .NET. It provides a thoughtful, fast and powerful alternative to Linq to SQL and the Entity Framework.
Notable features:
Motivation:
The lack of a suitable free code-first ORM with support for major open-source databases in 2007-2008 for .NET motivated me to design a write a new one with a query system based on C# operator overloading. I added LINQ support in mid 2008 after Microsoft released LINQ in .NET 3.5. The primary goals of Shaolinq was:
Define data model:
// Object inheriting from generic DataAccessObject to get "Id" primary key property [DataAccessObject] public abstract class Person : DataAccessObject<Guid> { [AutoIncrement] [PersistedMember] public abstract Guid Id { get; set; } [PersistedMember] public DateTime? Birthdate {get; set; } [PersistedMember] public abstract int Age { get; set; } [PersistedMember] public abstract string Name { get; set; } [PersistedMember] public abstract Person BestFriend { get; set; } [BackReference] public abstract BorrowedBook { get; set; } [Description] [Index(LowercaseIndex = true)] [ComputedTextMember("{Name} of age {Age}")] public abstract string Description { get; set; } } // Object inheriting from non generic DataAccessObject to manually define its own primary keys [DataAccessObject] public abstract Book : DataAccessObject { [PrimaryKey("$(TYPE_NAME)$(PROPERTY_NAME)")] [PersistedMember] public abstract long SerialNumber { get; set; } [PrimaryKey] [PersistedMember] public abstract string PublisherName { get; set; } [PersistedMember] public abstract string Title { get; set; } [RelatedDataAccessObjects] public abstract RelatedDataAccessObjects<Person> Borrowers { get; } } // The data access model - defines all types/tables [DataAccessModel] public abstract class ExampleModel : DataAccessModel { [DataAccessObjects] public abstract DataAccessObjects<Book> Books { get; } [DataAccessObjects] public abstract DataAccessObjects<Person> People { get; } }
Create SQLite database:
using Shaolinq; using Shaolinq.Sqlite; static void Main() { var configuration = SqliteConfiguration.Create(":memory:"); var model = DataAccessModel.BuildDataAccessModel<ExampleModel>(configuration); model.Create(DatabaseCreationOptions.DeleteExistingDatabase); }
Create MySQL database:
using Shaolinq; using Shaolinq.MySql; static void Main() { var configuration = = MySqlConfiguration.Create("ExampleDatabase", "localhost", "root", "root"); var model = DataAccessModel.BuildDataAccessModel<ExampleModel>(configuration); model.Create(DatabaseCreationOptions.DeleteExistingDatabase); }
Insert objects:
using (var scope = new DataAccessScope()) { var person = model.people.Create(); person.Name = "Steve"; scope.Complete(); }
// Insert object using distributed transaction
using (var scope = new TransactionScope()) { var person = model.people.Create(); person.Name = "Steve"; person.Age = 18; scope.Complete(); }
Insert object asynchronously:
using (var scope = new DataAccessScope()) { var person = model.People.Create(); person.Name = "Steve"; person.Age = 18; await scope.CompleteAsync(); }
Update object asynchronously and without needing to performa SELECT query:
using (var scope = new DataAccessScope()) { // Gets a reference to an object with a composite primary key without hitting the database var book = model.Books.GetReference(new { Id = 100, PublisherName = "Penguin" }); book.Title = "Expert Shaolinq"; // Will throw if the book (above) does not exist on commit in a single trip to the database await scope.CompleteAsync(); }
Perform queries with implicit joins and explicit joins using Include. Query for all books and for each book all borrowers and for each borrower their best friend. Then print out the borrower and their best friends' name.
var books = await model.Books.Include(c => c.Borrowers.IncludedItems().BestFriend).ToListAsync(); foreach (var value in books.Borrowers.Items().SelectMany(c => new { c.Name, BestFriendName = c.BestFriend.Name }))) { Console.WriteLine($"Borrower: {value.Name} BestFriend: {value.BestFriend.Name}") }
Asynchronously find the age of all people in the database
var averageAge = await model.People.AverageAsync(c => c.Age); Console.WriteLine($"Average age is {averageAge}");
Delete all people named Steve from the database using LINQ syntax
using (var scope = new DataAccessScope()) { await model.People.Where(c => c.Name == "Steve").DeleteAsync(); // or await model.People.DeleteAsync(c => c.Name == "Steve"); Console.WriteLine("Deleted all people named Steve"); await scope.CompleteAsync(); }
Asynchronously enumerate all people whos name starts with Steve using fast server-side case-insensitive index
using (var enumerator = model.People.Where(c => c.Description.ToLower().StartsWith("steve")).GetAsyncEnumerator()) { while (await enumerator.MoveNextAsync()) { Console.WriteLine($"Name: {enumerator.Current.Name}"); } }
// Query using SELECT FOR UPDATE (lock individual row)
using (var scope = new DataAccessScope()) { var person = await model.People.SelectForUpdateAsync(c => c.Name == "Steve"); person.Age = 19; await scope.CompleteAsync(); }
Find all people whos name contains 's' server-side two different ways
var people1 = await model.People.Where(c => c.Name.IsLike("%s%")).ToListAsync(); var people2 = await model.People.Where(c => c.Name.IndexOf("s") >= 0).ToListAsync();
Print the names all people who have a bestfriend who has a bestfriend whos name is "Steve"
// Will perform automatic implicit left join on BestFriend var people = await model .People .Where(c => c.BestFriend.BestFriend.Name == "Steve") .WithEachAsync(Console.WriteLine);
Assign a person's best friend without querying for the best friend if you know the object primary keys.
using (var scope = new DataAccessScope()) { // No query performed var person1 = model.People.GeReference(personId); // No query performed person1.BestFriend = model.People.GetReference(bestFriendId); // A single UPDATE statement is performed await scope.CompleteAsync(); }
Find all people born in December using server-side date functions
var people = await model.Where(c => c.Birthdate.Year == 12).ToListAsync();
// Find all people and books that are related using classic linq join syntax
var result = await (from book in model.Books join person in model.People on book equals person.BorrowedBook select new { person }).ToListAsync();
// Asynchronously aggregate and enumerate (all people grouped by name with average age)
var values = await (from person in model.People group person by person.Name select new { name = person.Name, AverageAge = person.Average(c => Age) }).GetAsyncEnumerator(); while (await values.MoveNextAsync()) { var value = values.Current; Console.WriteLine($"Average age of person with name of {value.Name} is {value.AverageAge}"); }
Copyright (c) 2007-2016 Thong Nguyen (tumtumtum@gmail.com)