Skip to content

Latest commit

 

History

History
302 lines (231 loc) · 12.7 KB

README.md

File metadata and controls

302 lines (231 loc) · 12.7 KB

Yarn

yet another repository for .net

Yarn is a generic repository pattern implementation for .Net. Its goal is to enforce consistent approach to data persistence regardless of the underlying technology.

Here is what it currently supports:

  • EF
  • NHibernate
    • SQL Server
    • MySql
    • SQLite
    • Oracle
    • Postgres
  • RavenDB
  • MongoDB
  • In-memory object storage
  • Nemo (micro-ORM library)

###Quick example of the pattern usage###

// Bind IRepository to specific implementation (this should happen during application startup)
ObjectContainer.Current.Register<IRepository>(() => new Yarn.Data.MongoDbProvider.Repository());

// Resolve IRepository (this may happen anywhere within application)
// Let's assume we have defined entity Category
var repo = ObjectContainer.Current.Resolve<IRepository>();
var category = repo.GetById<Category, int>(1000);
var categories = repo.GetByIdList<Category, int>(new[] { 1000, 1100 });

###IoC with Yarn###

// Yarn provides a simple IoC container implementation
// One can easily override it with any DI framework 
// of choice by implementing IContainer
// The following code should be called on application startup
// in order to override IoC container
ObjectContainer.Initialize(() => new Any_IoC_Implementation_Based_On_IContainer());

// IRepository is instantiated using default constructor of Yarn.Data.EntityFrameworkProvider.Repository
ObjectContainer.Current.Register<IRepository, Yarn.Data.EntityFrameworkProvider.Repository>();

// IRepository is instantiated using parametrized constructor of Yarn.Data.EntityFrameworkProvider.Repository
ObjectContainer.Current.Register<IRepository>(
  () => new Yarn.Data.EntityFrameworkProvider.Repository(lazyLoadingEnabled: false));
  
// IRepository is instantiated unders a specific instance name
ObjectContainer.Current.Register<IRepository>(
  () => new Yarn.Data.EntityFrameworkProvider.Repository(lazyLoadingEnabled: false), "Lazy");

// IRepository is instantiated as a singleton
ObjectContainer.Current.Register<IRepository>(new Yarn.Data.EntityFrameworkProvider.Repository());

// Resolved IRepository implementation
var repo = ObjectContainer.Current.Resolve<IRepository>();
// IRepository is resolved by instance name
var repo = ObjectContainer.Current.Resolve<IRepository>("Lazy");

###Slightly more sophisticated example utilizing multiple implementations of IRepository###

// Bind IRepository to specific implementation (this should happen during application startup)
// For this example one must provide "EF.Default.Model" application setting and "EF.Default.Connection" connection string setting
// ("EF.Default.Model" points to an assembly which contains model class definition)
ObjectContainer.Current.Register<IRepository>(() => new Yarn.Data.MongoDbProvider.Repository(), "mongo");
ObjectContainer.Current.Register<IRepository>(() => new Yarn.Data.EntityFrameworkProvider.Repository(), "ef");

// Resolve IRepository (this may happen anywhere within application)
// "mongo" will resolve to MongoDB implementation, while "ef" will resolve to EF implementation
var repo = ObjectContainer.Current.Resolve<IRepository>("ef");
//var repo = ObjectContainer.Current.Resolve<IRepository>("mongo");
var category = repo.GetById<Category, int>(1000);

###NHibernate implementation of IRepository###

// With NHibernate one must specify implementation of the data context to be used with repository
// For this example one must provide "NHibernate.MySqlClient.Model" application setting and "NHibernate.MySqlClient.Connection" connection string setting
ObjectContainer.Current.Register<IRepository>(
  () => new Yarn.Data.NHibernateProvider.Repository("nh_uow"), "nh");
ObjectContainer.Current.Register<IDataContext>(
  () => new Yarn.Data.NHibernateProvider.MySqlClient.MySqlDataContext(), "nh_uow");

// In order to use NHibernate with SQL Server one has to bind IDataContext to the SQL Server implementation
// Similarly to the MySQL example "NHibernate.SqlClient.Model" application setting and "NHibernate.SqlClient.Connection" connection string setting should be defined
// ObjectContainer.Current.Register<IDataContext>(() => new Yarn.Data.NHibernateProvider.SqlClient.Sql2012DataContext"nh_uow");

var repo = ObjectContainer.Current.Resolve<IRepository>("nh");
var categories = repo.FindAll<Category>(c => c.Name.Contains("cat"), offset: 50, limit: 10);

###Full text search implementation with IRepository###

  • EF

    // Currently works only with SQL Server provider for EF
    ObjectContainer.Current.Register<IRepository, Yarn.Data.EntityFrameworkProvider.FullTextRepository>();
    ObjectContainer.Current.Register<IFullTextProvider, Yarn.Data.EntityFrameworkProvider.SqlClient.SqlFullTextProvider>();
    
    var repo = ObjectContainer.Current.Resolve<IRepository>();
    var categories = ((IFullTextRepository)repo).FullText.Seach<Category>("hello world");
  • NHibernate

    ObjectContainer.Current.Register<IRepository, Yarn.Data.NHibernateProvider.FullTextRepository>();
    ObjectContainer.Current.Register<IFullTextProvider, Yarn.Data.NHibernateProvider.LuceneClient.LuceneFullTextProvider>();
    // One can resort to use of SQL Server full text as well
    // ObjectContainer.Current.Register<IFullTextProvider, Yarn.Data.NHibernateProvider.SqlClient.SqlFullTextProvider>();
    
    var repo = ObjectContainer.Current.Resolve<IRepository>();
    var categories = ((IFullTextRepository)repo).FullText.Seach<Category>("hello world");

###Specification pattern implementation with IRepository###

// Bind IRepository to specific implementation (this should happen during application startup)
ObjectContainer.Current.Register<IRepository, Yarn.Data.EntityFrameworkProvider.Repository>();

// Resolve IRepository (this may happen anywhere within application)
var repo = ObjectContainer.Current.Resolve<IRepository>();

// Create a specification to abstract search criteria
var spec = new Specification<Category>(c => c.Name.Contains("hello")).Or(c => c.Name.Contains("world"));
var categories = repo.FindAll<Category>(spec);

###Utilizing caching with IRepository###

// Implement a cache provider to support caching of your choice
public class SimpleCache : ICacheProvider
{
  // Implementation goes here
}

// Bind IRepository to specific implementation (this should happen during application startup)
ObjectContainer.Current.Register<IRepository, Yarn.Data.EntityFrameworkProvider.Repository>();

// Resolve IRepository (this may happen anywhere within application)
var repo = ObjectContainer.Current.Resolve<IRepository>();

// Initialize cache repository adapter
// Note: Cache adapter implements write-through cache for entity-related 
// operations and generational caching for queries
var cachedRepo = repo.WithCache<SimpleCache>();

// Create a specification to abstract search criteria
var spec = new Specification<Category>(c => c.Name.Contains("hello")).Or(c => c.Name.Contains("world"));

// This call produces a cache miss, hence the database is hit
var categories1 = cachedRepo.FindAll<Category>(spec);

// This call produces a cache hit, hence there will be no trip to the database
var categories2 = cachedRepo.FindAll<Category>(spec);

###Passing parameters to repository/data context constructor during initialization###

// Bind IRepository to specific implementation
// Constructor arguments may differ depending on a concrete implemnetations of IRepository

// Here is the example based on Entity Framework repository implementation
ObjectContainer.Current.Register<IRepository>(
  () => new Yarn.Data.EntityFrameworkProvider.Repository(lazyLoadingEnabled: false, 
                                                          nameOrConnectionString: "NorthwindConnection", 
                                                          configurationAssembly: typeof(Customer).Assembly));
                    
// Here is the example based on NHiberante repository implementation using SQL Server database backend
ObjectContainer.Current.Register<IRepository, Yarn.Data.NHibernate.Repository>();
ObjectContainer.Current.Register<IDataContext>(
  () => new Yarn.Data.NHibernate.SqlClient.Sql2012DataContext(nameOrConnectionString: "NorthwindConnection", 
                                                              configurationAssembly: typeof(Customer).Assembly));

###Eager loading###

// Bind IRepository to specific implementation (this should happen during application startup)
ObjectContainer.Current.Register<IRepository, Yarn.Data.EntityFrameworkProvider.Repository>();

// Resolve IRepository (this may happen anywhere within application)
var repo = ObjectContainer.Current.Resolve<IRepository>();

// Load customer with orders and order details
var customer = repo.As<ILoadServiceProvider>()
                  .Load<Customer>()
                  .Include(c => c.Orders)
                  .Include(c => c.Orders.Select(o => o.Order_Details))
                  .Find(c => c.CustomerID == "ALFKI");

###Object graph merging###

// Bind IRepository to specific implementation (this should happen during application startup)
ObjectContainer.Current.Register<IRepository, Yarn.Data.EntityFrameworkProvider.Repository>();

// Resolve IRepository (this may happen anywhere within application)
var repo = ObjectContainer.Current.Resolve<IRepository>();

// Merge customer changes with customer data from the database
// Yarn will attempt to merge only the changes specified by the navigation paths
// Note: currently only EF, NHibernate and Nemo providers implement this functionality
var mergedCustomer = repo.As<ILoadServiceProvider>()
                  .Load<Customer>()
                  .Include(c => c.Orders)
                  .Include(c => c.Orders.Select(o => o.Order_Details))
                  .Update(customer);

###Repository adapters###

// Auditable adapter will automatically populate audit information 
// when calling Add/Update for all entities which implement IAuditable interface
var repo = ObjectContainer.Current.Resolve<IRepository>().WithAudit(Thread.CurrentPrincipal);

// Soft-delete adapter will automatically re-write Remove as Update 
// and will filter all deleted records out on retrieve for all entities
// which implement ISoftDelete interface
var repo = ObjectContainer.Current.Resolve<IRepository>().WithSoftDelete(Thread.CurrentPrincipal);

// Multi-tenancy adapter will automatically filter tenant related data as well 
// as check tenant ownership when calling Add, Update and Remove for all entities 
// which implement ITenant interface
// Note: the following example assumes Thread.CurrentPrincipal implements ITenant interface
var repo = ObjectContainer.Current.Resolve<IRepository>().WithMultiTenancy((ITenant)Thread.CurrentPrincipal);

// Fail-over adapter can use an alternative repository if one becomes unavailable
// Once the fail-over happens, the repo adapter will stick to that one until it becomes unavailable
// Updates however are sent to both repositories
// Note: one can use it with more than two repository implementations by chaining
var repo = ObjectContainer.Current.Resolve<IRepository>().WithFailover(ObjectContainer.Current.Resolve<IRepository>("no-sql"));

// It is also possible to chain the adapters
// Note: IPrincipal parameter is optional for soft-delete and auditable adapters
var repo = ObjectContainer.Current.Resolve<IRepository>().WithSoftDelete().WihAudit();

###Bulk operations###

// Bind IRepository to specific implementation (this should happen during application startup)
ObjectContainer.Current.Register<IRepository, Yarn.Data.EntityFrameworkProvider.Repository>();

// Resolve IRepository (this may happen anywhere within application)
var repo = ObjectContainer.Current.Resolve<IRepository>();

// As of now bulk operations are implemented by EF and Mongo providers only
var bulk = repo.As<IBulkOperationsProvider>();

// Bulk retrieve
var customers = bulk.GetById<Customer, string>(new[] { "ALFKI", "ANTON"  });

// Bulk delete by id
bulk.Delete<Customer, string>(new[] { "ALFKI", "ANTON"  });

// Bulk delete
bulk.Delete<Customer>(c => c.City == "London");

// Bulk update
bulk.Update<Customer>(c => c.City == "New York", c => new Customer { City = c.City + " City" });

###Quick Interceptor###

public class LogInterceptor : IDisposable
{
  private readonly ILog logger = LogManager.GetLogger("my-log");
  private readonly Stopwatch sw;
  
  public LogIntercetor(InterceptorContext ctx)
  {
    sw = Stopwatch.StartNew();
    logger.Info("Begin " + ctx.Method.Name);
    ctx.Execute();
  }
  
  public void Dispose()
  {
    sw.Stop();
    logger.Info("End " + ctx.Method.Name + ", " + sw.Elapsed.TotalMilliseconds + " ms");  
  }
}

// Interceptor will automatically intercept all methods
var repo = ObjectContainer.Current.Resolve<IRepository>().WithInterceptor(ctx => new LogInterceptor(ctx));