Castle Windsor: conditional component registration when scanning an assembly - castle-windsor

Am using Castle Windsor to register multiple components that share one common interface at the top chain, using the following:
container.Register(
Types.FromAssemblyNamed("MyProject.MyAssembly")
.BasedOn<IService>()
.WithServiceAllInterfaces()
.Configure(c => c.LifeStyle.HybridPerWebRequestTransient())
)
Among all the components inside MyAssembly, I have the following:
public interface IHandler : IService
public class MessageHandler : IHandler
public class CachedMessageHandler : IHandler
During the registration above, is there a way to pick one of the two components (MessageHandler or CachedMessageHandler) based on some conditional value?

Yes there is.
.BasedOn<IService>()
is shorthand for
.Pick().If(t => typeof(IService).IsAssignableFrom(t))
Knowing that, you have the possibility to do powerful things. Just to give you an idea what you can do:
1.
Possibly this is what you wanted. Pick one of the two components based on some conditional value:
var shouldUseCache = true; //get this from configuration
//later in container.Register
.Pick().If(t =>
{
if (shouldUseCache)
{
return typeof (IService).IsAssignableFrom(t) && typeof (CachedMessageHandler) == t;
}
return typeof (IService).IsAssignableFrom(t) && typeof (MessageHandler) == t;
})
Be warned though:
once the component is registered, it will be hard/impossible/antipattern to remove that registration from the container
if you make "conditional registration", it may be tricky for developers to debug injections later. Depending on your situation you may want to create HandlerProvider which will have explicit methods like: .GetCached or .GetTransient
2.
You can select all types which implement IService but exclude those which also implement IHandler:
.Pick().If(t => typeof(IService).IsAssignableFrom(t)
&& !typeof(IHandler).IsAssignableFrom(t))
3.
You can do naming conventions, for example only register all types which implement IHandler and ends with SuperHandler:
.Pick().If(t => typeof(IHandler).IsAssignableFrom(t)
&& t.Name.EndsWith("SuperHandler"))

Related

How to map database function to Entity Framework Core property

my problem is that i can't map database function to use it in object property like computed column but for different tables with relation. My relation is SizeItem to Review (one to many)
I have IEntityTypeConfiguration
internal class SizeItemConfiguration : IEntityTypeConfiguration<SizeItem>
{
public const string TableName = "size_items";
public void Configure(EntityTypeBuilder<SizeItem> builder)
{
builder.ToTable(TableName);
builder.Property(p => p.Id)
.UseHiLo("size_item_hilo");
}
}
And want to compute an average rating from Review entities related to a SizeItem.
I tried to find examples to use database functions and EF Core functions but nothing works.
How can I get computed SizeItem.AverageRating for every sql command? May be code will looks like:
builder.Property(p => Functions.Average(p.Reviews.Select(x => x.Rating)));
User-defined function mapping doesn't work for me because I'm using Repository pattern.

Binding in code to property with operation without special ValueConverter

In axml I can Bind like this
local:MvxBind="Visibility Status==0, Converter=Visibility"
but when I'm trying to do it in code:
set.Bind(_imgStatus).For(c => c.Hidden).To(vm => vm.Status == 0).WithConversion("Visibility");
or
set.Bind(_imgStatus).For(c => c.Hidden).To(vm => vm.Status.Equals(0)).WithConversion("Visibility");
I get error
Property expression must be of the form 'x => x.SomeProperty.SomeOtherProperty' or 'x => x.SomeCollection[0].Property' (System.ArgumentException)
I'm aware that I can write my own ValueConverter, but I'm interested if there's a way to surpass that, since there's obviously a way to write it in axml.
MvvmCross doesn't include expressions like To(vm => vm.Status == 0) in the fluent syntax. This is partly because they are hard to code... and partly because their coding often requires runtime compilation (which iOS won't allow)
The only way around this is to use the string format in iOS - e.g. like in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-35-Tibet/BindMe.Touch/Views/FirstView.cs#L63
So your sample might become something like:
set.Bind(_imgStatus).For(c => c.Hidden).To("Visibility(Status == 0)");
or even (in this case):
set.Bind(_imgStatus).For(c => c.Hidden).To("Status != 0");
There are also some described fluent methods to allow you to pass in the entire binding string - e.g. something like:
set.Bind(_imgStatus).FullyDescribed("Hidden Visibility(Status != 0)");

WEB API return all objects instead of one

I want to expose a list of services from my DB or just return one service detail via Web API with my EF DBmodel . I used VS2012 Web API scaffolding, quite easy so far and it works and return the list of services in JSON when I hit the URL(.../api/Services). The problem is that when I want to obtain just one service URL(.../api/Services/1), I still obtain the full list of all services although when I trace it seems to return only a count of 1 object.
What happening here?
Here are the 2 controller actions.
ps: I also tried using a .Where() instead of .Find() but the result is the same in both cases.
// GET api/Services
public IEnumerable<service> Getservices()
{
var services = db.services.Include(s => s.Category).Include(s => s.Country).Include(s => s.StateProvince).Include(s => s.Territory);
return services.AsEnumerable();
}
// GET api/Services/5
public service Getservice(int id)
{
service service = db.services.Find(id);
if (service == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return service;
}
Try handling it as: var service = db.services.Single(s => s.Id == id)
First, check if your database has a single item for your query or not.
If you are querying by primary key then
db.services.SingleOrDefault(s => s.Id == id) should do.
You will need to handle the exception if you are querying on some field which may give back more than one result.
The variant of filtering (Single, SingleOrDefault, First, FirstOrDefault) that you use will depend upon the exact semantics of the code.

Yii model: Dynamic table relations

Table.linkedIndex is related to LinkedIndex.ID. The value of the field LinkedIndex.TableName is either Linked1 or Linked2 and defines which of these tables is related to a row in Table.
Now i want to make a dynamical link with Yii models so that i can easily get from a Table row to the corresponding Linked1 or Linked2 row:
Table.linkedID = [LinkedIndex.TableName].ID
Example
Table values:
LinkedIndex values:
Now I should get the row from Linked2 where ID=2:
$model = Table::model()->findByPk(0);
$row = $model->linked;
Model
In the model Table, I tried to make the relation to the table with the name of the value of linkedIndex.TableName:
public function relations()
{
return array(
'linkedIndex' => array(self::HAS_ONE, 'LinkedIndex', array('ID' => 'linkedIndex')),
'linked' => array(
self::HAS_ONE,
'linkedIndex.TableName',
array('ID' => 'linkedID'),
)
)
}
But then I get the error:
include(linkedIndex.TableName.php) [function.include]: failed to open stream: No such file or directory
Is there any way to make a dynamic relation Table.linkedID -> [LinkedIndex.TableName].ID with Yii Models?
Per the Yii docs here:
http://www.yiiframework.com/doc/api/1.1/CActiveRecord#relations-detail
I'd suggest using self::HAS_ONE instead (unless there can be multiple rows in LinkedIndex with the same ID - although from the looks of above, I doubt that's the case).
You can link tables together that have different keys by following the schema:
foreign_key => primary_key
In case you need to specify custom PK->FK association you can define it as array('fk'=>'pk'). For composite keys it will be array('fk_c1'=>'pk_с1','fk_c2'=>'pk_c2').
so in your case:
public function relations(){
return array(
'linkedIndex' => array(self::HAS_ONE, 'LinkedIndex', array('ID' => 'linkedIndex')),
);
}
where LinkedIndex is the class name for the LinkedIndex model (relative to your Table model - i.e. same folder. You could change that, of course) and array('ID' => 'linkedIndex') specifies the relationship as LinkedIndex.ID = Table.linkedIndex.
Edit
Looking at your updated example, I think you're misunderstanding how the relations function works. You're getting the error
include(linkedIndex.TableName.php) [function.include]: failed to open stream: No such file or directory
because you're trying to create another relation here:
'linked' => array(
self::BELONGS_TO,
'linkedIndex.TableName',
array('ID' => 'linkedID'),
)
This part: linkedIndex.TableName refers to a new model class linkedIndex.TableName, so Yii attempts to load that class' file linkedIndex.TableName.php and throws an error since it doesn't exist.
I think what you're looking for is to be able to access the value TableName within the table LinkedIndex, correct? If so, that's accessible from within the Table model via:
$this->linkedIndex->TableName
This is made possible by the relation we set up above. $this refers to the Table model, linkedIndex refers to the LinkedIndex relation we made above, and TableName is an attribute of that LinkedIndex model.
Edit 2
Per your comments, it looks like you're trying to make a more complex relationship. I'll be honest that this isn't really the way you should be using linking tables (ideally you should have a linking table between two tables, not a linking table that says which 3rd table to link to) but I'll try and answer your question as best as possible within Yii.
Ideally, this relationship should be made from within the LinkedIndex model, since that's where the relationship lies.
Since you're using the table name as the linking factor, you'll need to create a way to dynamically pass in the table you want to use after the record is found.
You can use the LinkedIndex model's afterFind function to create the secondary link after the model is created within Yii, and instantiate the new linked model there.
Something like this for your LinkedIndex model:
class LinkedIndex extends CActiveRecord{
public $linked;
public static function model($className = __CLASS__){
return parent::model($className);
}
public function tableName(){
return 'LinkedIndex';
}
public function afterFind(){
$this->linked = new Linked($this->TableName);
parent::afterFind();
}
//...etc.
}
The afterFind instantiates a new Linked model, and passes in the table name to use. That allows us to do something like this from within the Linked model:
class Linked extends CActiveRecord{
private $table_name;
public function __construct($table_name){
$this->table_name = $table_name;
}
public static function model($className = __CLASS__){
return parent::model($className);
}
public function tableName(){
return $this->table_name;
}
//...etc.
}
which is how we dynamically create a class with interchangeable table names. Of course, this fails of the classes need to have separate operations done per-method, but you could check what the table_name is and act accordingly (that's pretty janky, but would work).
All of this would result in being to access a property of the linked table via (from within the Table model):
$this->linkedIndex->linked->foo;
Because the value of LinkedIndex.TableName and Table.linkedID is needed to get the values, I moved the afterFind, suggested by M Sost, directly into the Table-Class and changed its content accordingly. No more need for a virtual model.
class Table extends CActiveRecord {
public $linked; // Needs to be public, to be accessible
// ...etc.
public function afterFind() {
$model = new $this->linkedIndex->TableName;
$this->linked = $model::model()->findByPk( $this->linkedID );
parent::afterFind();
}
// ...
}
Now I get the row from Linked2 where ID=2:
$model = Table::model()->findByPk(0);
$row = $model->linked;

enumerate entity children

I need to create a method that will take Linq-to-sql entity and return a list of all it's children(only 1st generation) entitysets, without any data that entitysets contain, just names. Is there any way I can do that? Thanks
Well, if I understand your question correctly, you can inspect the meta-model for a data context instance. The meta-model describes the tables, columns and associations in your model. Basically you want to look at associations on a table where the association is 1-to-many.
This doesn't involve retrieving any data, as you are not actually working with entity instances, just the information that describes them.
This code should do it:
public static string[] GetChildEntities<T>(DataContext context, T entity)
{
var mapping = context.Mapping.GetTable(typeof(T));
return mapping.RowType.Associations.Where(a => a.IsMany)
.Select(a => a.ThisMember.Name).ToArray();
}
This will return the names of any properties that expose the EntitySet instances for the given parent entity.
EDIT
This code finds the first 1->* association between the parent and child entities based on the meta-model, retrieves the value of the EntitySet property on the parent entity, and adds the child entity to that set. This should work for most basic LINQ to SQL implementations.
public static void AddChild<P, C>(DataContext context, P parent, C child)
where P : class
where C : class
{
var parentMapping = context.Mapping.GetTable(typeof(P));
var childAssociation =
parentMapping.RowType.Associations
.Where(a => a.IsMany && a.OtherType.Type == typeof(C))
.FirstOrDefault();
if (childAssociation != null)
{
var entitySet = (EntitySet<C>) childAssociation.ThisMember
.MemberAccessor
.GetBoxedValue(parent);
entitySet.Add(child);
}
}