TFS Custom Build Template - Get the team project collection without the Uri - c#

Is there anyway to get the collection URI from IBuildDefinition or other related 'services'.
I am trying to avoid having to supply the URI to the collection in the build template as a custom parameter. I am looking for a way to retrieve it programmatically from (in this instance) within UITypeEditor custom class.
Is there a way to query for this without resorting to hardcoding? It seems to me the build process itself (definition, controllers, agents, etc) knows which collection they are dealing with but how can I find out?
UPDATE: Here is sample code when you are inheriting from UITypeEditor. Then you just access the TeamProjectCollection property of the VersionControlService:
public class Editor : UITypeEditor
{
public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (provider != null)
{
IWindowsFormsEditorService service = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (service != null)
{
VersionControlServer vcs = provider.GetService(typeof(VersionControlServer)) as VersionControlServer;
// Do what you need to do with it here
}
}
return value;
}
public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}

You can get the TfsTeamProjectProjection object from IBuildServer:
http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.build.client.ibuildserver.teamprojectcollection.aspx
Then get you Uri from this object.

Inside of the override for UITypeEditor.EditValue the relevant line of code to obtain the TeamProjectCollection is
VersionControlServer vcs = provider.GetService(typeof(VersionControlServer)) as VersionControlServer;
And then it is in the property
vcs.TeamProjectCollection

Related

Entity Framework Core - Disable Model caching , call onModelCreating() for each instance dcontext

Documentation Says : The model for that context is cached and is for all further instances of the context in the app domain. This caching can be disabled by setting the ModelCaching property on the given ModelBuidler
But i can't find way to do it. I have to disable caching because I am adding Model at runtime and loading all the models from assembly and creating database.
I found this link which says one way of achieving this is using DBModelBuilding - adding model mannually to context but it is for Entity Framework, Not helped for EF Core.
Entity Framework 6. Disable ModelCaching
I hope some one has solution for this.
Thank you
Once a model is successfully created, EF Core will cache it forever, unless you implement a cache manager that is able to tell whether a model is equivalent to another, and therefore it can be cached or not.
The entry point is to implement the cache manager:
internal sealed class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create([NotNull] DbContext context)
{
return GetKey(context);
}
}
The GetKey method which you have to write must return an object that will be used as key. This method should inspect the provided context and return the same key when the models are the same, and something different when they are not. More on IModelCacheKeyFactory Interface.
I understand, this might not be clear (and it wasn't for me either), so I write a full example of what I have in production.
A Working Example
My target is to use the same context for different schemas. What we need to do is
create a new context option
implement the logic in the context
create the cache key factory
make the extension method to specify the schema
call the extension method on the db context
1. Create a new context option
Here there is a boilerplate containing _schemaName only. The boilerplate is necessary as the extension option is immutable by design and we need to preserve the contract.
internal class MySchemaOptionsExtension : IDbContextOptionsExtension
{
private DbContextOptionsExtensionInfo? _info;
private string _schemaName = string.Empty;
public MySchemaOptionsExtension()
{
}
protected MySchemaOptionsExtension(MySchemaOptionsExtension copyFrom)
{
_schemaName = copyFrom._schemaName;
}
public virtual DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);
public virtual string SchemaName => _schemaName;
public virtual void ApplyServices(IServiceCollection services)
{
// not used
}
public virtual void Validate(IDbContextOptions options)
{
// always ok
}
public virtual MySchemaOptionsExtension WithSchemaName(string schemaName)
{
var clone = Clone();
clone._schemaName = schemaName;
return clone;
}
protected virtual MySchemaOptionsExtension Clone() => new(this);
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
private const long ExtensionHashCode = 741; // this value has chosen has nobody else is using it
private string? _logFragment;
public ExtensionInfo(IDbContextOptionsExtension extension) : base(extension)
{
}
private new MySchemaOptionsExtension Extension => (MySchemaOptionsExtension)base.Extension;
public override bool IsDatabaseProvider => false;
public override string LogFragment => _logFragment ??= $"using schema {Extension.SchemaName}";
public override long GetServiceProviderHashCode() => ExtensionHashCode;
public override void PopulateDebugInfo([NotNull] IDictionary<string, string> debugInfo)
{
debugInfo["MySchema:" + nameof(DbContextOptionsBuilderExtensions.UseMySchema)] = (ExtensionHashCode).ToString(CultureInfo.InvariantCulture);
}
}
}
2. The logic in the context
Here we force the schema to all the real entities. The schema is obtained by the option attached to the context
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var options = this.GetService<IDbContextOptions>().FindExtension<MySchemaOptionsExtension>();
if (options == null)
{
// nothing to apply, this is a supported scenario.
return;
}
var schema = options.SchemaName;
foreach (var item in modelBuilder.Model.GetEntityTypes())
{
if (item.ClrType != null)
item.SetSchema(schema);
}
}
3. Create the cache key factory
Here we need to the create the cache factory which will tel EF Core that it can cache all the models on the same context, i.e. all the contexts with the same schema will use the same model:
internal sealed class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create([NotNull] DbContext context)
{
const string defaultSchema = "dbo";
var extension = context.GetService<IDbContextOptions>().FindExtension<MySchemaOptionsExtension>();
string schema;
if (extension == null)
schema = defaultSchema;
else
schema = extension.SchemaName;
if (string.IsNullOrWhiteSpace(schema))
schema = defaultSchema;
// ** this is the magic **
return (context.GetType(), schema.ToUpperInvariant());
}
}
The magic is here is in this line
return (context.GetType(), schema.ToUpperInvariant());
that we return a tuple with the type of our context and the schema. The hash of a tuple combines the hash of each entry, therefore the type and schema name are the logical discriminator here. When they match, the model is reused; when they do not, a new model is created and then cached.
4. Make the extension method
The extension method simply hides the addition of the option and the replacement of the cache service.
public static DbContextOptionsBuilder UseMySchema(this DbContextOptionsBuilder optionsBuilder, string schemaName)
{
if (optionsBuilder == null)
throw new ArgumentNullException(nameof(optionsBuilder));
if (string.IsNullOrEmpty(schemaName))
throw new ArgumentNullException(nameof(schemaName));
var extension = optionsBuilder.Options.FindExtension<MySchemaOptionsExtension>() ?? new MySchemaOptionsExtension();
extension = extension.WithSchemaName(schemaName);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
optionsBuilder.ReplaceService<IModelCacheKeyFactory, MyModelCacheKeyFactory>();
return optionsBuilder;
}
In particular, the following line applies our cache manager:
optionsBuilder.ReplaceService<IModelCacheKeyFactory, MyModelCacheKeyFactory>();
5. Call the extension method
You can manually create the context as follows:
var options = new DbContextOptionsBuilder<DataContext>();
options.UseMySchema("schema1")
options.UseSqlServer("connection string omitted");
var context = new DataContext(options.Options)
Alternatively, you can use IDbContextFactory with dependency injection. More on IDbContextFactory Interface.
You'll need to change the cache key to properly represent the model that you are building/make it distinct.
Implement IDbModelCacheKeyProvider Interface on derived DbContext. Check this out
https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.infrastructure.idbmodelcachekeyprovider?redirectedfrom=MSDN&view=entity-framework-6.2.0
Build the model outside the DbContext and then provide it in the options.

How to pass WebAPI Controller data such that Posted Model has access at Binding/Deserialization or Validation Time

Consider this WebAPI Controller where some meta data is retrieved at instantiation.
public class MyController : ApiController
{
private readonly MetaData _metaData;
public MyController(IService service) // IService is injected via Unity Container DI
{
_metaData = service.GetMetaData();
}
[ValidateModel]
public IHttpActionResult Post([FromBody] PostModel model)
{
// do something
return new ResponseMessageResult(null);
}
}
And suppose the Post action's PostModel class is defined as below:
public class PostModel : IValidatableObject
{
ComplexObject Data { get; set; }
[OnDeserialized]
internal void OnDeserialized(StreamingContext context)
{
// do something, using _metaData
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// validate, using _metaData
}
}
I want my PostModel to have access to the MyController _metaData object ideally by the time OnDeserialized() is called, but I would settle for at least by the time Validate() is executed.
I have considered:
ActionFilter- impossible, as model binding/validation occurs before
ActionFilter execution
Custom ValueProvider/ModelBinder- I thought perhaps these together would allow me to define the StreamingContext used in OnDeserialized() and set its Context property to _metaData. But even if this is possible, I could not figure out a way to do it correctly.
MessageHandler- I think a per-route message handler is not ideal, but I could use dependency injection to retrieve the _metaData and then...append this object data to the posted data? This feels hacky and I'm not very enthusiastic about this type of solution.
Dependency Injection- I could give PostModel a reference to the service during its own instantiation, however the Controller and the Model live in separate projects, with the Model project not having a Unity Container dependency at present. Instead, it relies on the Controller project and other referencing projects to use their own DI to pass any required data.
Perhaps there is some kind of action-level Attribute I'm not aware of that would benefit me here, or maybe one of the above was on the right track? I have to believe there is a way to pass/access dynamic server-side data during deserialization or at least by validation of the Model that I'm just not aware of or thinking of the proper way to do.
Two additional notes:
I am using Asp.Net WebAPI and do not have access to Core WebAPI attributes, filters, etc.
The Post action is in ProjectA but PostModel is in ProjectB. ProjectA references projectB, but the inverse is not true. ProjectB does not presently have a dependency on Unity Container DI.
Thanks in advance for your help!
You can use an IModelBinder to both deserialize and validate your PostModel. Doing it this way, you'll have access to your Controller instance during the deserialization. This code is fairly rough, but you should get the idea.
IModelBinder Implementation
public class PostDataModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(PostDataModel))
{
return false;
}
try
{
// Get the content of the request, deserialize into your model object
Task<string> bodyTask = actionContext.Request.Content.ReadAsStringAsync();
PostDataModel vm = JsonConvert.DeserializeObject<PostDataModel>(bodyTask.Result);
// Get an instance of your IService and get metadata
var service = (IService)actionContext.ControllerContext.Configuration.DependencyResolver.GetService(typeof(IService));
var metadata = service.GetMetadata();
// this is how you would get the Metadata directly from your controller as long as it was accessible
//var metadata = ((IMetadata)actionContext.ControllerContext.Controller).Metadata;
// do stuff with metadata to validate your object however you wish
vm.PopulateValidationErrors();
// return true if you were able to deserialize this object, false if you couldn't
return vm != null;
}
catch {
// do logging
return false;
}
}
}
Use IModelBinder globally by adding it to the Register method of WebApiConfig
config.BindParameter(typeof(PostDataModel), new PostDataModelBinder());

Retrieve Custom Binding Parameters in Provider Ninject

Hi I am using Ninject in a Xamarin MVVM project. What I am trying to do is to bind specific implementations based on a enum type:
var foo = new Ninject.Parameters.Parameter("type", VMType, true);
Kernel.Get<ICommonComponentVM>(foo);
and the provider:
public class ICommonComponentVMProvider : Provider<ICommonComponentVM>
{
protected override ICommonComponentVM CreateInstance(IContext context)
{
//return the implementation based on type
}
}
which is binded in the Kernel Module as:
public class CoreModule : NinjectModule
{
public override void Load()
{
Bind<ICommonComponentVM>().ToProvider<ICommonComponentVMProvider>();
}
}
How can I extract the custom parameter from the binding IContext?
Or is this the correct way to do this? The Ninject wiki lacks this info.
EDIT
I arrived at
var param = context.Parameters.Single((arg) => arg.Name == "type");
but accessing the value of the parameter with param.GetValue needs two arguments: IContext and ITarget. I have the context but what should I put as Target?
In the meantime it works with null:
var type = (CommonVMTypes)param.GetValue(context, null);
so it looks like this:
protected override ICommonComponentVM CreateInstance(IContext context)
{
var param = context.Parameters.Single((arg) => arg.Name == "type");
if (param == null){
return null;
}
var type = (CommonVMTypes)param.GetValue(context, null); //<-- Needs an Action ITarget
switch (type)
// ...
}
You can access the parameters by the property ICollection<IParameter> IContext.Parameters. You can find it by using context.Parameters.Single(x => x.Name == "type").
You could also subclass Parameter or implement IParameter for a custom parameter type with strongly typed information, p.Ex. ComponentVMTypeParameter and then select it using context.Parameters.OfType<ComponentVMTypeParameter>().Single().
Alternative approaches:
use conditional bindings (When(...) syntax, can check for parameters, too) instead of the providers. Does not require extending the provider.
use a factory instead of a provider.
using named bindings:
Bind<IFoo>().To<Foo1>().Named("Foo1")
IResolutionRoot.Get<IFoo>("Foo1");
However, in principle there's no need to use an IProvider. You could instead
However, if there's a limited amount of types I'd consider using an abstract factory instead

Autofac property injection into System.ComponentModel.DataAnnotations ValidationAttribute

I am not able to perform property injection into a custom data annotation validation attribute
public class CustomValidationAttribute : ValidationAttribute
{
public ILogger Logger { get; set; }
public CustomValidationAttribute(string keyPointer)
{ }
public override bool IsValid(object value)
{
// Implementation here
return true;
}
}
Now, on my MVC Application_Start method I have the following Autofac configuration:
// Autofac Ioc Container
var builder = new ContainerBuilder();
builder.RegisterType<Logger>().As<ILogger>().InstancePerHttpRequest();
builder.RegisterType<CustomValidationAttribute>()
.OnActivating(e =>
{
e.Instance.Logger = e.Context.Resolve<ILogger>();
});
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
I have also tried the autowiring capabilities:
builder.RegisterType<CustomValidationAttribute>().PropertiesAutowired();
I am guessing that the properties of an attribute on data annotations are resolved at compile time and are immune to runtime injection.
This methods works fine for MVC filter attributes but does not work for data annotation attributes.
Any help is really appreciated on alternate methods to make this work.
For reference, we had a problem with a ValidationAttribute which needed to do some database work using a Repository, which in turn used an Entity Framework DbContext.
Our problem was that the DbContext was being cached by the attribute. This led to the data it contained being stale which affected the result of the validation!
We fixed it by doing our Repository resolve inside an Autofac Lifetimescope declaration inside the IsValid method:
using Autofac;
...
public override bool IsValid(object value)
{
using (var lifetimeScope = MvcApplication.Container.BeginLifetimeScope())
{
var repo = lifetimeScope.Resolve<IMyRepo>();
// Do your validation checks which require the repo here
} // The repo will be released / disposed here
}
Just adding my solution here as I haven't found this solution documented for this problem anywhere else - perhaps it's just so obvious nobody else has been as dumb as me :)
You are correct in your analysis - Autofac has a mechanism to inject into Filter Attributes [which is achieved by not instantiating them as attributes and leans on facilities exposed MVC 3].
There is no such natural extension point applicable to Validation Attributes, and Autofac doesn't make any attempt to do so.
I ended up using a new validator and some reflection to set an instance of the property in the data annotation.
public class IocValidator : DataAnnotationsModelValidator<ValidationAttribute>
{
public IocValidator(ModelMetadata metadata, ControllerContext context,
ValidationAttribute attribute)
: base(metadata, context, attribute) { }
public override IEnumerable<ModelValidationResult> Validate(object container)
{
IEnumerable<PropertyInfo> props =
from p in Attribute.GetType().GetProperties()
where p.CanRead
&& p.CanWrite
&& (p.PropertyType.IsInterface || p.PropertyType.IsAbstract)
select p;
foreach (PropertyInfo prop in props)
{
var instance = IocHelper.Resolver.GetService(prop.PropertyType);
if (instance != null)
prop.SetValue(Attribute, instance, null);
}
return base.Validate(container);
}
}
Then in my Application_Start I registered my new validator adapter as such:-
DataAnnotationsModelValidatorProvider.RegisterDefaultAdapter(typeof(IocValidator));
There are definite performance implications on this approach and the dependence of the IocHelper in the Validator (ohh, the irony, dependency on the dependency injection container).
Any thoughts or better approaches are quite welcomed.
I solved it a bit differently (you're still wiring stuff via ValidationAttributes) in this answer, enabling one to write:
class MyModel
{
...
[Required, StringLength(42)]
[ValidatorService(typeof(MyDiDependentValidator), ErrorMessage = "It's simply unacceptable")]
public string MyProperty { get; set; }
....
}
public class MyDiDependentValidator : Validator<MyModel>
{
readonly IUnitOfWork _iLoveWrappingStuff;
public MyDiDependentValidator(IUnitOfWork iLoveWrappingStuff)
{
_iLoveWrappingStuff = iLoveWrappingStuff;
}
protected override bool IsValid(MyModel instance, object value)
{
var attempted = (string)value;
return _iLoveWrappingStuff.SaysCanHazCheez(instance, attempted);
}
}
With some helper classes (look over there), you wire it up e.g. in ASP.NET MVC like so in the Global.asax :-
DataAnnotationsModelValidatorProvider.RegisterAdapterFactory(
typeof(ValidatorServiceAttribute),
(metadata, context, attribute) =>
new DataAnnotationsModelValidatorEx(metadata, context, attribute, true));

Property Injection into an Action Filter

I'm trying to get Property Injection working on a Custom Action Filter Attribute. It is working as it is supposed to, however, I'd like to use DI on the Property itself. My filter looks like this
[AttributeUsage(AttributeTargets.Class)]
public sealed class HeaderFilterAttribute : ActionFilterAttribute
{
public IMarketService MarketService
{ get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var view = (ViewResultBase)filterContext.Result;
if (view != null)
{
BaseViewModel viewModel = view.ViewData.Model as BaseViewModel;
if (viewModel != null)
viewModel.Header = GetHeaderScript();
}
base.OnActionExecuted(filterContext);
}
private string GetHeaderScript()
{
//Use MarketService here and return header script
return "script";
}
}
This is how I'm configuring the property using StructureMap inside my BootStrapper class.
//HeaderFilterAttribute
IMarketRepository marketRepository = new SqlMarketRepository();
IMarketService marketService = new MarketService(marketRepository);
ObjectFactory.Container.Configure(r => r.ForConcreteType<HeaderFilterAttribute>().
Configure.WithProperty("MarketService").
EqualTo(marketService));
My problem is I do not have access to SqlMarketRepository since all my concrete types are injected via DI and I really don't want to use concrete types in my bootstrapper. So the ultimate question now is, how do I inject MarketService into the Filter attribute without resorting to the above? :)
In your ObjectFactory.Initialize() call, add the following line:
SetAllProperties(x => x.OfType<IMarketService>());
That will inject the configured IMarketService instance into any property of type IMarketService, on any object retrieved from the container.
I think you need a custom action invoker implementation that will resolve the filters. You can dig a Windsor sample out of my company's implementation (about 1/2 way down). There should be several more available online. I know I've seen some on this site.
PS. I noticed you're using a base view model to populate a header. I'd recommend using the ViewData[] collection with a static key instead of inheritance in your view model. :)

Categories