Well im trying to create a custom container class that contains in my case ViewMappingEntry's, so i created a class that inherits Freezable and implements ICollection.. but when creating an instance in Blend and browsing it i get this error: Cannot add content to an object of type 'ViewModelMapping'..
Note: It compiles fine and i get no error in Visual studio, so what am i missing?
public class ViewModelMapping : Freezable, ICollection<MappingEntry>
{
object locker = new object();
List<MappingEntry> internalList = new List<MappingEntry>();
#region ICollection<MappingEntry> Members
public void Add(MappingEntry item)
{
if (this.IsFrozen || this.IsSealed)
throw new InvalidOperationException("ViewModelMapping is frozen");
lock (locker)
internalList.Add(item);
}
public void Clear()
{
if (this.IsFrozen || this.IsSealed)
throw new InvalidOperationException("ViewModelMapping is frozen");
lock (locker)
internalList.Clear();
}
public bool Contains(MappingEntry item)
{
lock (locker)
return internalList.Contains(item);
}
public void CopyTo(MappingEntry[] array, int arrayIndex)
{
lock (locker)
internalList.CopyTo(array, arrayIndex);
}
public int Count
{
get
{
lock (locker)
return internalList.Count;
}
}
public bool IsReadOnly
{
get { return base.IsFrozen || base.IsSealed; }
}
public bool Remove(MappingEntry item)
{
if (this.IsFrozen || this.IsSealed)
throw new InvalidOperationException("ViewModelMapping is frozen");
lock (locker)
return internalList.Remove(item);
}
#endregion
#region IEnumerable<MappingEntry> Members
public IEnumerator<MappingEntry> GetEnumerator()
{
lock (locker)
return (IEnumerator<MappingEntry>)internalList.ToArray().GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
lock (locker)
return internalList.ToArray().GetEnumerator();
}
#endregion
protected override Freezable CreateInstanceCore()
{
ViewModelMapping viewModelMapping = new ViewModelMapping();
lock(locker)
viewModelMapping.internalList = new List<MappingEntry>(this.internalList.ToArray());
return viewModelMapping;
}
}
after trying around i found a solution if i implemented all these interfaces -> ICollection, IList, IList, ICollection, IEnumerable it started working in blend.
Related
I have a problem.
I have in my new table two new fields
1) Name -> AccountNum, EDT--> DimensionDynamicAccount
2) Name -> AccountType, EDT--> LedgerJournalACType
class declaration
:
public class FormRun extends ObjectRun
{
DimensionDynamicAccountController dimAccountController;
}
init (for the form):
public void init()
{
super();
dimAccountController = DimensionDynamicAccountController::construct(
MyTable_ds,
fieldstr(MyTable, LedgerDimension),
fieldstr(MyTable, AccountType));
}
4. Override the following methods on the Segmented Entry control instance in the form design.
public void jumpRef()
{
dimAccountController.jumpRef();
}
public void loadAutoCompleteData(LoadAutoCompleteDataEventArgs _e)
{
super(_e);
dimAccountController.loadAutoCompleteData(_e);
}
public void segmentValueChanged(SegmentValueChangedEventArgs _e)
{
super(_e);
dimAccountController.segmentValueChanged(_e);
}
public void loadSegments()
{
super();
dimAccountController.parmControl(this);
dimAccountController.loadSegments();
}
public boolean validate()
{
boolean isValid;
isValid = super();
isValid = dimAccountController.validate() && isValid;
return isValid;
}
5. Override the following methods on the data source field that backs the Segmented Entry control.
public Common resolveReference(FormReferenceControl _formReferenceControl)
{
return dimAccountController.resolveReference();
}
Now my problem is Lookup only works for AccountType=="Ledger" not for customer, Vendor etc...
If I have a AccountType == Vendor or similant but different to Ledger I see this
I would want to have same the same thing that's in the LedgerJournalTrans Form
There is a solution,
thanks all,
enjoy
This might be too obvious, but I think you're missing the lookup() method.
See:
\Forms\LedgerJournalTransDaily\Designs\Design\[Tab:Tab]\[TabPage:OverViewTab]\[Grid:overviewGrid]\SegmentedEntry:LedgerJournalTrans_AccountNum\Methods\lookup
public void lookup()
{
if (!ledgerJournalEngine.accountNumLookup(ledgerJournalTrans_AccountNum,
ledgerJournalTrans,
ledgerJournalTrans.OffsetAccountType,
ledgerJournalTrans.parmOffsetAccount(),
ledgerJournalTrans_Asset))
{
super();
}
}
My database have different schema depending on user selections on runtime.
My code is below:
public partial class FashionContext : DbContext
{
private string _schema;
public FashionContext(string schema) : base()
{
_schema = schema;
}
public virtual DbSet<Style> Styles { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(#"Server=.\sqlexpress;Database=inforfashionplm;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Style>()
.ToTable("Style", schema: _schema);
}
}
Upon testing. I created a context instance with 'schema1'.
So far so good.
But when I create another context instance with different schema 'schema2', the resulting data in which the schema is still on 'schema1'.
Here is the implementation:
using (var db = new FashionContext("schema1"))
{
foreach (var style in db.Styles)
{
Console.WriteLine(style.Name);
}
}
Console.ReadLine();
Console.Clear();
using (var db = new FashionContext("schema2"))
{
foreach (var style in db.Styles)
{
Console.WriteLine(style.Name);
}
}
Console.ReadLine();
Later I noticed that the OnModelCreating is called only one time, so it is never called again when you create a new context instance of the same connection string.
Is it possible to have dynamic schema on runtime? Note: this is possible in EF6
One of possible way was mentioned above, but briefly, so I will try to explain with examples.
You ought to override default ModelCacheKeyFactory and ModelCacheKey.
ModelCachekeyFactory.cs
internal sealed class CustomModelCacheKeyFactory<TContext> : ModelCacheKeyFactory
where TContext : TenantDbContext<TContext>
{
public override object Create(DbContext context)
{
return new CustomModelCacheKey<TContext>(context);
}
public CustomModelCacheKeyFactory([NotNull] ModelCacheKeyFactoryDependencies dependencies) : base(dependencies)
{
}
}
ModelCacheKey.cs, please review Equals and GetHashCode overridden methods, they are not best one and should be improved.
internal sealed class ModelCacheKey<TContext> : ModelCacheKey where TContext : TenantDbContext<TContext>
{
private readonly string _schema;
public ModelCacheKey(DbContext context) : base(context)
{
_schema = (context as TContext)?.Schema;
}
protected override bool Equals(ModelCacheKey other)
{
return base.Equals(other) && (other as ModelCacheKey<TContext>)?._schema == _schema;
}
public override int GetHashCode()
{
var hashCode = base.GetHashCode();
if (_schema != null)
{
hashCode ^= _schema.GetHashCode();
}
return hashCode;
}
}
Register in DI.
builder.UseSqlServer(dbConfiguration.Connection)
.ReplaceService<IModelCacheKeyFactory, CustomModelCacheKeyFactory<CustomContext>>();
Context sample.
public sealed class CustomContext : TenantDbContext<CustomContext>
{
public CustomContext(DbContextOptions<CustomContext> options, string schema) : base(options, schema)
{
}
}
You can build the model externally and pass it into the DbContext using DbContextOptionsBuilder.UseModel()
Another (more advanced) alternative is to replace the IModelCacheKeyFactory to take schema into account.
I found a way to recreate the compiled model on each context creation.
public partial class MyModel : DbContext {
private static DbConnection _connection
{
get
{
//return a new db connection
}
}
private static DbCompiledModel _model
{
get
{
return CreateModel("schema name");
}
}
public MyModel()
: base(_connection, _model, false)
{
}
private static DbCompiledModel CreateModel(string schema)
{
var modelBuilder = new DbModelBuilder();
modelBuilder.HasDefaultSchema(schema);
modelBuilder.Entity<entity1>().ToTable(schema + ".entity1");
var builtModel = modelBuilder.Build(_connection);
return builtModel.Compile();
}
}
We are using Caliburn Micro for the first time.
We have a AppBootstrapper inherited from ShellViewModel.
Situvation is that VieModels should have the same instance unless it is reset.
we are able to achieve shared or not shared everytime, but releasing the export whenever needed is still a mystery.
public class AppBootstrapper : Bootstrapper<ShellViewModel>
{
private static CompositionContainer _container;
protected override void Configure()
{
try
{
_container = new CompositionContainer(
new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x))));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(_container);
StyleManager.ApplicationTheme = ThemeManager.FromName("Summer");
_container.Compose(batch);
}
catch (Exception exception)
{
}
}
public static void ReleaseAll()
{
}
protected override object GetInstance(Type serviceType, string key)
{
try
{
var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
var exports = _container.GetExportedValues<object>(contract);
if (exports.Any())
return exports.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
catch (ReflectionTypeLoadException ex)
{
foreach (Exception inner in ex.LoaderExceptions)
{
// write details of "inner", in particular inner.Message
}
return null;
}
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
try
{
return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
}
catch (Exception exception)
{
return null;
}
}
protected override void BuildUp(object instance)
{
_container.SatisfyImportsOnce(instance);
}
}
ShellViewModel
[Export(typeof(ShellViewModel))]
public sealed class ShellViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public ShellViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
Items.Add(compositionContainer.GetExportedValue<AViewModel>());
Items.Add(compositionContainer.GetExportedValue<BViewModel>());
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
public void B()
{
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.B.ToString()));
}
public void A()
{
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public void RESET()
{
AppBootstrapper.ReleaseAll();
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public enum AppMessageType
{
A,
B
}
}
AViewModel
[Export(typeof(AViewModel))]
public sealed class AViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public AViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
DisplayName = ShellViewModel.AppMessageType.A.ToString();
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
}
BViewModel
[Export(typeof(BViewModel))]
public sealed class BViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public BViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
DisplayName = ShellViewModel.AppMessageType.B.ToString();
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
}
Now AViewModel and BViewModel have single instance.
Whenever Release Button is clicked i want to have new instance of AViewModel and BViewModel.
Hoping to get a reply soon.
Regards,
Vivek
When working with an IoC container, the only part of your code that should take it as a dependency should be your composition root (i.e. your AppBootstrapper in this case). You shouldn't be injecting or referencing the container anywhere else in your code (except possibly factories).
If you want your ShellViewModel to control the lifetime of your child view models (A and B), then you should consider injecting view model factories into your ShellViewModel (via constructor injection if they are required dependencies).
Your AViewModelFactory would just have a single Create method that returns a new instance of AViewModel, likewise with the BViewModelFactory. You can simply new up your view models directly in the factories. If your view models have large dependency chains themselves, then you could consider adding a reference to your container in the factories, although preferably consider looking into the MEF ExportFactory<T> type.
Is there a way I can use Autofac as my primary container for Catel?
I've seen that there is support for Unity, Ninject, MEF, Windsor, and Unity, but there is no mention of Autofac integration.
We haven't created an helper for Autofac, nor are we planning to write one.
You have the following options:
1) Write the Helper class yourself (see the others for examples) and register it, then the ServiceLocator can sync it
2) In the latest prerelease of Catel (3.7 prerelease) we have introduced the IDependencyResolver. You can implement this interface on your Autofac container and register that in the ServiceLocator. Note though that we don't have full support for this so it might cause side-effects.
3) Use the ServiceLocator in Catel
edit on 2013-09-14
4) If you are interested in the latest version of Catel (nightly build), you can now replace the default service locator with your implementation:
Issue details: https://catelproject.atlassian.net/browse/CTL-175
Documentation: https://catelproject.atlassian.net/wiki/pages/viewpage.action?pageId=622682#IoC(ServiceLocatorandTypeFactory)-Replacingthedefaultcomponents
Here is my solution for Catel 3.8 and Autofac (should work for other 3rd party containers too).
I wrote an implementation for IDependencyResolver backed by an Autofac container and an implementation for IServiceLocator. The former contains the applications 3rd party IoC configuration and logic. The latter is used to grab all type and instance registrations of Catels modules and foreward them to Autofac.
The basic strategy is
Create and configure a 3rd party container
Use it to instanciate (partially customized) Catel IoC components and introduce them to Catels IoC configuration
Let Catel do its type and instance registration using the previously created (customized) implementations and foreward them to the third party container
So here is my implementation for the IServiceLocator:
internal class CustomServiceLocator : IServiceLocator
{
private readonly CustomDependencyResolver _dependencyResolver;
public CustomServiceLocator(CustomDependencyResolver dependencyResolver)
{
_dependencyResolver = dependencyResolver;
}
public object GetService(Type serviceType)
{
throw new NotImplementedException();
}
public RegistrationInfo GetRegistrationInfo(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public bool IsTypeRegistered(Type serviceType, object tag = null)
{
return _dependencyResolver.CanResolve(serviceType, tag);
}
public bool IsTypeRegisteredAsSingleton(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public void RegisterInstance(Type serviceType, object instance, object tag = null)
{
var builder = new ContainerBuilder();
IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle> registrationBuilder = builder.RegisterInstance(instance);
if (tag != null)
{
registrationBuilder.Keyed(tag, serviceType);
}
_dependencyResolver.UpdateContainer(builder);
}
public void RegisterType(Type serviceType, Type serviceImplementationType, object tag = null, RegistrationType registrationType = RegistrationType.Singleton,
bool registerIfAlreadyRegistered = true)
{
var builder = new ContainerBuilder();
IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle> registrationBuilder = builder.RegisterType(serviceImplementationType).As(serviceType);
if (tag != null)
{
registrationBuilder.Keyed(tag, serviceType);
}
switch (registrationType)
{
case RegistrationType.Singleton:
registrationBuilder.SingleInstance();
break;
case RegistrationType.Transient:
registrationBuilder.InstancePerDependency();
break;
default:
registrationBuilder.InstancePerDependency();
break;
}
_dependencyResolver.UpdateContainer(builder);
TypeRegistered(this, new TypeRegisteredEventArgs(serviceType, serviceImplementationType, tag, registrationType));
}
public object ResolveType(Type serviceType, object tag = null)
{
// Must be implemented. Catels ViewModelBase resolves the DependencyResolver in ctor using the ServiceLocator...Why???
return _dependencyResolver.Resolve(serviceType, tag);
}
public IEnumerable<object> ResolveTypes(Type serviceType)
{
throw new NotImplementedException();
}
public void RemoveInstance(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public void RemoveAllInstances(Type serviceType)
{
throw new NotImplementedException();
}
public void RemoveAllInstances(object tag = null)
{
throw new NotImplementedException();
}
public bool IsExternalContainerSupported(object externalContainer)
{
throw new NotImplementedException();
}
public void RegisterExternalContainer(object externalContainer)
{
throw new NotImplementedException();
}
public void RegisterExternalContainerHelper(IExternalContainerHelper externalContainerHelper)
{
throw new NotImplementedException();
}
public void ExportInstancesToExternalContainers()
{
throw new NotImplementedException();
}
public void ExportToExternalContainers()
{
throw new NotImplementedException();
}
public bool AreAllTypesRegistered(params Type[] types)
{
return _dependencyResolver.CanResolveAll(types);
}
public object[] ResolveAllTypes(params Type[] types)
{
return _dependencyResolver.ResolveAll(types);
}
public bool AutomaticallyKeepContainersSynchronized { get; set; }
public bool CanResolveNonAbstractTypesWithoutRegistration { get; set; }
public bool SupportDependencyInjection { get; set; }
public bool AutoRegisterTypesViaAttributes { get; set; }
public bool IgnoreRuntimeIncorrectUsageOfRegisterAttribute { get; set; }
public event EventHandler<MissingTypeEventArgs> MissingType;
public event EventHandler<TypeRegisteredEventArgs> TypeRegistered;
}
And here the implementation for IDepencyResolver.
internal class CustomDependencyResolver : IDependencyResolver
{
private readonly IContainer _container;
public CustomDependencyResolver()
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies());
builder.RegisterInstance(this).SingleInstance(); // dependency of CustomServiceLocator
builder.RegisterInstance(this).As<IDependencyResolver>().SingleInstance(); // Dependency of ViewModelBase. Catels ViewModelBase resolves the DependencyResolver in ctor using the ServiceLocator...Why???
builder.RegisterType<CustomServiceLocator>().As<IServiceLocator>().SingleInstance(); // dependency of TypeFactory (subscribes to TypeRegistered event to clear its cache)
builder.RegisterType<TypeFactory>().As<ITypeFactory>().SingleInstance(); // dependency of ViewModelFactory
_container = builder.Build();
}
public bool CanResolve(Type type, object tag = null)
{
return _container.IsRegistered(type);
}
public bool CanResolveAll(Type[] types)
{
return types.All(type => _container.IsRegistered(type));
}
public object Resolve(Type type, object tag = null)
{
object obj;
if (tag == null)
{
if (_container.TryResolve(type, out obj))
return obj;
}
else
{
if (_container.TryResolveKeyed(tag, type, out obj))
return obj;
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", tag ?? type.Name));
}
public object[] ResolveAll(Type[] types, object tag = null)
{
var objects = new ArrayList();
if (tag == null)
{
foreach (Type type in types)
{
object obj;
if (_container.TryResolve(type, out obj))
{
objects.Add(obj);
}
else
{
throw new Exception(string.Format("Could not locate any instances of contract {0}.", type.Name));
}
}
}
else
{
foreach (Type type in types)
{
object obj;
if (_container.TryResolveKeyed(tag, type, out obj))
{
objects.Add(obj);
}
else
{
throw new Exception(string.Format("Could not locate any instances of contract {0}.", tag));
}
}
}
return objects.ToArray();
}
public void UpdateContainer(ContainerBuilder builder)
{
builder.Update(_container);
}
}
Putting it all together at startup:
public partial class App : Application
{
/// <summary>
/// Raises the <see cref="E:System.Windows.Application.Startup"/> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.StartupEventArgs"/> that contains the event data.</param>
protected override void OnStartup(StartupEventArgs e)
{
#if DEBUG
Catel.Logging.LogManager.AddDebugListener();
#endif
StyleHelper.CreateStyleForwardersForDefaultStyles();
// create the DependencyResolver and do Catel IoC configuration
CustomDependencyResolver dependencyResolver = new CustomDependencyResolver();
DependencyResolverManager.Default.DefaultDependencyResolver = dependencyResolver;
IoCConfiguration.DefaultDependencyResolver = dependencyResolver;
IoCConfiguration.DefaultServiceLocator = dependencyResolver.Resolve<IServiceLocator>();
IoCConfiguration.DefaultTypeFactory = dependencyResolver.Resolve<ITypeFactory>();
// let Catel register its dependencies
Catel.Core.ModuleInitializer.Initialize();
Catel.MVVM.ModuleInitializer.Initialize();
base.OnStartup(e);
}
}
Tradoffs and side effects: In my opinion Catels IoC implementation is a litte bit fuzzy. e.g. Sometimes the ServiceLocator is used to resolve the DependencyResolver, sometimes the other way around. I tried to figure out the most common internally used ways and cover them with my solution. A possible side effect may occure, if a type is registered to Autofacs container after application startup and the TypeFactory is not notified to clear its cache (i did not analyse the TypeFactory itself). I suggest an implementation for a TypeRegistered event in CustomDependencyResolver, subscribed by the CustomServiceLocator to foreward it to the TypeFactory.
AsycSubject<Unit>() sub;
// stuff
if(!sub.HasFired())
// Do stuff
Current best attempt is:
public static bool HasFired<T>(this AsyncSubject<T> sub)
{
AsyncSubject<bool> ret = new AsyncSubject<bool>();
sub.Timeout(TimeSpan.FromMilliseconds(20))
.Subscribe(_ =>
{
ret.OnNext(true);
ret.OnCompleted();
},
ex =>
{
ret.OnNext(false);
ret.OnCompleted();
});
return ret.First();
}
But it feels very ugly and long. I suspect I'm missing something simple. Any suggestions?
It's easier to wrap around the existing AsyncSubject and add the required state.
public class AsyncSubjectEx<T> : ISubject<T>, IDisposable
{
AsyncSubject<T> Subject = new AsyncSubject<T>();
public bool HasValue { get; protected set; }
public object Gate = new object();
public void OnCompleted()
{
Subject.OnCompleted();
}
public void OnError(Exception error)
{
Subject.OnError(error);
}
public void OnNext(T value)
{
lock (Gate)
{
Subject.OnNext(value);
HasValue = true;
}
}
public IDisposable Subscribe(IObserver<T> observer)
{
lock (Gate)
return Subject.Subscribe(observer);
}
public void Dispose()
{
Subject.Dispose();
}
}
Ironically, the original AsyncSubject upon reflection shows that there is a hasValue field, but it doesn't happen to be exposed. Consider reporting this to the Rx team - might be useful sometime.