How to make "defered" value export with MEF? - wpf

I have a Caliburn Micro's bootstrapper and I use the MEF as IoC. One of interfaces implementors can throw exception from it's constructor. So, when I do the following:
CompositionBatch batch = new CompositionBatch();
batch.AddExportedValue<IFrProvider>(new ShtrihFr());
Then I get exception at application startup, but I want to get it at the time of resolvation.
How to accomplish that with MEF?
Update 1.
Here what I did:
[Export(typeof (LoadingViewModel))]
public class LoadingViewModel {
public LoadingViewModel() {}
[Import]
private readonly IFrProvider frProvider;
}
[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider {
[ImportingConstructor]
public ShtrihFr(int password = 1) {
}
}
So when I do the following:
protected override object GetInstance(Type serviceType, string key) {
string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
IEnumerable<object> exports = container.GetExportedValues<object>(contract);
var exportedList = exports as IList<object> ?? exports.ToList();
if (exportedList.Any())
return exportedList.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
exportedList count is 0. It can't find the implementation. I cheked that the container has amidts it's parts the ShtrihFr implementation. How to solve the problem?
Update 2.
For debugging purpose I did added the following to the beggining of GetInstance method:
if (serviceType.FullName == "Microtech.Hardware.IFrProvider") {
var export = container.GetExport<IFrProvider>();
var frProvider = export.Value;
}
At line container.GetExport I get ImportCardinalityMismatchException. No exports were found that match the constraint...

One way to achieve this is the following. On application startup create Catalog that contains all composable parts of your application and initialize the CompositionContainer. Here is the code.
var catalog = new DirectoryCatalog(path to the directory that contains your dlls, "*.dll");
var compositionContainer = new CompositionContainer(catalog);
compositionContainer.ComposeParts();
Next mark your ShtrihFr class with Export attribute:
[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider
{
public ShtrihFr()
{
throw new NotImplementedException("Not Implemented");
}
}
This way the CompositionContainer through the DirectoryCatalog will only get list of composable parts and their contracts. Actual instances will not be created, so you won't get exception on application startup.
When you need instance of the ShtrihFr class, you can use one of the following lines:
var part = compositionContainer.GetExportedValue<IFrProvider>();
The above line will throw exception if the constructor throws exception, which is the case now.
var part = compositionContainer.GetExport<IFrProvider>();
The above line will not throw exception when executed, but to get the actual instance you will need to access the Value property of the part variable, which will throw exception.
Update 1:
Add default constructor to the ShtrihFr class, so it look like this:
[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider
{
public ShtrihFr(int password = 1)
{
}
[ImportingConstructor]
public ShtrihFr(int password = 1)
{
}
}
And instead of using the overridden GetInstance method, use something like this, if you need collection of exported parts:
public IEnumerable<T> GetInstances<T>(Type constraint = null)
{
if (constraint == null)
return compositionContainer.GetExportedValues<T>();
return compositionContainer.GetExportedValues<T>(AttributedModelServices.GetContractName(constraint));
}
or something like this, if you need single exported part:
public T GetInstance<T>(Type constraint = null)
{
if (constraint == null)
return compositionContainer.GetExportedValue<T>();
return compositionContainer.GetExportedValue<T>(AttributedModelServices.GetContractName(constraint));
}
In both methods, the type parametar T is the type of the exported part you want to instantiate. The constraint parameter passed to both methods is additional constraint that is optional and if needed is applied to the Export attribute of the classes.

Related

Entity state is DETACHED when it should be Modified

I am trying to modify data and save changes to my database using EFCore 3.1.
but my modifications are not being saved to the database, so after further investigation I found out that after I pull my entity from the context, its state is DETACHED instead of ATTACHED,
so that's why the changes aren't being saved, because they're not tracked in the first place.
I couldn't figure out why this is happening, I made sure that I did not add AsNoTracking() when getting the entity.
Here are my classes and methods:
public class UserSettingsDataAccess : IUserSettingsDataAccess
private readonly NotificationDBContext _context;
private readonly IReminderDatesDataAccess _reminderDatesDataAccess;
public UserSettingsDataAccess(NotificationDBContext context, IReminderDatesDataAccess reminderDatesDataAccess)
{
_context = context;
_reminderDatesDataAccess = reminderDatesDataAccess;
}
public bool ToggleRemindersForAppointmentAsync(int appointment_id)
{
Appointments appointment = _appointmentDataAccess.GetByIdWithReminders(appointment_id);
if (appointment == null || appointment.Reminders == null)
return bool.Parse(null);
var x = _context.Entry(appointment).State;
appointment.Reminders.IsActive = !appointment.Reminders.IsActive;
var y = _context.Entry(appointment).State;
_context.SaveChanges();
var z = _context.Entry(appointment).State;
return appointment.Reminders.IsActive.Value;
}
//rest of code is omitted for brevity
}
This one uses another method to get the appointment, toggle its reminder , save changes and return the new reminder state. all of x,y,z variables have DETACHED value . when debugging
Here's the second class that contains the method that brings the appointment :
public class AppointmentDataAccess: IAppointmentDataAccess
{
private readonly NotificationDBContext _context;
public ReminderDatesDataAccess(NotificationDBContext context)
{
_context = context;
}
public Appointments GetByIdWithReminders(int appointment_id)
{
return _context.Appointments.Where(a => a.Id == appointment_id && a.DeletedAt == null)
.Include(a=>a.Reminders).FirstOrDefault();
}
}
Startup.cs :
services.AddDbContext<NotificationDBContext>(options => options
.UseSqlServer(Configuration.GetConnectionString("Database"))
, ServiceLifetime.Transient,ServiceLifetime.Transient);
and IUserSettingsDataAccess, IAppointmentDataAccess are just interfaces.
Can anyone point out why this is happening? and how to fix it? it's been driving me crazy for a good couple of hours . TIA!

Unity: Type registered with ContainerControlledLifetimeManager is not being Singleton

I have an issue with a Unity Container in a WPF application. I am using Prism with UnityBootstrapper. I need to register a class as a singleton. This is the class:
public class RepositoryBase<T> : IRepository<T> where T : class, new()
{
private string connectionString;
public RepositoryBase(string conne)
{
connectionString = conne;
}
public async Task<List<T>> Get()
{
var db = GetDbConnection();
var value = await db.GetAllWithChildrenAsync<T>(recursive: true);
CloseDatabaseConnection(db);
return value;
}
}
In the bootstrapper, I register the above type as follows:
protected override void ConfigureContainer()
{
var assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
var path = System.IO.Path.GetDirectoryName(assemblyLocation);
path = Path.Combine(path, "Data.db3");
Container.RegisterType(typeof(IRepository<>), typeof(Data.RepositoryBase<>), new ContainerControlledLifetimeManager(), new InjectionConstructor(path));
base.ConfigureContainer();
}
However, if I place a breakpoint in the constructor of the RepositoryBase class, I can see it breaks several times.
Can anyone see what am I doing wrong?

Getting most basic MetaDataType to work

In addition to my original post I guess I need to mention that I am using Prism 6.3. Apparently, the compiler doesn't like stuff added to the metadata class that's not in the original partial. Not sure how to resolve this.
Thanks again ... Ed
Ok, I give, UNCLE!
I am trying to add data annotations to my wpf entity framework app. I've tried 6 ways to Sunday with no luck. I put together what is what I consider the most simple example possible and followed all the instructions ... nothing works.
Here goes.
I have a class that is generated by EF (db first).
namespace junk.DataModels
{
public partial class MyClass
{
public string SomeText { get; set; }
}
}
I have another file with the following partial class:
namespace junk.DataModels
{
[MetadataType(typeof(MyClassMetaData))]
public partial class MyClass
{
}
public partial class MyClassMetaData
{
private string _someText;
[Required(ErrorMessage = "Required")]
public string SomeText
{
get { return _someText; }
set { SetProperty(ref _someText, value); }
}
}
}
In my ViewModel I define:
private MyClass _mc;
public MyClass MC
{
get { return _mc; }
set
{
SetProperty(ref _mc, value);
}
}
And in the constructor:
MC = new MC();
MC.SomeText = "Hello World.";
Lastly, in my xaml:
I have a single bound control:
<TextBox x:Name="txt" Text="{Binding MC.SomeText,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True,
ValidatesOnNotifyDataErrors=True,
UpdateSourceTrigger=PropertyChanged }"
/>
According to everything I've read, if I run this and clear the textbox, I should get a validation error. I have tried all combinations of "ValidatesOn" it doesn't seem to make a difference. Can someone take pity on me and share the secret sauce? I must be missing something simple. If I bind to the metadataclass it works but that is kinda defeating the purpose.
Any help would be great!
Try adding the following static constructor to your buddy class "MyClass". It will register the metadata against your EF class so the Validator can find the Data Annotations:
static MyClass()
{
// Register the metadata against our EF data object.
// This will ensure the Validator find the annotations
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(
typeof(MyClass),
typeof(MyClassMetaData)),
typeof(MyClass)
);
}
You could also try running a unit test to confirm whether the Validator has used your annotation, before adding the complexity of the GUI:
[TestMethod]
public void TestAnnotations()
{
MyClass c = new MyClass();
// Manually validate the MyClass object
List<ValidationResult> validationResults = new List<ValidationResult>();
ValidationContext context = new ValidationContext(c, serviceProvider: null, items: null);
bool isValid = Validator.TryValidateObject(c, context, validationResults, validateAllProperties: true);
Assert.IsFalse(isValid, "Validation should fail because we didn't set SomeText");
}

WPF how to use DesignerProperties.IsInDesignTool to solve a design time error "cannot create an instance"?

I have the following class that I use in order to populate a combo box:
public class DamageTypeList
{
static Begbil2Entities _DB = new Begbil2Entities();
public static List<HUB_DamageTypes> _list = (from d in _DB.HUB_DamageTypes orderby d.DamageOrder select d).ToList();
public static List<HUB_DamageTypes> TList
{
get
{
return _list;
}
}
}
In the xaml file I add it like this:
<UserControl.Resources>
<me:DamageTypeList x:Key="DamageTypeList"/>
The xaml line creates an error (ONLY in design time, it runs pefectly at runtime):
Cannot create an instance of "DamageTypeList". C:\HUB\HUB\HubbCostOfferPage.xaml
I have found some suggestions to solve it by using:
if (!DesignerProperties.IsInDesignTool)
But how do I use it to solve my problem?
You can use the flag DesignerProperties.IsInDesignTool to prevent the DB creation and to use hardcoded entities in your list.
public class DamageTypeList
{
static Begbil2Entities _DB;
public static List<HUB_DamageTypes> _list;
public static Begbil2Entities DB
{
get
{
if(_DB == null && !DesignerProperties.IsInDesignTool)
_DB = new Begbil2Entities();
return _DB;
}
}
public static List<HUB_DamageTypes> TList
{
get
{
if(_list == null)
{
if(!DesignerProperties.IsInDesignTool)
_list = (from d in DB.HUB_DamageTypes orderby d.DamageOrder select d).ToList();
else
_list = new List<HUB_DamageTypes>(){
// Initialize it with hardcoded values
};
}
return _list;
}
}
}
Before doing that, tough, I would investigate a little further what is the cause of the design-time exception, as #fhlamarche suggested. You can try to debug the design time execution, is not that hard. See this link.
The designer attempts to call the default constructor but your class doesn't have one.
You just need to add a private or internal default constructor to your class.

Looking for work-around for inability of DataGridView control to bind to hierarchical (OO) data

It would seem that the DataGridView control can only bind to data sources that are flat (all the Properties are primative types). My data is hierarchal. For example:
interface INestedObj
{
string Prop3 { get; }
}
interface IParentObj
{
public string Prop1 { get; }
public string Prop2 { get; }
public INestedObj NestedObj { get; }
}
Given this, how does one bind to an object implementing IParentObj? Eventually you are faced with having to do something like this:
grid.Columns["prop1Col"].DataPropertyName = "Prop1";
grid.Columns["prop2Col"].DataPropertyName = "Prop2";
grid.Columns["prop3Col"].DataPropertyName = "How to display Prop3?";
grid.Columns["prop3Col"].DataPropertyName = "NestedObj.Prop3"; // does not work
I am looking for advice and/or work-arounds.
TIA
You can expose properties from INestedObj for binding, but the solution is very messy.To give some background, all WinForms controls which support databinding use TypeDescriptor to determine which properties exist on the objects they're binding to. Through TypeDescriptionProvider and CustomTypeDescriptor, you can override the default behaviour and thusly add/hide properties - in this case, hiding the NestedObj property and replacing it with all of the properties on the nested type.
The technique i'm going to show has 2 (big-ish) caveats:
Since you're working with interfaces (and not concrete classes), you have to add the custom type descriptor at runtime.
The custom type descriptor needs to be able to create a concrete instance of IParentObj, therefore it must know one such class which has a default constructor.
(Please excuse the lengthy code)
First, you need a way of wrapping a PropertyDescriptor from the nested type so that it can be accessed from the parent type:
public class InnerPropertyDescriptor : PropertyDescriptor {
private PropertyDescriptor innerDescriptor;
public InnerPropertyDescriptor(PropertyDescriptor owner,
PropertyDescriptor innerDescriptor, Attribute[] attributes)
: base(owner.Name + "." + innerDescriptor.Name, attributes) {
this.innerDescriptor = innerDescriptor;
}
public override bool CanResetValue(object component) {
return innerDescriptor.CanResetValue(((IParentObj)component).NestedObj);
}
public override Type ComponentType {
get { return innerDescriptor.ComponentType; }
}
public override object GetValue(object component) {
return innerDescriptor.GetValue(((IParentObj)component).NestedObj);
}
public override bool IsReadOnly {
get { return innerDescriptor.IsReadOnly; }
}
public override Type PropertyType {
get { return innerDescriptor.PropertyType; }
}
public override void ResetValue(object component) {
innerDescriptor.ResetValue(((IParentObj)component).NestedObj);
}
public override void SetValue(object component, object value) {
innerDescriptor.SetValue(((IParentObj)component).NestedObj, value);
}
public override bool ShouldSerializeValue(object component) {
return innerDescriptor.ShouldSerializeValue(
((IParentObj)component).NestedObj
);
}
}
Then you need to write a custom type descriptor that exposes the properties from the nested type:
public class ParentObjDescriptor : CustomTypeDescriptor {
public override PropertyDescriptorCollection GetProperties(
Attribute[] attributes) {
PropertyDescriptorCollection properties
= new PropertyDescriptorCollection(null);
foreach (PropertyDescriptor outer in TypeDescriptor.GetProperties(
new ParentObj() /* concrete implementation of IParentObj */,
attributes, true)) {
if (outer.PropertyType == typeof(INestedObj)) {
foreach (PropertyDescriptor inner in TypeDescriptor.GetProperties(
typeof(INestedObj))) {
properties.Add(new InnerPropertyDescriptor(outer,
inner, attributes));
}
}
else {
properties.Add(outer);
}
}
return properties;
}
}
...and then you need a way of exposing the descriptor from above:
public class ParentObjDescriptionProvider : TypeDescriptionProvider {
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance) {
return new ParentObjDescriptor();
}
}
Finally, at run-time (before you bind to the DataGridView), you must associate the type description provider with the IParentObj interface. You can't do this at compile-time because TypeDescriptionProviderAttribute can't be placed on interfaces...
TypeDescriptor.AddProvider(new ParentObjDescriptionProvider(), typeof(IParentObj));
I tested this by binding a DataGridView to an IParentObj[] and, low and behold, it creates columns for Prop1, Prop2 and NestedObj.Prop3.
You have to ask yourself, though... is it really worth all that effort?
Here is a simple solution that came to me at the end of a long day.
I used a Linq query and projection to create an anonymous type that displays the proper information in the DataGridView.
var query = from pt in parentObjCollection
select new {Prop1=pt.Prop1, Prop2=pt.Prop2, NestedObj.Prop3=pt.NestedObj.Prop3};
I had to supply the proper value (NestedObj.Prop3) to the DataPropertyName property to get the value to display in the grid.
When I have more time I am going to try and implement Bradley's solution.
You could probably add an unbound column for "NestedObj.Prop3" and manually handle its value. To get the column populated, handle the CellFormatting event of the DataGridView, get the DataBoundItem from the current row and get the Prop3 from that. To update the data source, handle the CellValidated event to update the DataBoundItem.
There may be more appropriate events to use than the ones I mentioned, but you get the idea.
The easiest way I found is to create a Self property. See this solution:
Databinding a combobox column to a datagridview per row (not the entire column)

Resources