import on a overrided attribute

Jun 7, 2010 at 3:56 PM

well there is something that mess me up,

in the sample below i have done a override on property which my base class already import it, an import it with an alternative export! but it always import the default one!

here is the sample:

this is my IBase interface with two implementation:

public interface IBase
    {
        string Name { get; }    
    }

    [Export("Base2", typeof(IBase))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class Base2 : IBase
    {
        public string Name
        {
            get { return "Base2"; }
        }
    }

    [Export("Default", typeof(IBase))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class Base : IBase
    {
        public string Name
        {
            get { return "Default"; }
        }
    }

then i have a composition in my entity base class with have import the default IBase

public class EntityBase : IEntity, IDisposable
    {
        public virtual void Dispose()
        {
            Guid = "";
            ThreadId = 0;
        }
        [Import("Default", typeof(IBase))]
        public virtual IBase Temp { get; set; }

        public string Guid { get; set; }
        public int ThreadId { get; set; }

    }

and then i inherit from the Entity base and override the Temp property, and cover it with another Import attribute which is for importing "Base2"

[Export(typeof(Course))]
    public class Course : EntityBase
    {

        public string Name { get; set; }
        public int Hour { get; set; }

        [Import("Base2", typeof(IBase))]
        public override IBase Temp { get; set; }

        public override string ToString()
        {
            return Name + " - " + Hour;
            //return base.ToString();
        }

        public override void Dispose()
        {
            Name = "";
            Hour = 0;
            base.Dispose();
        }
    }



what i got when import the "course" now? THE DEFAULT one not the BASE2 one!!!!is there any solution? did i miss something?

Developer
Jun 7, 2010 at 5:03 PM

This is not a supported scenario. In fact what you are likely getting here is Temp having 2 imports and getting set twice. We do not support overriding member imports. If you really want to import something different in the derived class then create another property.

Jun 7, 2010 at 5:31 PM

It's really bad answer. I think that MEF must be more robust to given scenarios due to it,s position in System.ComponentModel. Its really simple scenario.

Is there any hope that you resolve this in future releases?

Thank you.

 

Jun 8, 2010 at 6:28 AM
Edited Jun 8, 2010 at 6:51 AM

well i think  solved the senario with some changes on mef source code(thanks to open source idea)

only thing that i did to solve the problem was to reverse the chain of looking for property in type and base types

the changes are in "AttributedPartCreationInfo" class inside "System.ComponentModel.Composition.AttributedModel" namespace

I changed the << private IEnumerable<MemberInfo> GetImportMembers(Type type) >> method.

here is the unchanged  code:

 

private IEnumerable<MemberInfo> GetImportMembers(Type type)
{
	if (type.IsAbstract)
	{
		yield break;
	}

	foreach (MemberInfo member in GetDeclaredOnlyImportMembers(type))
	{
		yield return member;
	}

	// Walk up the type chain until you hit object.
	if (type.BaseType != null)
	{
		Type baseType = type.BaseType;

		// Stopping at object instead of null to help with performance. It is a noticable performance
		// gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.
		// We also need the null check in case we're passed a type that doesn't live in the runtime context.
		while (baseType != null && baseType != CompositionServices.ObjectType)
		{
			foreach (MemberInfo member in GetDeclaredOnlyImportMembers(baseType))
			{
				yield return member;
			}
			baseType = baseType.BaseType;
		}
	}
}

and this my CHANGED code:

private IEnumerable<MemberInfo> GetImportMembers(Type type)
{
    if (type.IsAbstract)
    {
        yield break;
    }
    var typeStack = new Stack<Type>();
    typeStack.Push(type);
    if (type.BaseType != null)
    {
        Type baseType = type.BaseType;
        typeStack.Push(baseType);
        while (baseType != null && baseType != CompositionServices.ObjectType)
        {
            baseType = baseType.BaseType;
            typeStack.Push(baseType);
        }
    }
    while (typeStack.Count>0)
    {
        var checkType = typeStack.Pop();
        foreach (MemberInfo member in GetDeclaredOnlyImportMembers(checkType))
        {
            yield return member;
        }
    }
}

well it works FINE in my scenario!!! yohoooo

Developer
Jun 8, 2010 at 3:28 PM

Be very careful about taking a dependency on your code change, while it may work in your very specific case it will not interop completely with what ships in .Net 4.0. 

Lets take a step back and figure out the true scenario you are trying to achieve with this approach. What scenario are you trying to solve?

In general it is a bad idea to change a import on a base type because that base type has explicitly stated they need that dependency to function and now you are potentially breaking that contract. 

Jun 8, 2010 at 6:11 PM
Edited Jun 8, 2010 at 6:13 PM

well for some clarifying I have tried mef in about 10 complicated scenarios(until now)(although not that much complicated!!!) and hopefully i only failed in one(thanks to mef team)! but u mentioned a good one! i need the "base"(Default) IBase object that imported in EntityBase!

well i did Export the EntityBase by another contract name, what i got was on the exported object is what i really expected!

In the EntityBase imported item, I got the "default" implementation of IBase as the way it should be.(thanks to mef team)

more over, i got the scenario and the solution in codepaste(links below)

 

scenario: http://codepaste.net/ca3epq

Solution : http://codepaste.net/kt5oq5

 

tomorrow I'm going to have a performance test for my new implementation one too!

BTW: by the sentence "Be very careful about taking a dependency on your code change, while it may work in your very specific case it will not interop completely with what ships in .Net 4.0." you mean that it's not gonna fix in mef future releases or even patched? KIDDING me yeah? it's the basic for a plug-in systems which has a default implementation on the core, and the others(thanks to LOST) could implement their own implementation over it!

Developer
Jun 9, 2010 at 3:41 AM

The MEF team made an explicit design decision to not support overriding properties with ImportAttributes because it was a very complex problem to solve and rationalize correctly in all cases. We also didn't see any common scenarios for it's use so we decided to not support it and unless we have a compelling reason we will likely not add support for it in the future. 

The solution you provided while it may appear to solve your specific situation it doesn't solve the general issue. For example I bet if you put a breakpoint in your property setter it will get hit twice once for "Default" and once for "Base2", it just happens that the "Base2" was the last to be called so it looks like it does what you want. 

Jun 9, 2010 at 7:51 AM

I think that for a greate company and each of its partners, customers values are very important. we send you your fault and there isnt any item in project description that explain your desicion about overriding properties. we want that your product have greate impact in .Net world. I think that for having greate product you must hear more from your client. ( you can see his approch form greate communities such as spring.net )

Thank you.

Jun 9, 2010 at 9:10 AM

well I THINK that what we are talking about is something that implemented in .Net framework which is it's position in System.ComponentModel!! so supporting models and scenarios must be default strategy. more than that, it's not my fault calling it twice(I didn't check it yet, it's under test). and i think i can resolve this issue in mef two if needed!

well if make a project open source, it means that we are interested in comments AND scenarios that may challenge our code! if won't like this, why we make it open? if somebody ask for something, it's not the best way to threat others as the way you did! (busted! I got u red handed). well if there is a problem with double calling a method, I think that it's not my fault! it's your team code, not mine!

even though, I think mef code would have the same bug too! but unfortunately the result is wrong because the wrong injection made last! (in my code, the correct injection made last)

which of them are correct, NONE! so what should we do? hit the code, and get the best answer which is "SUPPORT EVERY SCENARIOS and DO IT IN THE Best way! well I will correct your code, in the way that it result one call of the setter! and I'll send you, hope your team, make it available for every one around.

 

thanks

Jun 9, 2010 at 11:33 AM
Edited Jun 9, 2010 at 11:44 AM

the issue u mentioned(2 calls for property setter) is SOLVED now!

 

private IEnumerable<MemberInfo> GetImportMembers(Type type)
{
var local = new HashSet<string>();
if (type.IsAbstract)
{
yield break;
}

foreach (var member in GetDeclaredOnlyImportMembers(type))
{
var info = member;
local.Add(info.Name);
yield return member;
}
if (type.BaseType != null)
{
Type baseType = type.BaseType;
while (baseType != null && baseType != CompositionServices.ObjectType)
{
foreach (var member in GetDeclaredOnlyImportMembers(baseType))
{
var info = member;
if (local.Contains(info.Name))
continue;
local.Add(info.Name);
yield return member;
}
baseType = baseType.BaseType;
}
}
}

 

here is the code: http://codepaste.net/ieffxu

only one call if override or not, and the correct input. is there any performance issue? the answer is NO! because it's only called once in composition or in recompose!

any other idea?

 

Jun 9, 2010 at 3:08 PM
Edited Jun 9, 2010 at 3:29 PM

Hi Sarv

Thanks for the feedback. We are on Codeplex as you mentioned so that we can the kind of information you are sending us. As Wes mentioned, there are some fundamental design goals we had in mind when building MEF. We specifically did punt on overriding imports just like we punted overriding exported members. One of the reasons was it was very costly to determine at runtime which members were overridden. Doing that determination affected perf wise all of our customers even if they weren't overriding. Features like that are just too expensive based on the across the board impact.

That being said, we are listening. But listening to what customers say doesn't always mean we will just do it. It means we will consider it against all of our other priorities, and we will.

Keep the feedback coming

Regards

Glenn