Please refer to the following code. Using MEF I have created a manager object and a plug-in, which imports the IQueryEngine as it should.
However, when I call the method _queryEngine.Get() in the plugin, I get a MissingMethodException, like the method isn't implemented in the concrete implementation of QueryEngine, however it is implemented. I believe that this has to do with the ManagedElementDTO class, but I am not sure what to do here.
Can anyone shed some light on what is going on?
I have an interface:
public interface IQueryEngine
{
ManagedElementDTO Get();
}
And the implementation:
public class QueryEngine : IQueryEngine
{
public ManagedElementDTO Get()
{
return new ManagedElementDTO();
}
}
This engine is declared in a manager:
[Export]
public class ProviderSupervisor : ISupervisor
{
[Export("QueryEngine")]
private IQueryEngine _queryEngine;
[ImportMany(typeof(IProviderModule))]
private IEnumerable<IProviderModule> _providers;
public ProviderSupervisor(IStore store)
{
_queryEngine = new QueryEngine();
CreateContainer();
}
/// <summary>
/// Creates the composition container and composes the parts.
/// </summary>
/// <returns>The container.</returns>
private CompositionContainer CreateContainer()
{
try
{
var catalog = new AggregateCatalog();
var directory = GetProviderDirectory();
if (directory.Exists)
{
catalog.Catalogs.Add(new DirectoryCatalog(directory.FullName));
}
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
return container;
}
catch (Exception)
{
// TODO: Log Error
throw;
}
}
private static DirectoryInfo GetProviderDirectory()
{
var location = ConfigurationSupervisor.GetInstance().ProviderLocation;
if (!string.IsNullOrWhiteSpace(location))
{
var providerLocation = new DirectoryInfo(Environment.ExpandEnvironmentVariables(location));
if (providerLocation.Exists)
{
return providerLocation;
}
}
// Use the current assembly location as the default.
var exeLocation = new FileInfo(Assembly.GetExecutingAssembly().Location);
return exeLocation.Directory;
}
}
and the plug-in:
[Export(typeof(IProviderModule))]
public class Provider : IProviderModule, IDisposable
{
private IQueryEngine _queryEngine;
#region Properties
/// <summary>
/// Gets or sets the query engine.
/// </summary>
[Import("QueryEngine", typeof(IQueryEngine), RequiredCreationPolicy = CreationPolicy.Shared)]
public IQueryEngine QueryEngine
{
get { return _queryEngine; }
set
{
_queryEngine = value;
}
}
public void InvokeQueryEngine()
{
var item = _queryEngine.Get(); // Exception!
}
}
In order to export your QueryEngine type you should be doing the following
[Export(typeof(IQueryEngine))]
public class QueryEngine : QueryEngine
Now in the plugin class importing it should be as simple as
[Import(typeof(IQueryEngine), RequiredCreationPolicy = CreationPolicy.Shared)]
public IQueryEngine QueryEngine
{
get { return _queryEngine; }
set
{
_queryEngine = value;
}
}
I'm not quite sure what you are attempting to do with the [Export] on the IQueryEngine field. You should be able to delete that entirely
Related
System.ComponentModel.Composition.ImportCardinalityMismatchException
'No exports were found that match the constraint: ContractName IntroToMEF.DigitalMonitor
RequiredTypeIdentity IntroToMEF.IMonitor'
'
My types are correct. I am able to Get exported value if I have only one instance inheriting from IMonitor. Whenever I need to use contract name I get an error. I am out of ideas.
Clearing Visual Studio Cache does not work.
[Export(typeof(IMonitor))]
public class DigitalMonitor : IMonitor
{
public void start()
{
Console.WriteLine("Digital Monitor: Started monitoring");
}
}
internal class HostMefInApp
{
[Import]
public ILogger logger { get; set; }
[Import]
public IMonitor monitor { get; set; }
private void ComposeMultiple()
{
Console.WriteLine("DEBUG:" + GetType().Assembly);
var assemblyCatalog = new AssemblyCatalog(GetType().Assembly);
var container = new CompositionContainer(assemblyCatalog);
// var contractName = AttributedModelServices.GetContractName(typeof(FileLogger));
try
{
var instancesOfFileLogger = container.GetExports<ILogger>(); //"IntroToMEF.FileLogger"
}
catch (ImportCardinalityMismatchException ex)
{
Console.WriteLine(ex);
}
CompositionBatch batch = new CompositionBatch();
batch.AddPart(AttributedModelServices.CreatePart(new FileLogger()));
batch.AddPart(AttributedModelServices.CreatePart(new DigitalMonitor()));
batch.AddPart(AttributedModelServices.CreatePart(new AnalogMonitor()));
container.Compose(batch);
var digitalMonitorContractName = AttributedModelServices.GetContractName(typeof(DigitalMonitor));
var currentLogger = container.GetExportedValue<ILogger>();
var digitalMonitor = container.GetExportedValue<IMonitor>(digitalMonitorContractName);
logger = currentLogger;
monitor = digitalMonitor;
}
I think that you should:
[Export(typeof(IMonitor))]
[Export(typeof(DigitalMonitor))]
public class DigitalMonitor : IMonitor
{
public void start()
{
Console.WriteLine("Digital Monitor: Started monitoring");
}
}
meaning, export DigitalMonitor as IMonitor and DigitalMonitor. This will give you possibility to import it using both ways. Then of course your code will work with:
var digitalMonitor = container.GetExportedValue<DigitalMonitor>();
I am creating a vsix project where I need to add some custom errors in Error List window. Now I am stuck in IServiceProvider. What it contains and why I need this and how I can get this? In my code, I need to initialize with a service provider. How can I do this? Following is code :
internal class ErrorListManager
{
public static ErrorListProvider errorListProvider;
public static void Initialize(IServiceProvider serviceProvider)
{
errorListProvider = new ErrorListProvider(serviceProvider);
}
public static void AddError(string errorMsg) {
AddTask(errorMsg, TaskErrorCategory.Error);
}
private static void AddTask(string errorMsg, TaskErrorCategory category)
{
errorListProvider.Tasks.Add(new ErrorTask
{
Category = TaskCategory.User,
ErrorCategory = category,
Text = errorMsg
});
}
}
Please help. I am a beginner for C# and VSIX. Thanks!
To write something in VS Error List you need to use ErrorListProvider. In my implementation, I inherit from ErrorListProvider and implement my own behavior for "ErrorWindowController". The code looks like this (read the code comments for more information):
public class ErrorWindowController : ErrorListProvider
{
#region Constructor
/// <summary>
/// Instance Constructor
/// </summary>
/// <param name="aServiceProvider"></param>
public ErrorWindowController(IServiceProvider aIServiceProvider) : base(aIServiceProvider)
{
}
#endregion
#region Public Methods
// Use this to add a collection of custom errors in VS Error List
public void AddErrors(IEnumerable<ErrorModel> aErrors)
{
SuspendRefresh();
foreach (ErrorModel error in aErrors)
{
ErrorTask errorTask = new ErrorTask
{
ErrorCategory = error.Category,
Document = error.FilePath,
Text = error.Description,
Line = error.Line - 1,
Column = error.Column,
Category = TaskCategory.BuildCompile,
Priority = TaskPriority.High,
HierarchyItem = error.HierarchyItem
};
errorTask.Navigate += ErrorTaskNavigate;
Tasks.Add(errorTask);
}
BringToFront();
ResumeRefresh();
}
// Remove all the errors from Error List which are depending of a project and this specific project is closed
// Or remove all the errors from Error List when the VS solution is closed
public void RemoveErrors(IVsHierarchy aHierarchy)
{
SuspendRefresh();
for (int i = Tasks.Count - 1; i >= 0; --i)
{
var errorTask = Tasks[i] as ErrorTask;
aHierarchy.GetCanonicalName(Microsoft.VisualStudio.VSConstants.VSITEMID_ROOT, out string nameInHierarchy);
errorTask.HierarchyItem.GetCanonicalName(Microsoft.VisualStudio.VSConstants.VSITEMID_ROOT, out string nameErrorTaskHierarchy);
if (nameInHierarchy == nameErrorTaskHierarchy)
{
errorTask.Navigate -= ErrorTaskNavigate;
Tasks.Remove(errorTask);
}
}
ResumeRefresh();
}
// Remove all the errors from the Error List
public void Clear()
{
Tasks.Clear();
}
#endregion
#region Private Methods
// This is optional
// Add navigation for your errors.
private void ErrorTaskNavigate(object sender, EventArgs e)
{
ErrorTask objErrorTask = (ErrorTask)sender;
objErrorTask.Line += 1;
bool bResult = Navigate(objErrorTask, new Guid(EnvDTE.Constants.vsViewKindCode));
objErrorTask.Line -= 1;
}
#endregion
}
The ErrorModel is very simple:
public class ErrorModel
{
#region Properties
public string FilePath { get; set; }
public int Line { get; set; }
public int Column { get; set; }
public TaskErrorCategory Category { get; set; }
public string Description { get; set; }
public IVsHierarchy HierarchyItem { get; set; }
#endregion
}
Happy Codding!
Your package is a service provider. Its base class AsyncPackage derives from the Package class which implements OLE.Interop.IServiceProvider and System.IServiceProvider (yes, VSX is confusing and there are two IServiceProvider interfaces). So, from your package class you would call ErrorListManager.Initialize(this).
That said, you may want to use the new API of VS 2015 and higher for the ErrorList. The old approach that you are using doesn't allow you to add values to columns such as "Code", etc. See samples here:
VSSDK-Extensibility-Samples/ErrorList: https://github.com/microsoft/VSSDK-Extensibility-Samples/tree/master/ErrorList
madskristensen/WebAccessibilityChecker: https://github.com/madskristensen/WebAccessibilityChecker/tree/master/src/ErrorList
madskristensen/TaskOutputListener: https://github.com/madskristensen/TaskOutputListener/blob/master/src/ErrorListProvider/ErrorListProvider.cs
I have a class LanguagePopupMessage which is used all over the application (and external libraries). If this class is constructed it fetches the namespace where it's created and adds a suffix to be unique.
The Question is: How can get all LanguagePopupMessage definitions including the fieldname parameter?
Im using structuremap in my application. It's also scanning all libraries at startup, so maybe there is a possiblity how to automaticate it. 👀
using System;
using System.Diagnostics;
namespace ConsoleApp1
{
/// <summary>
/// Creates the namespace for a popup window and has an additional flag for the caption
/// </summary>
public class LanguagePopupMessage
{
public string Namespace { get; }
public string Caption => $"{Namespace}Caption";
public LanguagePopupMessage(string fieldName)
{
if(string.IsNullOrEmpty(fieldName))
throw new ArgumentNullException(nameof(fieldName));
if (_GetNamespace() is Type type)
{
Namespace = $"{type}.{fieldName}";
}
else
{
throw new InvalidOperationException("could not fetch the namespace");
}
}
private Type _GetNamespace()
{
StackTrace st = new StackTrace();
foreach (var sf in st.GetFrames())
{
var type = sf.GetMethod().DeclaringType;
if (type != GetType())
{
return type;
}
}
return null;
}
public override string ToString()
{
return $"Namespace '{Namespace}' Caption '{Caption}'";
}
}
class Program
{
//ConsoleApp1.Program.PopupMessage.ConfigNotLoaded
//ConsoleApp1.Program.PopupMessage.ConfigNotLoadedCaption
private static readonly LanguagePopupMessage _CONFIG_NOT_LOADED_POPUP_MESSAGE = new LanguagePopupMessage("ConfigNotLoaded");
static void Main(string[] args)
{
Console.ReadKey();
}
}
}
namespace ConsoleApp1.Subfolder
{
public class SubfolderClass
{
/// <summary>
/// ConsoleApp1.Subfolder.SubfolderClass.FooMessage
/// ConsoleApp1.Subfolder.SubfolderClass.FooMessageCaption
/// </summary>
public static readonly LanguagePopupMessage Message = new LanguagePopupMessage("FooMessage");
}
}
I made a custom IRegistrationConvention - FindAllLanguagePopupMessages for structuremap. During runtime a new container scans all libraries -> all Types if there are any FieldInfo of type LanguagePopupMessage and adding it into a collection.
To get better performance, I made an Attribute - ContainsTranslationDefinition to filter the classes.
Sourcecode
public class ContainsTranslationDefinition : Attribute
{ }
/// <summary>
/// Creates the namespace for a popup window and has an additional flag for the caption
/// </summary>
public class LanguagePopupMessage
{
public string Namespace { get; }
public string Caption => $"{Namespace}Caption";
public LanguagePopupMessage(string fieldName)
{
if(string.IsNullOrEmpty(fieldName))
throw new ArgumentNullException(nameof(fieldName));
if (_GetNamespace() is Type type)
{
Namespace = $"{type}.{fieldName}";
}
else
{
throw new InvalidOperationException("could not fetch the namespace");
}
}
private Type _GetNamespace()
{
StackTrace st = new StackTrace();
foreach (var sf in st.GetFrames())
{
var type = sf.GetMethod().DeclaringType;
if (type != GetType())
{
return type;
}
}
return null;
}
public override string ToString()
{
return $"Namespace '{Namespace}' Caption '{Caption}'";
}
}
/// <summary>
/// Add <see cref="LanguagePopupMessage"/> into the <see cref="Container.Model"/> type lifecycle
/// </summary>
public class FindAllLanguagePopupMessages : IRegistrationConvention
{
private readonly ILifecycle _Lifecycle = new UniquePerRequestLifecycle();
public void ScanTypes(TypeSet types, Registry registry)
{
List<LanguagePopupMessage> dec = new List<LanguagePopupMessage>();
foreach (Type type in types.AllTypes())
{
if (!Attribute.IsDefined(type, typeof(ContainsTranslationDefinition)))
{
continue;
}
_FindConfigDeclarations(type, dec);
}
foreach (LanguagePopupMessage languagePopupMessage in dec)
{
Console.WriteLine($"{languagePopupMessage}");
}
}
private static void _FindConfigDeclarations(Type type, List<LanguagePopupMessage> declarations)
{
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);
declarations.AddRange(fields
.Where(info => info.IsInitOnly && typeof(LanguagePopupMessage).IsAssignableFrom(info.FieldType))
.Select(info => (LanguagePopupMessage)info.GetValue(null)));
// find all nested class types and run method recursively
foreach (var nestedType in type.GetNestedTypes(BindingFlags.Public))
{
_FindConfigDeclarations(nestedType, declarations);
}
}
}
[ContainsTranslationDefinition]
public class TestClass
{
private static readonly LanguagePopupMessage _CONFIG_1 = new LanguagePopupMessage("ConfigNotLoaded1");
private static readonly LanguagePopupMessage _CONFIG_2 = new LanguagePopupMessage("ConfigNotLoaded2");
}
[ContainsTranslationDefinition]
public class Program
{
//ConsoleApp1.Program.PopupMessage.ConfigNotLoaded
//ConsoleApp1.Program.PopupMessage.ConfigNotLoadedCaption
private static readonly LanguagePopupMessage _CONFIG_NOT_LOADED_POPUP_MESSAGE = new LanguagePopupMessage("ConfigNotLoaded3");
static void Main(string[] args)
{
// Create container and tell where to look for depencies
IContainer container = new Container(c => c.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.WithDefaultConventions();
scanner.AssembliesFromApplicationBaseDirectory();
scanner.With(new FindAllLanguagePopupMessages());
}));
Console.ReadKey();
}
}
Preview
How do I inject my dbContext class using Unity? I can't just create a Interface like for my other "normal" classes? What should I do with my RequestContext class and what should my UnityConfig look like?
public class RequestContext : IdentityDbContext<User>
{
public RequestContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
Database.SetInitializer<RequestContext>(new CreateDatabaseIfNotExists<RequestContext>());
}
public DbSet<Request> Requests { get; set; }
public DbSet<Record> Records { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
public static RequestContext Create()
{
return new RequestContext();
}
}
In my Repository class i use it like this but want to inject instead:
private RequestContext dbContext;
private IUserRepository _userRepository;
public RequestRepository(IUserRepository userRepository)
{
dbContext = new RequestContext();
_userRepository = userRepository;
}
I'm usually solving this with a DbContextFactory. This would allow you to create the context when needed and also dispose it when you're done.
public interface IDbContextFactory
{
IdentityDbContext<User> GetContext();
}
public class DbContextFactory : IDbContextFactory
{
private readonly IdentityDbContext<User> _context;
public DbContextFactory()
{
_context = new RequestContext("ConnectionStringName");
}
public IdentityDbContext<User> GetContext()
{
return _context;
}
}
This factory can easily be injected. You can see a more complete example here: Repository Pattern universal application
With the factory you will also have the option to create the DbContext in constructor or in the method. When using Unity I recommend you to do as little as possible in the constructor, as Unity will resolve the entire chain for you. This means that the DbContext will be created every time the repository is resolved. This would require the class that injects the repository also needs to dispose the repository (which in turn should dispose the DbContext), and what happens when two classes are using the same repository instance? This can obviously be solved with lifetimemanagers and good programming practices, but I find it more elegant to simply open and close the context when needed.
Example for usage in a method:
using (var context = _dbContextFactory.GenerateContext())
{
return context.Requests.FirstOrDefault(x => x.Id == foo);
}
And a more complete example for your repository:
public class RequestRepository
{
private IDbContextFactory _contextFactory;
public RequestRepository(IDbContextFactory contextFactory)
{
// DbContext will not be created in constructor, and therefore your repository doesn't have to implement IDisposable.
_contextFactory= contextFactory;
}
public Request FindById(int id)
{
// Context will be properly disposed thanks to using.
using (var context = _dbContextFactory.GenerateContext())
{
return context.Requests.FirstOrDefault(x => x.Id == id);
}
}
}
And when you're creating your interface for your context I can also recommend you to change DbSet<T> to IDbSet<T> to allow easier unit testing. Example of interface for DbContext.
public interface IDbContext : IDisposable, IObjectContextAdapter
{
IDbSet<Request> Requests { get; set; }
IDbSet<Record> Records { get; set; }
int SaveChanges();
DbSet Set(Type entityType);
DbSet<TEntity> Set<TEntity>() where TEntity : class;
}
If your're looking to inject the DbContext in the constructor you could also take a look at the Unit of Work-pattern, which wraps the DbContext and allows several classes to use the same context over a specific lifetime (eg a request). One may argue that EF already implements the Unit of Work-pattern, but I leave that discussion for another time. Here's a couple of examples:
http://www.codeproject.com/Articles/741207/Repository-with-Unit-of-Work-IoC-and-Unit-Test
Onion Architecture, Unit of Work and a generic Repository pattern
This site has a GREAT tutorial on how to get Unity working: https://medium.com/aeturnuminc/repository-pattern-with-dependency-injection-mvc-ef-code-first-91344413ba1c
I'm going to assume you have Entity Framework installed, know how to create a viewmodel and put on attributes using System.ComponentModel.DataAnnotations and System.ComponentModel.DataAnnotations.Schema namespaces, and I'll assume you can create a view and controller. None of that is really relevant until the end, anyway.
You have to get the NuGet package Unity to install these references:
Microsoft.Practices.Unity
Unity.Mvc3 (or 4 or 5)
My DataContext (Model1.cs) looks like this:
public partial class Model1 : DbContext
{
public Model1()
: this(true)
{ }
public Model1(bool enableLazyLoading = true)
: base("name=Model1")
{
// You can do this....
//Database.SetInitializer<Model1>(new CreateDatabaseIfNotExists<Model1>());
//this.Configuration.LazyLoadingEnabled = false;
// or this...
Database.SetInitializer<Model1>(null);
this.Configuration.ProxyCreationEnabled = false;
((IObjectContextAdapter)this).ObjectContext.ContextOptions.ProxyCreationEnabled = enableLazyLoading;
((IObjectContextAdapter)this).ObjectContext.ContextOptions.LazyLoadingEnabled = enableLazyLoading;
}
// All my tables and views are assigned to models, here...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
}
My Repository (DataRepository.cs) looks like this:
namespace WeeklyReport.Repository
{
public class DataRepository : IDataRepository
{
private bool disposing;
private readonly Model1 context;
public virtual void Dispose()
{
if (disposing)
{
return;
}
disposing = true;
if (context != null)
{
context.Dispose();
}
}
public void SaveChanges()
{
context.SaveChanges();
}
public DataRepository()
{
context = new Model1();
context.Configuration.ProxyCreationEnabled = false;
}
public IEnumerable<ReportViewModel> GetAllMetrics()
{
var myMetrics = context.MetricsTable; // put into ReportVM and return, etc.
}
// etc., etc.
}
}
My Interface (IDataRepository.cs) looks like this:
namespace WeeklyReport.Repository
{
public interface IDataRepository
{
void SaveChanges();
IEnumerable<ReportViewModel> GetAllMetrics();
}
}
My UnityConfig.cs in the App_Start folder looks like this:
using Microsoft.Practices.Unity;
using WeeklyReport.Repository;
using System.Web.Mvc;
using Unity.Mvc3;
namespace WeeklyReport
{
public class UnityConfig
{
public static void RegisterContainer()
{
var container = new UnityContainer();
//ensure the repository is disposed after each request by using the lifetime manager
container.RegisterType<IDataRepository, DataRepository>(new HierarchicalLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
}
}
And you have to call RegisterContainer in Global.ascx.cs inside Application_Start:
UnityConfig.RegisterContainer();
From a controller, it gets a handle to IDataRepository:
using WeeklyReport.Repository;
namespace WeeklyReport.Controllers
{
public class ReportController : Controller
{
private readonly IDataRepository repository;
public ReportController(IDataRepository repository)
{
this.repository = repository;
}
public ActionResult Index()
{
List<ReportViewModel> reportVM = new List<ReportViewModel>();
var reqs = repository.GetAllMetrics();
// iterate reqs and put into reportVM, etc.
return View(reportVM);
}
}
}
And you can call repository as if it was the real class - you're just getting an interface instance to it.
I'm solving this problem with DbContext.Set<TEntity>() method, DbContext wrapper class and generics.
I have IRepositoryContext interface and RepositoryContext to wrap my DbContext:
public interface IRepositoryContext
{
DbContext DbContext { get; }
/// <summary>
/// Commit data.
/// </summary>
void Save();
}
public class RepositoryContext : IRepositoryContext
{
private readonly DbContext _dbContext;
public RepositoryContext(DbContext dbContext)
{
_dbContext = dbContext;
}
public DbContext DbContext { get { return _dbContext; } }
public void Save()
{
_dbContext.SaveChanges();
}
}
Ok, then I write bas implementation of generic repository:
public abstract class RepositoryBase<TEntity, TId> : IRepository<TEntity, TId>
where TEntity : class , IEntity<TId>, IRetrievableEntity<TEntity, TId>
where TId : struct
{
protected readonly IRepositoryContext RepositoryContext;
protected readonly DbContext Context;
protected RepositoryBase(IRepositoryContext repositoryContext)
{
RepositoryContext = repositoryContext;
}
public DbSet<TEntity> Data { get { return RepositoryContext.DbContext.Set<TEntity>(); }
public TEntity Get(TId id)
{
return Data.Find(id);
}
public virtual IList<TEntity> GetAll()
{
return Data.ToList();
}
public virtual TEntity Save(TEntity entity)
{
try
{
var state = entity.Id.Equals(default(TId)) ? EntityState.Added : EntityState.Modified;
RepositoryContext.DbContext.Entry(entity).State = state;
RepositoryContext.Save();
return entity;
}
catch (DbEntityValidationException e)
{
throw ValidationExceptionFactory.GetException(e);
}
}
public virtual void Delete(TEntity entity)
{
if (entity == null) return;
Data.Remove(entity);
Context.SaveChanges();
}
public void Commit()
{
RepositoryContext.Save();
}
public IList<TEntity> Get(Expression<Func<TEntity, bool>> criteria)
{
return Data.Where(criteria).ToList();
}
// some other base stuff here
}
Ok, now I can register my DbContext with next extension methods:
public static class RikropCoreDataUnityExtensions
{
#region Const
private readonly static Type _repositoryInterfaceType = typeof(IRepository<,>);
private readonly static Type _deactivatableRepositoryInterfaceType = typeof(IDeactivatableRepository<,>);
private readonly static Type _deactivatableEntityType = typeof(DeactivatableEntity<>);
private readonly static Type _retrievableEntityType = typeof(IRetrievableEntity<,>);
#endregion Const
#region public methods
/// <summary>
/// Register wrapper class.
/// </summary>
/// <typeparam name="TContext">DbContext type.</typeparam>
/// <param name="container">Unity-container.</param>
public static void RegisterRepositoryContext<TContext>(this IUnityContainer container)
where TContext : DbContext, new()
{
container.RegisterType<IRepositoryContext, RepositoryContext>(new InjectionFactory(c => new RepositoryContext(new TContext())));
}
/// <summary>
/// Register wrapper class.
/// </summary>
/// <typeparam name="TContext">DbContext type.</typeparam>
/// <param name="container">Unity-container.</param>
/// <param name="contextConstructor">DbContext constructor.</param>
/// <param name="connectionString">Connection string name.</param>
public static void RegisterRepositoryContext<TContext>(this IUnityContainer container,
Func<string, TContext> contextConstructor, string connectionString)
where TContext : DbContext
{
container.RegisterType<IRepositoryContext, RepositoryContext>(
new InjectionFactory(c => new RepositoryContext(contextConstructor(connectionString))));
}
/// <summary>
/// Automatically generation and registration for generic repository marked by attribute.
/// </summary>
/// <param name="container">Unity-container.</param>
/// <param name="assembly">Assembly with repositories marked with RepositoryAttribute.</param>
public static void RegisterCustomRepositories(this IUnityContainer container, Assembly assembly)
{
foreach (var repositoryType in assembly.GetTypes().Where(type => type.IsClass))
{
var repositoryAttribute = repositoryType.GetCustomAttribute<RepositoryAttribute>();
if (repositoryAttribute != null)
{
container.RegisterType(
repositoryAttribute.RepositoryInterfaceType,
repositoryType,
new TransientLifetimeManager());
}
}
}
/// <summary>
/// Automatically generation and registration for generic repository for all entities.
/// </summary>
/// <param name="container">Unity-container.</param>
/// <param name="assembly">Assembly with Entities which implements IRetrievableEntity.</param>
public static void RegisterRepositories(this IUnityContainer container, Assembly assembly)
{
foreach (var entityType in assembly.GetTypes().Where(type => type.IsClass))
{
if (!entityType.InheritsFromGeneric(_retrievableEntityType))
continue;
Type[] typeArgs = entityType.GetGenericTypeArguments(_retrievableEntityType);
Type constructedRepositoryInterfaceType = _repositoryInterfaceType.MakeGenericType(typeArgs);
container.RegisterRepository(constructedRepositoryInterfaceType);
if (entityType.InheritsFrom(_deactivatableEntityType.MakeGenericType(new[] { typeArgs[1] })))
{
var constructedDeactivatableRepositoryInterfaceType =
_deactivatableRepositoryInterfaceType.MakeGenericType(typeArgs);
container.RegisterRepository(constructedDeactivatableRepositoryInterfaceType);
}
}
}
#endregion public methods
#region private methods
/// <summary>
/// Generate and register repository.
/// </summary>
/// <param name="container">Unity-container.</param>
/// <param name="repositoryInterfaceType">Repository interface type.</param>
private static void RegisterRepository(this IUnityContainer container, Type repositoryInterfaceType)
{
var factoryGenerator = new RepositoryGenerator();
var concreteFactoryType = factoryGenerator.Generate(repositoryInterfaceType);
container.RegisterType(
repositoryInterfaceType,
new TransientLifetimeManager(),
new InjectionFactory(
c =>
{
var activator = new RepositoryActivator();
return activator.CreateInstance(c, concreteFactoryType);
}));
}
#endregion private methods
}
Finally you can just resolve IRepository<EntityType> on your classes. You just need to register your RepositoryContext:
container.RegisterRepositoryContext<MyDbContext>();
//container.RegisterRepositoryContext(s => new MyDbContext(s), "myConStr");
And your repository will resolve IRepositoryContext and you can have access to DbSet<TEntity> and other DbContext members via IRepositoryContext property.
You can use full source code for repositories, Unity-helpers on Github.
Introduction
Class SessionModel is a service locator providing several services (I am going to elaborate my system architecture in the future, but for now I need to do it that way).
Code
I edited the following code part to be a Short, Self Contained, Correct (Compilable), Example (SSCCE):
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var sessionModel = new SessionModel(3);
// first case (see text down below):
var compositionContainer = new CompositionContainer();
// second case (see text down below):
//var typeCatalog = new TypeCatalog(typeof (SessionModel));
//var compositionContainer = new CompositionContainer(typeCatalog);
compositionContainer.ComposeExportedValue(sessionModel);
var someService = compositionContainer.GetExportedValue<ISomeService>();
someService.DoSomething();
}
}
public class SessionModel
{
private int AValue { get; set; }
[Export]
public ISomeService SomeService { get; private set; }
public SessionModel(int aValue)
{
AValue = aValue;
// of course, there is much more to do here in reality:
SomeService = new SomeService();
}
}
public interface ISomeService
{
void DoSomething();
}
public class SomeService : ISomeService
{
public void DoSomething()
{
Console.WriteLine("DoSomething called");
}
}
}
Problem
I would like MEF to consider the parts (i.e. SomeService) exported by the service locator when composing other parts, but unfortunately this does not work.
First Case
When I try to get the exported value for ISomeService there is a System.ComponentModel.Composition.ImportCardinalityMismatchException telling me there are no exports with this contract name and required type identity (ConsoleApplication1.ISomeService).
Second Case
If I create the CompositionContainer using the TypeCatalog the exception is slightly different. It is a System.ComponentModel.Composition.CompositionException telling me MEF doesn't find a way to create a ConsoleApplication1.SessionModel (which is right and the reason why I am doing it myself).
Additional Information
mefx says for both cases:
[Part] ConsoleApplication1.SessionModel from: DirectoryCatalog (Path=".")
[Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")
[Part] ConsoleApplication1.SessionModel from: AssemblyCatalog (Assembly="ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")
What do I have to do? Is this possible with MEF or do I have to use Unity or StructureMap, or something else? Can this be done implementing an ExportProvider?
OK, that's how I did it:
I implemented my own SessionModelExportProvider finding exports in my SessionModel (see code below). Class SessionModelExport is just for holding the export data and – instead of creating an instance of a service – returning the value of the property of the SessionModel.
public class SessionModelExportProvider : ExportProvider
{
private List<Export> Exports { get; set; }
public SessionModelExportProvider(SessionModel sessionModel)
{
// get all the properties of the session model having an Export attribute
var typeOfSessionModel = typeof (SessionModel);
PropertyInfo[] properties = typeOfSessionModel.GetProperties();
var propertiesHavingAnExportAttribute =
from p in properties
let exportAttributes = p.GetCustomAttributes(typeof (ExportAttribute), false)
where exportAttributes.Length > 0
select new
{
PropertyInfo = p,
ExportAttributes = exportAttributes
};
// creating Export objects for each export
var exports = new List<Export>();
foreach (var propertyHavingAnExportAttribute in propertiesHavingAnExportAttribute)
{
var propertyInfo = propertyHavingAnExportAttribute.PropertyInfo;
foreach (ExportAttribute exportAttribute in propertyHavingAnExportAttribute.ExportAttributes)
{
string contractName = exportAttribute.ContractName;
if (string.IsNullOrEmpty(contractName))
{
Type contractType = exportAttribute.ContractType ?? propertyInfo.PropertyType;
contractName = contractType.FullName;
}
var metadata = new Dictionary<string, object>
{
{CompositionConstants.ExportTypeIdentityMetadataName, contractName},
{CompositionConstants.PartCreationPolicyMetadataName, CreationPolicy.Shared}
};
var exportDefinition = new ExportDefinition(contractName, metadata);
var export = new SessionModelExport(sessionModel, propertyInfo, exportDefinition);
exports.Add(export);
}
}
Exports = exports;
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition,
AtomicComposition atomicComposition)
{
return Exports.Where(e => definition.IsConstraintSatisfiedBy(e.Definition));
}
}
public class SessionModelExport : Export
{
private readonly SessionModel sessionModel;
private readonly PropertyInfo propertyInfo;
private readonly ExportDefinition definition;
public SessionModelExport(SessionModel sessionModel, PropertyInfo propertyInfo, ExportDefinition definition)
{
this.sessionModel = sessionModel;
this.propertyInfo = propertyInfo;
this.definition = definition;
}
public override ExportDefinition Definition
{
get { return definition; }
}
protected override object GetExportedValueCore()
{
var value = propertyInfo.GetValue(sessionModel, null);
return value;
}
}
The problem is that the SomeService is an instance property. You could have several SessionModel objects in your system, and MEF would have no way of knowing which SessionModel is returning the ISomeService instance that is supposed to be matched to an import.
Instead, just make SessionModel a static class and SomeService a static property. Alternatively, make SessionModel a singleton. The SomeService property would still be static, but would export the service from the one-and-only instance of SessionModel.
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.ReflectionModel;
using System.Reflection;
using System.Linq;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var catalogs = new AggregateCatalog();
var catalog = new System.ComponentModel.Composition.Hosting.AssemblyCatalog(Assembly.GetExecutingAssembly());
catalogs.Catalogs.Add(catalog);
var sessionModel = new SessionModel(3);
var container = new CompositionContainer(catalog);
ISomeService someService = container.GetExportedValueOrDefault<ISomeService>(sessionModel.cname);
if (someService != null)
{
someService.DoSomething();
}
}
}
public class SessionModel
{
private int AValue { get; set; }
//[Import("One",typeof(ISomeService))]
//public ISomeService SomeService { get; private set; }
public SessionModel(int aValue)
{
AValue = aValue;
// of course, there is much more to do here in reality:
}
public string cname { get { return "One"; } }
}
public class SessionModel1
{
private int AValue { get; set; }
//[Import("Two",typeof(ISomeService))]
//public ISomeService SomeService { get; private set; }
public SessionModel1(int aValue)
{
AValue = aValue;
}
public string cname { get { return "Two"; } }
}
public interface ISomeService
{
void DoSomething();
}
[Export("One",typeof(ISomeService))]
public class SomeService : ISomeService
{
public SomeService()
{
Console.WriteLine("Some Service Called");
}
public void DoSomething()
{
Console.WriteLine("DoSomething called");
Console.ReadKey();
}
}
[Export("Two",typeof(ISomeService))]
public class SomeService1 : ISomeService
{
public SomeService1()
{
Console.WriteLine("Some Service1 Called");
}
public void DoSomething()
{
Console.WriteLine("DoSomething called 1");
Console.ReadKey();
}
}
}
First case: By passing sessionModel to ComposeExportedValue you add a part of type SessionModel and not of ISomeService. To make this case work you nee to pass the service to ComposeExportedValue.
compositionContainer.ComposeExportedValue(sessionModel.SomeService);
Second case: In this case you leave the creation of parts to the container. The container can create new parts if there is either a parameter-less constructor or a constructor with parameters decorated with the ImportingConstructorAttribute. This most probably means that you will need to change your design a bit.
Personally I would go with the first case, but try to keep this to a minimum. After all the normal (and suggested) usage of MEF is letting the container create and handle parts.