Ragnarok defines how to manage lifecycles of applications for mainly XR (VR/AR/MR), inspired by
This framework isolates definitions from implementations for the lifecycle, allowing you to implement and extend it to adapt to the platforms you use, as shown below.
- ex) Unity
- ex) Xamarin
- ex) ASP.NET Core
- ex) Generic Host
- ex) Windows Presentation Foundation (WPF)
This framework depends on .NET Standard 2.0.
Developers should
- Go to Release pages.
- Download the latest version.
to use this framework.
Using our framework, you can resolve dependencies as below:
interface ISender
{
string Send();
}
interface IReceiver
{
void Receive(string message);
}
class Service
{
readonly ISender sender;
readonly IReciever receiver;
[Inject]
Service(ISender sender, IReceiver receiver)
{
this.sender = sender;
this.receiver = receiver;
}
public void Run()
{
var message = sender.Send();
receiver.Receive(message);
}
}
using implementations as below:
class ConsoleSender : ISender
{
readonly string announcement;
[Inject]
ConsoleSender(string announcement)
{
this.announcement = announcement;
}
public string Send()
{
Console.Write($"{announcement}:");
return Console.ReadLine() ?? string.Empty;
}
}
class ConsoleReceiver : IReceiver
{
[InjectField]
readonly string? header;
public void Receive(string message)
{
if (header == null)
{
Console.WriteLine($"{message}");
}
else
{
Console.WriteLine($"{header}: {message}");
}
}
}
like:
var context = new DependencyInjectionContext();
// Register ConsoleSender as ISender to instantiate per local scope.
context.Register<ConsoleSender>(Lifetime.Local)
.WithArgument("announcement", "Enter any text") // Add parameter to inject into constructor.
.As<ISender>();
// Register ConsoleReceiver as IReceiver to instantiate per global scope.
context.Register<ConsoleReceiver>(Lifetime.Global)
.WithField("header", "Recieved") // Add parameter to inject into fields.
.As<IReceiver>();
// Register Service to instantiate per request.
context.Register<Service>(Lifetime.Temporal);
using var scope = context.CreateScope();
// You can resolve an instance of Service and run it.
scope.Resolver.Resolve<Service>().Run();
or using IInstallation
s as below:
class ServiceInstallation : IInstallation
{
public void Install(IObjectContainer container)
{
container.Register<ISender, ConsoleSender>(Lifetime.Local).WithArgument("announcement", "Enter any text");
container.Register<IReceiver, ConsoleReceiver>(Lifetime.Global).WithField("header", "Recieved");
container.Register<Service>(Lifetime.Temporal);
}
}
like:
var context = new DependencyInjectionContext();
// You can register installation like:
context.Install<ServiceInstallation>();
// or
context.Install(new ServiceInstallation());
using var scope = context.CreateScope();
// The result is like:
// "Enter any text: This is a test."
// "Recieved: This is a test."
scope.Resolver.Resolve<Service>().Run();
In our framework, you can resolve collection as bellow:
interface ILogger
{
void Log(string message);
}
class ConsoleLogger : ILogger
{
...
}
class FileLogger : ILogger
{
...
}
like:
var context = new DependencyInjectionContext();
context.Register<ILogger, ConsoleLogger>(Lifetime.Global); // Same as Register<ConsoleLogger>(Lifetime.Global).As<ILogger>();
context.Register<ILogger, FileLogger>(Lifetime.Global);
using var scope = context.CreateScope();
// You can resolve collection like:
var array = scope.Resolver.Resolve<ILogger[]>();
// or
var readOnlyList = scope.Resolver.Resolve<IReadOnlyList<ILogger>>();
// or
var readOnlyCollection = scope.Resolver.Resolve<IReadOnlyCollection<ILogger>>();
// or
var enumerable = scope.Resolver.Resolve<IEnumerable<ILogger>>();
You can resolve instance using sub containers like:
var context = new DependencyInjectionContext();
context.RegisterFromSubContainer<Service>(subContainer =>
{
subContainer.Register<ISender, ConsoleSender>(Lifetime.Global).WithArgument("announcement", "Enter any text");
subContainer.Register<IReceiver, ConsoleReceiver>(Lifetime.Global).WithField("header", "Recieved");
subContainer.Register<Service>(Lifetime.Temporal);
});
using var scope = context.CreateScope();
// You can resolve an instance of Service.
var service = scope.Resolver.Resolve<Service>();
// but you can't resolve an instance of ISender.
var sender = scope.Resolver.Resolve<ISender>(); // RagnarokNotRegisteredException.
Sub containers (inspired by Zenject/Extenject/UniDi) help you making dependency scopes more smaller.
You can resolve instance using sub containers like:
var context = new DependencyInjectionContext();
context.RegisterFactory<Service, ServiceInstallation>(Ownership.External);
using var scope = context.CreateScope();
var facotry = scope.Resolver.Resolve<IFactory<Service>>();
var service = factory.Create();
service.Run();
Registering factories (inspired by Zenject/Extenject/UniDi and VContainer) help you creating instances on demand.
Now writing...
Please see GitHub Issues.
Please see GitHub Projects.
- Implementations using IL emission or Source Generator.
- Decolation/Interception (inspired by Autofac)
- Configuration (inspired by Microsoft.Extensions.Configuration and Microsoft.Extensions.Hosting)
This framework is under the MIT License, see LICENSE.
This framework is a part of YggdrAshill framework.
Other frameworks will be produced soon for YggdrAshill.