I have registered multiple instances of an Interface using castle Windsor in the following way:
ServiceLocatorHelper.Register(
Component.For<ISerializerAdapter>()
.ImplementedBy<SerializerAdapter>(),
Component.For<IDriver>()
.ImplementedBy<FileDriver>()
.Named(SerializationType.Binary.ToString()),
Component.For<IDriver>()
.ImplementedBy<XmlDriver>()
.Named(SerializationType.Xml.ToString()),
Component.For<IBroker>().ImplementedBy<Broker>()
);
The dependencies are in the following way:
+ IBroker
- IDriver
types
{
- FileDriver
- XmlDriver
}
- ISerializerAdapter
So in order to create a new object, this is the default constructor order:
IBroker broker = new Broker(new IDriver(new ISerializerAdapter));
The question is:
When I resolve a new IDriver using castle in the following way:
IBroker broker = container.Resolve<IBroker>();
IDriver driver = broker.Driver;
Assert.IsTrue(driver.GetType() == typeof(FileDriver));
The property IDriver is always of type FileDriver, while if I resolve the IDriver using the key it return the proper one:
IDriver fileDriver = container.Resolve<IDriver>(SerializationType.Binary.ToString());
Assert.IsTrue(fileDriver.GetType() == typeof(FileDriver));
IDriver fileDriver = container.Resolve<IDriver>(SerializationType.Xml.ToString());
Assert.IsTrue(fileDriver.GetType() == typeof(XmlDriver));
How can I say something like this?
IBroker broker = container.Resolve<IBroker>("Xml");
Assert.IsTrue(broker.Driver.GetType() == typeof(XmlDriver));
This is typical scenario to use Typed Factory Facility.
Just declare factory interface:
public interface IBrokerFactory {
IBroker GetXmlBroker();
IBroker GetBinaryBroker();
}
And register it like this:
Component.For<IBrokerFactory>().AsFactory(),
Component.For<IBroker>().
ImplementedBy<Broker>().
Named("BinaryBroker").
DependsOn(Dependency.OnComponent("BinaryDriver")),
Component.For<IBroker>().
ImplementedBy<Broker>().
Named("XmlBroker").
DependsOn(Dependency.OnComponent("XmlDriver")),
Component.For<IDriver>().ImplementedBy<FileDriver>().Named("BinaryDriver"),
Component.For<IDriver>().ImplementedBy<XmlDriver>().Named("XmlDriver"),
The GetXxx() method name should match with .Named("Xxx"), thats all. Usage:
var factory = container.Resolve<IBrokerFactory>();
IBroker broker = factory.GetXmlBroker();
Assert.IsTrue(broker.Driver.GetType() == typeof(XmlDriver));
Related
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"))
I'm making a clone action in sonata admin--following the recommendations of Sonata docs:
<?php // src/Acme/DemoBundle/Controller/CRUDController.php
namespace Acme\DemoBundle\Controller;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
class CRUDController extends Controller
{
public function cloneAction()
{
$id = $this->get('request')->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw new NotFoundHttpException(sprintf('unable to find the object with id : %s', $id));
}
$clonedObject = clone $object;
$clonedObject->setName($object->getName()." (Clone)");
$this->admin->create($clonedObject);
$this->addFlash('sonata_flash_success', 'Cloned successfully');
return new RedirectResponse($this->admin->generateUrl('list'));
}
}
after setting an id on the $clonedobject I get a DBAL exception. primary keys with same id not allowed--
I've tried setting a unique id
no id with the hope that auto increment in my schema would force ++
thanks for your help
Geert is right, setting the id to null is the way to go with Doctrine.
However, rather than having to implement a setId method in your object, you may as well override the __clone method as follows:
public function __clone()
{
parent::__clone();
$this->id = null;
$this->name .= " (Clone)";
}
See How to re-save the entity as another row in Doctrine 2
I think the easy solution is to set your id to null and doctrine will generate an id for you while creating the cloned object...
$clonedObject = clone $object;
$clonedObject->setId(NULL);
$clonedObject->setName($object->getName()." (Clone)");
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.
Its a simple question, but I'm not aware of the answer and I couldn't get it to work.
Can I update only one entity on the entire DataContext? Or should I follow plain ADO.NET for this operation only?
Edit:
public MyObject GetMyObjectById(int selectedId)
{
DataContext db = _dbManager.GetContext();
return db.MyObject.SingleOrDefault(p => p.Id == selectedId);
}
I am getting an object with the above query...
I am querying then for an integer...on another table/object
public int GetMyInteger()
{
DataContext db = _dbManager.GetContext();
return db.MyAnotherObject.FirstOrDefault().MyInteger;
}
Everything is fine for all my operations...but now i just want to update only the integer i got from the database...
public void SetMyInteger(int updInteger)
{
DataContext db = new DataContext(ConnectionString);
MyAnotherObject theEntity = db.MyAnotherObject.FirstOrDefault();
atheEntity.MyInteger = updInteger;
db.SubmitChanges(ConflictMode.ContinueOnConflict);
}
The above method deleted MyObject i got from the first query!!! Of course if i use the static context DataContext tries to update MyObject and MyAnotherObject which seems the correct behaviour.
Edit:
I have changed the method getting the integer with a new datacontext as well and seems to working correctly, i have a strange thought on why called the delete method, because it was the method that was called, but again .. is working now...
Thank you all for your time.
Yes it's possible. What have you tried? It should be as simple as this:
using (var dc = new YourDataContext())
{
Person p = dc.Persons.Take(1).Single();
p.FirstName = "Ahmad";
dc.SubmitChanges();
}
Yes, you can:
Foo foo = dc.Foos.Where(foo => foo.Id == 345).Single();
foo.Name = "foo";
dc.SubmitChanges();
This one i had today is a strange one.
I have this query in an assembly method.
public Order[] SelectAllOrders()
{
Order[] orders;
using (MyDataContext context = new MyDataContext())
{
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Order>(order => order.OrderDetails);
context.LoadOptions = dlo;
orders = context.Orders.Select(p => p).ToArray();
}
return orders;
}
Supposed i already called the ToArray() the SQL Command executed and gave me the objects i need and i give them to a new Order[] array this should not need the DataContext instance.
While im serializing the Order[] i get from the method return, serializer tries to access the DataContext again and i get an exception that cannot access disposed object.
Tried without the using() statement and works like it should. But, why i get this behavior?
Anyone could give an explanation why deferred loading still remains while I'm calling .ToArray() and assigning new variable with the contents?
The Select(p=>p) achieves very little; you might as well just call:
orders = context.Orders.ToArray();
Re the problem - I would guess that either OrderDetails hasn't really loaded, or it is trying to load some other data lazily. I would suggest investigating by (in a dev session):
Order[] orders;
using (MyDataContext context = new MyDataContext())
{
context.Log = Console.Out; // show me
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Order>(order => order.OrderDetails);
context.LoadOptions = dlo;
Console.WriteLine("> Calling ToArray");
orders = context.Orders.ToArray();
Console.WriteLine("> ToArray complete");
// TODO: your extra code that causes serialziation, probably
// involving `DataContractSerializer`
Console.WriteLine("> Calling Dispose");
}
With this, you should be able to see any extra database trips that are happning after the ToArray but before the Dispose(). The point being: this data is needed for serialization, so either a: ensure it gets loaded, or b: exclude it from serialization.