AddHandlebarsScaffolding with Entity Framework - c#

I try to make a custom class with the scaffold command.
This is my model of custom class Class.hbs
{{> imports}}
{{using-base-class}}
namespace {{namespace}}
{
{{#if comment}}
/// <summary>
{{comment}}
///
</summary>
{{/if}}
{{#each class-annotations}}
{{{class-annotation}}}
{{/each}}
public partial class {{class}} {{base-class}}
{
{{{> constructor}}}
{{{> properties}}}
}
}
My C# code:
public void ConfigureDesignTimeServices(IServiceCollection services)
{
services.AddHandlebarsScaffolding(options =>
{
options.TemplateData = new Dictionary<string, object>
{
{ "using-base-class", "using TEST_NET_7.Interface;" },
{ "base-class", ": IEntityId" }
};
});
}
The command working well with this configuration. It's adding my class to all generated classes, but now I want to ignore some class on scaffold. Like the entity class.
How I can do that ?

This work fine for me.
using HandlebarsDotNet;
using Microsoft.EntityFrameworkCore.Design;
using Newtonsoft.Json;
namespace ScaffoldingSample
{
public class CustomScaffoldingDesignTimeServices : IDesignTimeServices
{
private readonly List<string> IgnoreListClass = new()
{
"AspNetRole",
"AspNetRoleClaim",
"AspNetUser",
"AspNetUserClaim",
"AspNetUserLogin",
"AspNetUserToken"
};
public void ConfigureDesignTimeServices(IServiceCollection services)
{
/// This help me to debug app.
// System.Diagnostics.Debugger.Launch();
services.AddHandlebarsScaffolding(options =>
{
options.TemplateData = new Dictionary<string, object>
{
};
});
Handlebars.RegisterHelper("base-class", WriteBaseClass);
Handlebars.RegisterHelper("using-base-class", WriteUsingBaseClass);
}
void WriteBaseClass(EncodedTextWriter writer, Context context, Arguments parameters)
{
var obj = JsonConvert.DeserializeObject<ObjectContext>(JsonConvert.SerializeObject(context.Value));
/// Filter of class, write or not
if (!IgnoreListClass.Contains(obj?.#class ?? string.Empty))
writer.Write(": IEntityId");
}
void WriteUsingBaseClass(EncodedTextWriter writer, Context context, Arguments parameters)
{
var obj = JsonConvert.DeserializeObject<ObjectContext>(JsonConvert.SerializeObject(context.Value));
/// Filter of class, write or not
if (!IgnoreListClass.Contains(obj?.#class ?? string.Empty))
writer.Write("using TEST_NET_7.Interface;");
}
}
public class ObjectContext
{
public string #class { get; set; } = string.Empty;
}
}

Related

How to insert custom codes in dbContext OnConfiguring generation?

I try to follow this answer Is there a way to scaffold mysql json into custom type? to make custom json type convert, and it works perfect!
The only thing what bother me is that I should modify Context code manual, to insert builder => builder.UseNewtonsoftJson().
I am wonderring if it could be in the generation process, it would be a life saver.
I am inspired by the answer which metioned above, and try to make it work.
What I want is
public partial class spckContext : DbContext
{
...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
optionsBuilder
.UseMySql("server=localhost;port=3306;database=spck;user=root;password=;treattinyasboolean=true", Microsoft.EntityFrameworkCore.ServerVersion.Parse("8.0.29-mysql"), builder => builder .UseNewtonsoftJson())
.EnableSensitiveDataLogging()
.LogTo(Log, LogFilter, DbContextLoggerOptions.DefaultWithLocalTime); // <= stucked here, how to pass method as parameter?
}
}
...
}
I add these to my project:
using System.Drawing;
using Microsoft.Extensions.Logging;
using Console = Colorful.Console;
public partial class spckContext
{
public static void Log(string content)
{
Console.WriteLineFormatted(content, Color.Aqua);
}
public static bool LogFilter(Microsoft.Extensions.Logging.EventId id, LogLevel level)
{
switch (level)
{
case LogLevel.Trace:
case LogLevel.Debug:
case LogLevel.Warning:
case LogLevel.None:
return false;
case LogLevel.Error:
case LogLevel.Critical:
case LogLevel.Information:
return true;
default:
return false;
}
}
}
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
...
//Type Mapping
services.AddSingleton<IRelationalTypeMappingSource, CustomTypeMappingSource>(); // <= add this line
//Option Generator
services.AddSingleton<IProviderConfigurationCodeGenerator, ProviderConfigurationCodeGenerator>(); // <= and this line
...
}
}
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
using Pomelo.EntityFrameworkCore.MySql.Scaffolding.Internal;
using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
public class ProviderConfigurationCodeGenerator : MySqlCodeGenerator
{
private static readonly MethodInfo _enableSensitiveDataLoggingMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.EnableSensitiveDataLogging),
typeof(bool));
private static readonly MethodInfo _useNewtonJsonMethodInfo = typeof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod(
nameof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions.UseNewtonsoftJson),
typeof(MySqlDbContextOptionsBuilder),
typeof(MySqlCommonJsonChangeTrackingOptions));
private static readonly MethodInfo _logToMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.LogTo),
typeof(Action<string>),
typeof(Func<EventId, LogLevel, bool>),
typeof(DbContextLoggerOptions?));
private static readonly MethodInfo _logMethodInfo = typeof(spckContext).GetRequiredRuntimeMethod(
nameof(spckContext.Log),
typeof(string));
private static readonly MethodInfo _logFilterMethodInfo = typeof(spckContext).GetRequiredRuntimeMethod(
nameof(spckContext.LogFilter),
typeof(EventId),
typeof(LogLevel));
private readonly ProviderCodeGeneratorDependencies _dependencies;
private readonly IMySqlOptions _options;
public ProviderConfigurationCodeGenerator(ProviderCodeGeneratorDependencies dependencies, IMySqlOptions options) : base(dependencies, options)
{
_dependencies = dependencies;
_options = options;
}
public override MethodCallCodeFragment GenerateUseProvider(string connectionString, MethodCallCodeFragment? providerOptions)
{
if (providerOptions == null)
{
providerOptions = new MethodCallCodeFragment(_useNewtonJsonMethodInfo);
}
else
{
providerOptions = providerOptions.Chain(new MethodCallCodeFragment(_useNewtonJsonMethodInfo));
}
var fragment = base.GenerateUseProvider(connectionString, providerOptions); //works
fragment = fragment.Chain(_enableSensitiveDataLoggingMethodInfo); //works
fragment = fragment.Chain(_logToMethodInfo,
new NestedClosureCodeFragment("str", new MethodCallCodeFragment(_logMethodInfo)), // <= try and failed! it convert into `str => str.Log()`
new MethodCall(_logFilterMethodInfo), // <= try and failed! error reported
DbContextLoggerOptions.DefaultWithLocalTime);
return fragment;
}
}
public static class TypeExtensions
{
public static MethodInfo GetRequiredRuntimeMethod(this Type type, string name, params Type[] parameters)
=> type.GetTypeInfo().GetRuntimeMethod(name, parameters)
?? throw new InvalidOperationException($"Could not find method '{name}' on type '{type}'");
}
using Microsoft.EntityFrameworkCore.Storage;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
public class CustomTypeMappingSource : MySqlTypeMappingSource
{
public CustomTypeMappingSource(TypeMappingSourceDependencies dependencies, RelationalTypeMappingSourceDependencies relationalDependencies, IMySqlOptions options) : base(dependencies, relationalDependencies, options)
{
}
protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo)
{
if (mappingInfo.ClrType == typeof(MethodCall))
{
return new MethodCallTypeMapping();
}
return base.FindMapping(mappingInfo);
}
}
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Storage;
public class MethodCall
{
public MethodInfo Method;
public MethodCall(MethodInfo info)
{
Method = info;
}
}
public class MethodCallTypeMapping : RelationalTypeMapping
{
private const string DummyStoreType = "clrOnly";
public MethodCallTypeMapping()
: base(new RelationalTypeMappingParameters(new CoreTypeMappingParameters(typeof(MethodCall)), DummyStoreType))
{
}
protected MethodCallTypeMapping(RelationalTypeMappingParameters parameters)
: base(parameters)
{
}
protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
=> new MethodCallTypeMapping(parameters);
public override string GenerateSqlLiteral(object value)
=> throw new InvalidOperationException("This type mapping exists for code generation only.");
public override Expression GenerateCodeLiteral(object value)
{
return value is MethodCall methodCall
? Expression.Call(methodCall.Method) // <= not working, how to fix this?
: null;
}
}
So my question is how to make a MethodCallCodeFragment with method parameter? I tried google, but can't find anything valuable. And MSDN has no sample code for this feature.
Injecting the .UseNewtonsoftJson() and .EnableSensitiveDataLogging() calls can simply be done by providing the design time services with your own IProviderCodeGeneratorPlugin implementation:
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
services.AddSingleton<IProviderCodeGeneratorPlugin, CustomProviderCodeGeneratorPlugin>();
services.AddEntityFrameworkMySqlJsonNewtonsoft();
}
}
public class CustomProviderCodeGeneratorPlugin : IProviderCodeGeneratorPlugin
{
private static readonly MethodInfo EnableSensitiveDataLoggingMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.EnableSensitiveDataLogging),
typeof(bool));
private static readonly MethodInfo UseNewtonJsonMethodInfo = typeof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod(
nameof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions.UseNewtonsoftJson),
typeof(MySqlDbContextOptionsBuilder),
typeof(MySqlCommonJsonChangeTrackingOptions));
public MethodCallCodeFragment GenerateProviderOptions()
=> new MethodCallCodeFragment(UseNewtonJsonMethodInfo);
public MethodCallCodeFragment GenerateContextOptions()
=> new MethodCallCodeFragment(EnableSensitiveDataLoggingMethodInfo);
}
Implementing the complex .LogTo(Log, LogFilter, DbContextLoggerOptions.DefaultWithLocalTime) call is not as straitforward, because the translation logic of EF Core for translating a code generation expression tree to C# code is very basic at best.
Implementing a dummy type mapping to return a complex expression will not work in the end, because EF Core will not be able to translate the lambda expressions of content => LogTo(content) and (id, level) => LogFilter(id, level). You could try to trick it, but the simplest solution is to just circumvent the whole expression translation mechanism.
To output any string as C# code, just override ICSharpHelper.UnknownLiteral(object value) in your own implementation.
Here is a fully working example:
using System;
using System.Diagnostics;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate;
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
services.AddSingleton<IProviderCodeGeneratorPlugin, CustomProviderCodeGeneratorPlugin>();
services.AddSingleton<ICSharpHelper, CustomCSharpHelper>();
services.AddEntityFrameworkMySqlJsonNewtonsoft();
}
}
public static class TypeExtensions
{
public static MethodInfo GetRequiredRuntimeMethod(this Type type, string name, params Type[] parameters)
=> type.GetTypeInfo().GetRuntimeMethod(name, parameters)
?? throw new InvalidOperationException($"Could not find method '{name}' on type '{type}'");
}
public class CustomProviderCodeGeneratorPlugin : IProviderCodeGeneratorPlugin
{
private static readonly MethodInfo EnableSensitiveDataLoggingMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.EnableSensitiveDataLogging),
typeof(bool));
private static readonly MethodInfo UseNewtonJsonMethodInfo = typeof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod(
nameof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions.UseNewtonsoftJson),
typeof(MySqlDbContextOptionsBuilder),
typeof(MySqlCommonJsonChangeTrackingOptions));
private static readonly MethodInfo LogToMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.LogTo),
typeof(Action<string>),
typeof(Func<EventId, LogLevel, bool>),
typeof(DbContextLoggerOptions?));
public MethodCallCodeFragment GenerateProviderOptions()
=> new MethodCallCodeFragment(UseNewtonJsonMethodInfo);
public MethodCallCodeFragment GenerateContextOptions()
=> new MethodCallCodeFragment(EnableSensitiveDataLoggingMethodInfo)
.Chain(GenerateLogToMethodCallCodeFragment());
private MethodCallCodeFragment GenerateLogToMethodCallCodeFragment()
=> new MethodCallCodeFragment(
LogToMethodInfo,
new CSharpCodeGenerationExpressionString("Log"),
new CSharpCodeGenerationExpressionString("LogFilter"),
new CSharpCodeGenerationExpressionString("Microsoft.EntityFrameworkCore.Diagnostics.DbContextLoggerOptions.DefaultWithLocalTime"));
}
public class CSharpCodeGenerationExpressionString
{
public string ExpressionString { get; }
public CSharpCodeGenerationExpressionString(string expressionString)
=> ExpressionString = expressionString;
}
public class CustomCSharpHelper : CSharpHelper
{
public CustomCSharpHelper(ITypeMappingSource typeMappingSource)
: base(typeMappingSource)
{
}
public override string UnknownLiteral(object value)
=> value is CSharpCodeGenerationExpressionString codeGenerationExpressionString
? codeGenerationExpressionString.ExpressionString
: base.UnknownLiteral(value);
}
public partial class Context
{
public static void Log(string content)
=> Console.Write(content);
public static bool LogFilter(EventId id, LogLevel level)
=> level >= LogLevel.Information;
}
internal static class Program
{
private static void Main()
{
}
}
We basically just create our own type called CSharpCodeGenerationExpressionString to hold the C# code string that we want to output and then tell the CustomCSharpHelper.UnknownLiteral() method to return it as is.
The generated OnConfiguring() method looks like this:
public partial class Context : DbContext
{
// ...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
optionsBuilder
.UseMySql("server=127.0.0.1;port=3306;user=root;database=So73163124_01", Microsoft.EntityFrameworkCore.ServerVersion.Parse("8.0.29-mysql"), x => x.UseNewtonsoftJson())
.EnableSensitiveDataLogging()
.LogTo(Log, LogFilter, Microsoft.EntityFrameworkCore.Diagnostics.DbContextLoggerOptions.DefaultWithLocalTime);
}
}
// ...
}

using localization service GetAllLanguages from a component composer, incorrect DI?

I have an interface as below, which I use to add a specific language if it does not exist:
public interface IGetLanguagesService
{
void GetLanguages(ILocalizationService localization);
}
public class LanguageService : IGetLanguagesService
{
ILocalizationService _localizationService;
public void GetLanguages(ILocalizationService localization)
{
_localizationService = localization;
var currentLanguages = _localizationService.GetAllLanguages();
bool exists = false;
foreach (var currentLan in currentLanguages)
{
if (currentLan.IsoCode == "es-ES")
{
exists = true;
}
}
if (!exists)
{
AddLanguage(_localizationService);
}
}
public void AddLanguage(ILocalizationService localization)
{
var languageSE = new Language("es-ES") { CultureName = "es-ES", IsMandatory = true };
localization.Save(languageSE);
}
}
I want to use this at start-up so have created a component composer, which on Initialize() I want to call CallGetLanguages() but Im not entirely sure what should be in Initialize(), I think my DI may be wrong?
public class LanguagesComposer : ComponentComposer<LanguagesComponent>
{
public void Compose(Composition composition)
{
composition.Register<IGetLanguagesService>(Lifetime.Singleton);
composition.Register<ILocalizationService>(Lifetime.Singleton);
composition.Components().Append<LanguagesComponent>();
}
}
public class LanguagesComponent : IComponent
{
public void Initialize()
{
???????
}
public void Terminate()
{
throw new NotImplementedException();
}
IGetLanguagesService _getLanguagesService;
ILocalizationService _localization;
public void CallGetLanguages(IGetLanguagesService getLanguages, ILocalizationService localization)
{
_getLanguagesService = getLanguages;
_localization = localization;
_getLanguagesService.GetLanguages(localization);
}
}
You've passed ILocalizationService localization instance to LanguageService twice, pass it to constructor instead and use a constructor injection. The same issue with LanguagesComponent, pass all its dependencies to constructor instead of methods

How to Correctly Invoke WCF ServiceClient Proxy Extensions?

While troubleshooting a wcf client issue I came across some code from #marc-gravell here. I read the article a number of times and then decided to try and see if I could use the code for real so I created a console app and pulled it all in.
Wrapper:
public interface IDisposableWrapper<T> : IDisposable
{
T BaseObject { get; }
}
public class DisposableWrapper<T> : IDisposableWrapper<T> where T : class, IDisposable
{
public T BaseObject { get; private set; }
public DisposableWrapper(T baseObject) { BaseObject = baseObject; }
protected virtual void OnDispose()
{
BaseObject.Dispose();
}
public void Dispose()
{
if (BaseObject != null)
{
try
{
OnDispose();
}
catch
{
// swallow...
}
}
BaseObject = null;
}
}
Extensions:
public static class DisposableExtensions
{
// core "just dispose it without barfing"
public static IDisposableWrapper<T> Wrap<T>(this T baseObject)
where T : class, IDisposable
{
if (baseObject is IDisposableWrapper<T>) return (IDisposableWrapper<T>)baseObject;
return new DisposableWrapper<T>(baseObject);
}
// specific handling for service-model
public static IDisposableWrapper<TProxy> Wrap<TProxy, TChannel>(this TProxy proxy)
where TProxy : ClientBase<TChannel>
where TChannel : class
{
return new ClientWrapper<TProxy, TChannel>(proxy);
}
}
ClientWrapper:
public class ClientWrapper<TProxy, TChannel> : DisposableWrapper<TProxy>
where TProxy : ClientBase<TChannel>
where TChannel : class
{
public ClientWrapper(TProxy proxy) : base(proxy)
{
}
protected override void OnDispose()
{
// lots of code per state of BaseObject
//State != CommunicationState.Faulted;
}
}
Now, when I go to use it, I have this:
static void Main(string[] args)
{
using (var proxy = new PLPlacementServiceClient())
{
var result = proxy.GetDocumentClassForNewBusiness();
}
using (var proxy = new PLPlacementServiceClient().Wrap())
{
var result = proxy.BaseObject.GetDocumentClassForNewBusiness();
}
using (var proxy = new PLPlacementServiceClient().Wrap<>())//what goes here?
{
var result = proxy.BaseObject.GetDocumentClassForNewBusiness();
}
}
When I F-12 the PLPlacementServiceClient().Wrap() method , it takes me to the non-generic implementation in the extensions class
IDisposableWrapper<T> Wrap<T>(this T baseObject)
, but I was expecting to be taken to the other signature
IDisposableWrapper<TProxy> Wrap<TProxy, TChannel>(this TProxy proxy)
So here is my question(s), "How do I invoke the ClientBase version of the extension?"
Thank you,
Stephen
You must specify both type parameters for method Wrap. That is:
using (var proxy = new PLPlacementServiceClient().Wrap<PLPlacementServiceClient,/*type of the service contract PLPlacementServiceClient is implementing*/>())
{
var result = proxy.BaseObject.GetDocumentClassForNewBusiness();
}

No default Instance is registered and cannot be automatically determined for type 'EPiServer.Framework.Cache.IRequestCache

I have the following code
[Quartz.DisallowConcurrentExecutionAttribute()]
public class SearchIndexJob : IJob
{
private readonly ILog _Log = null;
private SearchManager _SearchManager;
public SearchIndexJob()
{
_Log = LogManager.GetLogger(GetType());
}
#region IJob Members
public void Execute(IJobExecutionContext context)
{
var container = new StructureMap.Container();
IServiceConfigurationProvider services = new StructureMapConfiguration(container);
var locator = new EPiServer.ServiceLocation.StructureMapServiceLocator(container);
var context2 = new EPiServer.ServiceLocation.ServiceConfigurationContext(HostType.WebApplication, services);
new Mediachase.Commerce.Initialization.CommerceInitialization().ConfigureContainer(context2);
container.Configure(ce =>
{
ce.For<IMarketService>().Use<MarketServiceDatabase>();
ce.For<IMarket>().Use<MarketImpl>();
ce.For<ICurrentMarket>().Singleton().Use<Mediachase.Commerce.Markets.CurrentMarketImpl>();
ce.For<ISynchronizedObjectInstanceCache>().Singleton().Use<EPiServer.Events.RemoteCacheSynchronization>();
ce.For<IObjectInstanceCache>().Use<HttpRuntimeCache>();
//ce.For<ITypeScannerLookup>().Use<FakeTypeScannerLookup>();
ce.For<IWarehouseRepository>().Singleton().Use<Mediachase.Commerce.Inventory.Database.WarehouseRepositoryDatabase>();
ce.For<IChangeNotificationQueueFactory>().Singleton().Use<CommerceChangeQueueFactory>();
ce.For<IPriceService>().Singleton().Use<PriceServiceDatabase>();
ce.For<IPriceDetailService>().Use<PriceDetailDatabase>();
ce.For<IWarehouseInventoryService>().Singleton().Use<WarehouseInventoryProxy>();
ce.For<IInventoryService>().Singleton().Use<InventoryServiceProvider>();
ce.For<IApplicationContext>().Use<FakeAppContext>();
ce.For<CatalogConfiguration>().Use(CatalogConfiguration.Instance);
ce.For<IRequiredMetaFieldCollection>().Singleton().Use<DefaultRequiredMetaFields>();
ce.For<MetaDataContext>().Singleton().Use(() => CatalogContext.MetaDataContext);
//ce.For<EventContext>().HybridHttpOrThreadLocalScoped().Use(eventContext);
ce.For<FrameworkContext>().Use(() => FrameworkContext.Current);
//ce.For<SqlContext>().Use(() => new SqlContext(BusinessFoundationConfigurationSection.Instance.Connection.Database));
ce.For<IChangeNotificationManager>().Singleton().Use<ChangeNotificationManager>();
////ce.For<Mediachase.Commerce.Catalog.ICatalogSystem>().Singleton().Use(() => Mediachase.Commerce.Catalog.CatalogContext.Current);
ce.For<IEventRegistry>().Use<EPiServer.Events.Clients.EventRegistry>();
ce.For<IEventBroker>().Use<FakeEventBroker>();
ce.For<Mediachase.Search.IndexBuilder>().Use<FakeIndexer>();
});
EPiServer.ServiceLocation.ServiceLocator.SetLocator(locator);
string applicationName = context.JobDetail.Description;
if (String.IsNullOrEmpty(applicationName) || applicationName == "all") // index all applications
{
AppDto dto = AppContext.Current.GetApplicationDto();
foreach (AppDto.ApplicationRow row in dto.Application)
{
IndexApplication(row.Name);
}
}
else
{
IndexApplication(applicationName);
}
}
#endregion
void IndexApplication(string applicationName)
{
_Log.Info(String.Format("Creating Search Manager for \"{0}\" Application.", applicationName));
_SearchManager = new SearchManager(applicationName);
_Log.Info("Created Search Manager.");
try
{
_SearchManager.SearchIndexMessage += new SearchIndexHandler(_SearchManager_SearchIndexMessage);
_SearchManager.BuildIndex(true);
}
catch (Exception ex)
{
_Log.Error("Search Manager Failed.", ex);
}
}
void _SearchManager_SearchIndexMessage(object source, SearchIndexEventArgs args)
{
_Log.Info(String.Format("Percent Complete: {0}%, {1}", Convert.ToInt32(args.CompletedPercentage), args.Message));
}
}
public class FakeEventBroker : IEventBroker
{
public bool Enabled { get; set; }
public System.Threading.Tasks.Task RaiseEventAsync(Guid eventId, Object parameter)
{
return null;
}
public event EventHandler<EventReceivedEventArgs> EventReceived;
public event EventHandler<EventMissedEventArgs> EventMissed;
}
public class FakeAppContext : IApplicationContext
{
public bool HasContentModelTypes { get; set; }
public bool DisableVersionSync { get; set; }
}
public class FakeIndexer : Mediachase.Search.IndexBuilder
{
public FakeIndexer() : base("","","")
{
}
}
and I get this error
"No default Instance is registered and cannot be automatically determined for type 'EPiServer.Framework.Cache.IRequestCache"
in this line " _SearchManager.BuildIndex(true);"
Any ideas?
It is hard to tell but I assume you need to register the IRequestCache in your container
I.e.
container.Configure(ce =>
{
ce.For<IMarketService>().Use<MarketServiceDatabase>();
ce.For<IMarket>().Use<MarketImpl>();
ce.For<IRequestCache>().Use<NoRequestCache>(); // or whatever implementation you need
...
}
Schedule Job is trying to Intialize Commerce, Most probably you will require to fix more then IRequestCache including DBContext, See an integration sample here. GIT Integration Sample

How to refactor ninject over-injection to a more DRY form

I would like to aggregate my injected objects into an Data object so that I don't have to have large constructor lists. However I still wish to use WhenInjectedInto to provide contextual binding.
For example the below spec tests a scenario that I believe will help
WhenInjectedIntoRequestChain
Indicates that the binding should only be used where the source
has been injected been injected into parent0 which itself has been injected into parent1 and so on
The method should have the signiture
public static IBindingInNamedWithOrOnSyntax<T>
WhenInjectedIntoRequestChain<T>
( this IBindingWhenInNamedWithOrOnSyntax<T> #this
, params Type[] parentChain
)
The spec used to test this should be
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Ninject;
using Weingartner.Controls.PluginFramework;
using Xunit;
namespace Weingartner.Controls.Spec.PluginFramework
{
public class NinjectExtensionsSpec
{
public interface IData { }
public class Data0 : IData {}
public class Data1 : IData {}
public class Data2 : IData {}
public class Params
{
public IList<IData> Data { get; set; }
public Params(IEnumerable<IData> data)
{
Data = data.ToList();
}
}
public class Target0
{
public Params P { get; set; }
public Target0(Params p) {P = p;}
}
public class Target1
{
public Params P { get; set; }
public Target1(Params p){P = p;}
}
[Fact]
public void WhenInjectedIntoHeirarchyShouldWork()
{
var k = new StandardKernel();
k.Bind<IData>().To<Data0>()
.WhenInjectedIntoRequestChain(typeof(Params),typeof(Target0));
k.Bind<IData>().To<Data1>()
.WhenInjectedIntoRequestChain(typeof(Params),typeof(Target1));
k.Bind<IData>().To<Data2>()
.WhenInjectedIntoRequestChain(typeof(Params),typeof(Target1));
var target0 = k.Get<Target0>();
var target1 = k.Get<Target1>();
target0.P.Data.Count.Should().Be(1);
target1.P.Data.Count.Should().Be(2);
}
}
}
Here is a solution to the above problem with some test cases. It is not an exact solution for the above problem but this new method can solve the above.
using System;
using System.Linq;
using Ninject.Activation;
using Ninject.Infrastructure.Language;
using Ninject.Syntax;
namespace Weingartner.Controls.PluginFramework
{
public static class NinjectExtensions
{
/// <summary>
/// Indicates that the binding should only be used where the source
/// has been injected into parentChain[0] which in turn has been injected
/// into parentChain[1] and son on
/// </summary>
/// <param name="parentChain">This list of parents in order</param>
/// <returns>The fluent syntax.</returns>
public static IBindingInNamedWithOrOnSyntax<T>
WhenInjectedIntoRequestChain<T>
( this IBindingWhenInNamedWithOrOnSyntax<T> #this
, params Type[] parentChain
)
{
#this.BindingConfiguration.Condition =
request =>
{
var result = true;
foreach (var parent in parentChain)
{
result = result && WhenInjectedInto(request, parent);
request = request?.ParentRequest;
}
return result;
};
return (IBindingInNamedWithOrOnSyntax<T>)#this;
}
private static bool WhenInjectedInto(IRequest request, Type parent)
{
if (!parent.IsGenericTypeDefinition)
return request?.Target != null
&& parent.IsAssignableFrom(request.Target.Member.ReflectedType);
if (!parent.IsInterface)
return request
?.Target?.Member.ReflectedType
.GetAllBaseTypes()
.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == parent)
?? false;
return request
?.Target?.Member.ReflectedType?
.GetInterfaces()
.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == parent)
?? false;
}
}
}

Categories