How do I configure StructureMap to use a generic repository?
Asked Answered
J

3

8

I have an interface IGenericRepository<TEntity> where TEntity : IEntity and an implementation GenericRepository<TEntity> where TEntity : Entity.

I'm trying to inject a specific IGenericRepository<Section> into a class using StructureMap:

    ObjectFactory.Initialize(x =>
        {
            x.For(typeof(IGenericRepository<>)).Use(typeof(GenericRepository<>));
        });

But when I try to use ObjectFactory.GetInstance<IGenericRepository<Section>>(); I get:

StructureMap Exception Code: 202 No Default Instance defined for PluginFamily System.Data.Common.DbConnection

Any ideas why this is or what I'm doing wrong?

Thanks in advance,

Simon

Jaela answered 14/5, 2010 at 15:35 Comment(0)
P
3

What is your constructor like for GenericRepository<>?

It or one of its dependencies is expecting a DbConnection that SM can't create.

Pol answered 14/5, 2010 at 16:11 Comment(2)
Thanks for that, my generic repository is taking an ObjectContext which I have no added through SMJaela
Is This an answer or another question? If it's a question why is it marked as the accepted answer?Hamamatsu
N
8

You are receiving in your GenericRepository's constructor a DbConnection, which is an abstract class, and are not configuring SM to know which specific class should use for it.

i.e.:

 ObjectFactory.Initialize(x =>
        {
            x.For(typeof(DbConnection)).Use(typeof(SqlConnection));
            x.For(typeof(IGenericRepository<>)).Use(typeof(GenericRepository<>));
        });
Nitrite answered 14/5, 2010 at 16:23 Comment(0)
H
4

I had this exact same problem:

Have a Generic Repository:

public interface IRepository<TEntity> : IDisposable where TEntity : class
    { }

and a Concrete Implementation:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    { }

which i wanted injected into the constructor of the Controllers at runtime where the TEntity would be the Model relevant to that Controller:

public FooBarController(IRepository<FOO_BAR_TYPE> repository)
        {
            _repo = repository;
        }

the Controller would then use Repository "_repo" to update the Model:

//
// POST: /EmergencyServiceType/Create
[HttpPost]
public ActionResult Create(FOO_BAR_TYPE foobar)
{
    if (ModelState.IsValid)
    {            
        // GetNextSequenceValue() deals with Oracle+EF issue with auto-increment IDs
        foobar.FOO_BAR_ID = _repo.GetNextSequenceValue(); 
        _repo.Add(foobar);
        _repo.SaveChanges();
        return RedirectToAction("Index");  
    }

    return View(foobar); // display the updated Model
}

simonjreid elluded to the answer for me: had to add the ObjectContext to the StructureMap configuration (the Repository's purpose was to wrap up the Context generated by EntityFramework, which i called MyContextWrapper. Therefore because the Repository depended on MyContextWrapper, which in turn depends on ObjectContext):

// This avoids 'No Default Instance for ...DbConnection' exception
x.For<System.Data.Objects.ObjectContext>().Use<MyContextWrapper>();
x.For<System.Web.Mvc.IController>().Use<Controllers.FooBarController>().Named("foobarcontroller"); // note that Named is required and is Case Sensitive

However, now i get the StructureMap runtime Exception:

StructureMap Exception Code: 205 Missing requested Instance property "connectionString"

After reading a post by Jeremy Miller A Gentle Quickstart (right at the bottom) i found that you can define what arguments to pass into the constructor of your registered types ie i needed to pass in the Connection String to the Constructor of the MyCustomContext class (here is the full listing of how i am initializing the ObjectFactory:

string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["MyContextWrapper"].ConnectionString;
ObjectFactory.Initialize(x =>
            {
                x.Scan(scan =>
                        {
                            // Make sure BUSINESS_DOMAIN assembly is scanned
                            scan.AssemblyContainingType<BUSINESS_DOMAIN.MyContextWrapper>(); 
                            scan.TheCallingAssembly();
                            scan.WithDefaultConventions();
                        });
                // 'connStr' below is a local variable defined above
                x.For<System.Data.Objects.ObjectContext>()
                    .Use<MyContextWrapper>()
                    .Ctor<string>().Is(connStr);
                x.For<System.Web.Mvc.IController>().Use<Controllers.FooBarController>().Named("foobarcontroller"); 
            });

And BOOM! Can now have my Controller instaniated at runtime by StructureMap and get it to inject an instance of IRepository...happy days.

Heriberto answered 6/10, 2011 at 7:0 Comment(0)
P
3

What is your constructor like for GenericRepository<>?

It or one of its dependencies is expecting a DbConnection that SM can't create.

Pol answered 14/5, 2010 at 16:11 Comment(2)
Thanks for that, my generic repository is taking an ObjectContext which I have no added through SMJaela
Is This an answer or another question? If it's a question why is it marked as the accepted answer?Hamamatsu

© 2022 - 2024 — McMap. All rights reserved.