We are using the repository pattern in our ASP.NET MVC 3 application. This means that, although we use EF 4.1 Code First to access the data in the backend, all MVC controllers do that via a generic repository class rather than directly over the DbContext subclass.
Simplified code snippet:
public class MyEntityContext : DbContext, IMyEntityContext
{
public IDbSet MyEntities { get; set; }
...
}
public class MyEntityRepository : IMyEntityRepository
{
private IMyEntityContext _context;
public IQueryable<MyEntity> MyEntities
{
return _context.MyEntities;
}
...
}
public class MyEntityController : Controller
{
private MyEntityRepository _repository;
...
}
We use interfaces and dependency injection for every dependency. It works fine. Looks nice, doesn't it? But now for the caveat:
We also provide a WCF Data Service (CTP supporting Code First) to access the entities. We want to use the repository in that service, too. But this seems tricky. When using the MyEntityContext directly, the service looks like this:
public class MyEntityService : DataService<MyEntityContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("MyEntities", EntitySetRights.All);
}
}
But when I try to replace the MyEntityContext by the repository, there are two issues:
The type specified for the generic DataService<..> needs to be a class with a default constructor, which breaks the pretty design-by-contract and dependency injection design.
It even seems that the type provided has to be a DbContext class: I tried and used the MyEntityRepository instead, but failed (see details).
I seem lost... Can anyone bring me back on the proper track?
Details:
My first go was:
public class MyEntityService : DataService<MyEntityRepository>
{
...
However, when calling the service, it fails with the following error message:
The server encountered an error processing the request. The exception message is 'On data context type 'MyEntityRepository', there is a top IQueryable property 'MyEntities' whose element type is not an entity type. Make sure that the IQueryable property is of entity type or specify the IgnoreProperties attribute on the data context type to ignore this property.'.
I tried the following steps to fix this, but did not get rid of this error message:
Adding a [DataServiceKey("MyEntityId")] to MyEntity, where MyEntityId is the correct key property of the entity.
Replacing the type of Repository.MyEntities by IDbSet instead of IQueryable.
BTW: The following posts are not duplicates:
WCF Repository Service pattern with entity framework
Service Layer/Repository Pattern
Best way to implement Repository Pattern?
webservices with repository pattern in c# and WCF?
WCF Service design pattern
Why do you want to use repository? You have context so use it. Don't create onion architecture just because you want to use pattern. WCF data service already handles everything you need itself. No sorry, it sometimes offers even more (for example interceptors).
By using custom repository you are moving to reflection provider data source. If you also plan to modify your entities through WCF data service that is also against your repository because reflection provider is read only unless it also implements IUpdateable. Check also rules for reflection provider.
Btw. WCF Data Services in .NET 4 doesn't support DbContext directly (that support is only in CTPs of upcoming version) but you there is workaround for that. The link is for old CTP. In current version there is not UnderlyingContext property but you can use IObjectContextAdapter to get ObjectContext.
As you can also see in last link type provided to the service doesn't need to have default constructor - it is up to you what constructor you use when creating data source. If you need dependency injection you will probably have to check the way how to inject directly to the service itself (for example here for Unity and plain WCF) and use injected data in CreateDataSource.
Here's how to make a WCF Data Service with whatever pattern you're using, even none at all.
Getting Started With OData Part 2: Building an OData Services from Any Data Source
Just make sure your entity, poco, model or whatever has a property public int ID , or has this class annotation provided by the System.Data.Services assembly in the System.Data.Services namespace:
[DataServiceKey("TheNameOfYourPrimaryKeyProperty")]
This will make it recognizable as an entity type by the WCF Data Service.
As others have pointed out though, just make sure that adding yet another layer in your stack is a good decision.
Related
I have a typical application with Controllers, Services, Repositories. So, there are 2 projects:
ASP.NET Core WebAPI with controllers
Core with all business logic
The WebAPI should know only about services from Core. In the Core I have public classes (services) that returns DTOs, but these services depends on DbContext that I want to mark as internal. Of course I can't
Error CS0051 Inconsistent accessibility: parameter type
'DevicesDbContext' is less accessible than method
'DeviceService.DeviceService(DevicesDbContext, IMapper)'
I'm using EF Core and instead of own Repositories I use DbContext. I have entity model that I have to use only in Core project. Could you please advice how can I achieve that?
For example my model is:
internal class Device
{
public int Id {get;set;}
}
DbContext:
internal class DevicesDbContext : DbContext
{
public DbSet<Device> Devices {get;set;}
}
Service:
public class DeviceService : IDeviceService
{
public DeviceService(DevicesDbContext dbContext, IMapper mapper)
{
}
..
}
I got that error in constructor of DeviceService. It is not a duplicate because I know what that error mean and how to fix that. Here I asked about design or architecture of this approach because I need to avoid of using models and dbcontext in WebAPI directly
If you don't want to use Repositories to guard data access (which generally still return Entities, not DTOs, so Entities need to be public) then the real question is:
"Why do you want to avoid using the DbContext & Entities in your Web API?"
Entity Framework is a framework. It's purpose is to facilitate data access to make your code easier to write and easier to understand. Just as you chose to use the .Net framework and leverage things like Linq, Generics, etc. by chossing EF you should seek to leverage everything it offers.
If you absolutely must keep the context and entities out of the API assembly references, or want to centralize business logic involving entities between a Web API and another set of MVC controllers then you're looking at building an anemic API. In this case:
Services.DLL -- References DbContext, entities..
public interface ISomethingService
{
IEnumerable<SomeDto> GetSome(/*params*/);
}
public class SomethingService : ISomethingService
{
public SomethingService(SomeDbContext context)
{ // Init stuff.
}
IEnumerable<SomeDto> ISomethingService.GetSome()
{
// Get some stuff and return DTOs.
}
}
Web API DLL -- Only references DTOs.
public class SomeAPI : ISomethingService
{
private ISomethingService Service { get; set; }
public SomeAPI(ISomethingService service)
{
Service = service;
}
public IEnumerable<SomeDto> GetSome()
{
return Service.GetSome();
}
}
The API is anemic in that it just passes requests through to a common service and forwards the response. The API doesn't need to implement the same interface, it can simply accept a reference to the service and consume it, passing through whatever parameters to get the DTOs that it will pass back.
The downside of this approach is that to modify the service you're flipping between the API and the Services layer vs. just working within the API. I don't favor using approaches like this because APIs and such often need to consider details like filtering, paging, etc. so I want to leverage the excellent LINQ capabilities that EF offers. I also leverage EF's IQueryable support heavily to keep my data access layer simple and compact, letting consuming services decide how to fetch the detail they need. Masking this with an extra service boundary adds complexity and inefficiencies as it either results in complex code, lots of very similar functions, and/or wasted memory/processing to return data that is not needed.
This is in a .NET Core project. In the Startup I register a singleton for my translation service that gets initialized with configuration settings from appsettings.json. Once it's set the whole app will use that service. This works great in my controllers and repositories, I just define the constructor to take that interface and it injects as expected:
services.AddSingleton<Foo.Bar.ITranslationService>(
new Foo.Bar.SomeTranslator(config.TranslationSettings));
But the issue I ran into is one where a POCO needed to use that translation service. And my POCOs all get generated as generics by the repository, so for example a repository method might look like this:
public TEntity GetById(object id){
return connection.Get<TEntity>(id);
}
So the repository has the injected TranslationService passed into it, but is there a way to cleanly pass that along to the POCO that needs it using DI? Or do I need to hack it somehow? I'm trying to figure out the cleanest way to do this, thanks!
How about property injection?
public TEntity GetById(object id){
var entity = connection.Get<TEntity>(id);
if (entity is ITranslatable t)
{
t.Translator = _translationService;
}
return entity;
}
You basically need to add the translation interface to the constructor of that object.
public class MYPOCO
{
private Foo.Bar.ITranslationService translator
public MYPOCO(Foo.Bar.ITranslationService translator)
{
this.translator = translator;
}
}
This then allow you to use the translator service in the class. But your going to run into a number of issues there.
Unless the framework explicitly creates your POCO then it will not
get injected. For example it would need to be a Controller of some
sort that would be created by the framework as soon as a route is
called.
You may need your own factories to create instances of the POCO.
To keep the POCO as clean as possible, you probably want a Factory, which would create an instance of your POCO and set the translations appropriately. The Factory would take an instance of the translator service.
Overview
I am making a .net-core web API with simple CRUD operations.
I have made the GET methods and got them up and running, however when I try to implement the Create method using dbContext.Add(myItem) I am not able to use dbContext.SaveChanges() afterwards.
My current work is available at:
https://github.com/petermefrandsen/TKD-theory-API
So far
I have tried adding a overwriting method to my database context.
Additionally I have tried adding the entity framework reference to the project.
As I do use interfaces for loose coupling I am at a loss when I comes to comparing with tutorials and other peoples similar problems. (read I am fairly new to c#).
Code
Controller:
[Route("dan/")]
[HttpPost]
public ActionResult<DanTheoryItem> PostDanTheoryItem(DanTheoryItem danTheoryItem)
{
_context.PostDanTheoryItem(danTheoryItem);
return new ActionResult<DanTheoryItem>(danTheoryItem);
}
IContext:
DanTheoryItem PostDanTheoryItem(DanTheoryItem danTheoryItem);
Context:
public DanTheoryItem PostDanTheoryItem(DanTheoryItem danTheoryItem)
{
var theoryItem = new DbDanTheoryItems
{
Id = danTheoryItem.Id,
KoreanTheoryItemId = danTheoryItem.KoreanTheoryItemId,
MainCategory = danTheoryItem.MainCategory,
SubCategory = danTheoryItem.SubCategory,
SubToSubCategory = danTheoryItem.SubToSubCategory,
NameLatin = danTheoryItem.NameLatin,
NamePhonetic = danTheoryItem.NamePhonetic,
NameAudio = danTheoryItem.NameAudio
};
_dbContext.DanTheoryItems.Add(theoryItem);
//_dbContext.SaveChanges();
return danTheoryItem;
}
Desired result
I'd like to have the controller call the context methods that will write the desired data to the database.
Your interface doesn't contain a SaveChanges method. Since you are using dependency injection only the methods in your interface will be available to your controller.
If you inherit from System.Data.Entity.IDbContext class in your custom interface, the method will be exposed to you in your controller.
Instantiating an instance of your DbLokisaurTKDTheoryAppContext class in your controller will expose the SaveChanges method as well.
It's hard to say exactly because you've neglected to post certain key portions of your code. However, my best guess is that you "context" is being provided IDbContext (of your own creating), instead of DbContext, and your interface doesn't define a SaveChanges method.
Honestly, just get rid of IDbContext. The point of an interface is to define a contract between multiple implementations. Here, there can be only one implementation: DbContext, and DbContext isn't even aware of this interface. Just inject your derived DbContext directly.
Using an interface isn't a magic wand. You have a hard dependency on Entity Framework here, so interface or not, you're tightly coupled. That's not necessary a bad thing though. EF is serving as your data layer, and this application is data driven; it's going to be tightly coupled to the data layer no matter what.
The API itself serves as your abstraction. Other layers will presumably just use the API, so no further abstraction is necessary, and in fact just adds more maintenance concern with no added benefit.
I am attempting to use Ninject on my current project, and up to now, have been loving it. I am in the middle of attempting to configure an IInterceptor object to intercept and handle a failed method call to my service layer. This is hosted in an ASP.NET MVC 5 application.
In the IInterceptor, I've tried several things, such as:
Setting private variables using constructor injection, but came to discover that it appears Ninject will reuse an IInterceptor instance for a method indefinitely, and I haven't found a way to stop that. Since one of the things I bring into scope is a DbContext which gets disposed elsewhere, it ends up failing on any future requests than the one it was created on.
I found that the IInvocation has a Request.Kernel property. However, when I attempt to resolve my UOW from the container, which is .InRequestScope(), it fails, since it attempts to resolve the IUowService dependencies, (one of the dependencies depends on the HttpContext which is null at this point), but appears to be doing so outside the Request scope. It is ignoring the fact that the dependencies it needs have already been created within the ASP.NET request, and is attempting to create new ones.
Setting a binding for the interceptor this.Bind<NinjectExceptionHandler>().ToSelf().InTransientScope(), yet this didn't seem to stop the caching of the interceptor.
I imagine there is something I am missing. I understand wanting to cache IInterceptor objects for performance, but I find it irksome that I can't easily use the IOC container or Injection to get the objects I need for my request.
This is the last issue I am having with getting interception up and running as I need, so any help is greatly appreciated!
Per your request i'm going into more detail on how we've achieved "1 proxy : 1 interceptor" instance relation ship.
We've taken the easy way which does not offer as much flexibility as what the official ninject interception extensions offers. We are relying directly on castle.core dynamic proxy and thus castle's IInvocation interface.
(Please not the code below is for a proxy without target, but a proxy with target is quite similar -- the only thing which changes is that you'll need to know the target class type and use IResolutionRoot.Get<TargetClassType>() to instanciate it).
Basically we created a binding like:
IBindingRoot.Bind<IFoo>()
.ToProvider<InterfaceProxyWithoutTargetProvider<IFoo>>();
Now of course we need to know which interceptors the proxy shall use. Again we are using an easy - and not so nice - design:
public interface IInterceptorBindingDefinition<TTarget>
{
Type InterceptorType { get; }
}
public class InterceptorBindingDefinition<TTarget, TInterceptor> : IInterceptorBindingDefinition<TTarget>
where TInterceptor : IInterceptor
{
Type InterceptorType { get { return typeof(TInterceptor); } }
}
IBindingRoot
.Bind<IInterceptorBindingDefinition<IFoo>>()
.To<InterceptorBindingDefinition<TTarget, LoggingInterceptor>();
IBindingRoot
.Bind<IInterceptorBindingDefinition<IFoo>>()
.To<InterceptorBindingDefinition<TTarget, SomeOtherInterceptor>();
This means IFoo shall get two interceptors: LoggingInterceptor and SomeOtherInterceptor.
and the implementation of the provider:
public class InterfaceProxyWithoutTargetProvider<TInterface> : IProvider<TInterface>
where TInterface : class
{
private readonly IProxyGenerator proxyGenerator;
private readonly IInterceptorFactory interceptorFactory;
public InterfaceProxyWithoutTargetProvider(IProxyGenerator proxyGenerator, IInterceptorFactory interceptorFactory)
{
this.proxyGenerator = proxyGenerator;
this.interceptorFactory = interceptorFactory;
}
public Type Type
{
get { return typeof(TInterface); }
}
public object Create(IContext context)
{
var interceptorTypes = context.Kernel.Get<IEnumerable<IInterceptorBindingDefinition<TInterface>>();
IList<IInterceptor> interceptors = interceptorTypes
.Select(x => x.InterceptorType)
.Select(x => context.ContextPreservingGet(x))
.ToList();
return this.proxyGenerator.CreateInterfaceProxyWithoutTarget<TInterface>(interceptors);
}
}
Now of course we polished the thing a little bit so we have a fluent syntax configuring the binding of the proxy and the interceptor - which is easy enough.
However ninject.extensions.interception's approach with its IAdviceRegistry and IAdvice is certainly better (but also requires more insight into how ninject works).
So it appears that there is no way to do what I was trying gracefully with Ninject. Once in the IInterceptor and in the later parts of async operations, the HttpContext was lost and Ninject couldn't resolve things that really it should have thought were in scope. Coupled with the fact that it reused IInterceptor's for a method (like I said, understandable, but irritating), I just couldn't get it to work right as I wanted to.
What I was able to do to get around the fact was something simple, yet a little kludgy (I think). Since all the methods that I was intercepting were in my service layer, and all my services implemented a IBaseService through a BaseService abstract base class, which happened to have the objects I needed as properties, I was able to do this in the interceptor:
var uow = (invocation.Request.Target as IBaseService).UnitOfWork;
This allowed me to access my unit of work and Fail it, as well as access the logging instance I was working on.
While this works, I would like to see someway to get interceptor constructor injection working correctly through multiple calls, or calls to the Kernel further down the line to realize that it has already resolved an object still in scope (although I am guessing that it may think its out of scope since ASP.Net abandoned the scope upon await).
For any interested, I am going to try and post about this on my blog soon (see my user page if actually interested, not spamming SO myself).
I have DbContext subclass
ReportingContext : DbContext
As I'm doing simple CRUD I then created a WCF Data service to expose my DbSet...
public class ReportService : DataService<ReportingContext>
I have been able to use the ReportingContext directly to do 'table splitting'. Basically use 2 entities (ReportLayout and ReportLayoutData) which both use a single table. I was able to configure this using the fluent API. Everything worked fine in Unit tests as I was able to return ReportLayouts and only load ReportLayoutData when they were accessed.
My problems started when I tried to do this through a WCF Data Service, OData version 5.6 - using the DataServiceContext class. Returning ReportLayouts work fine, but trying to lazy load the dependent data has not been possible so far. I have tried different things:
Calling Include via a service method actually worked when I debugged the service directly and checked the generated sql - 2 separate queries as for the unit test. However, the service simply did not include the ReportLayoutData property in its returned properties when viewed in the browser and I got client side errors relating to the missing property.
[WebGet]
public IQueryable<ReportLayout> GetReportsByID(string ids)
{
var ints = GetInts(ids);
return CurrentDataSource.Reports.Include("LayoutData").Where(x => ints.Contains(x.ReportLayoutID)).AsQueryable();
}
private static int[] GetInts(string ids)
{
return ids.Split(",".ToCharArray()).Select(x => Convert.ToInt32(x)).ToArray();
}
I tried to use DataServiceContext.Expand - $expand - and this failed with various
errors as I tried slighly different arguments
I tried calling Execute, various problems
I turned the ReportLayoutData property into an IQueryable even though it is a 1-1 relationship and now it says ReportLayoutData is not a property of ReportLayout when running the EF specific unit test that previously worked fine.
My question: is it possible to Lazy Load via a WCF Data service in this manner or should I just expose 2 collections and resolve the results into a single object on the client? If it is possible I would just like to see the basic pattern - a couple of related entities, fluent API declarations and the DataService code. Thanks for any help.
EDIT
I am currently being plagued by the error:
A property with name 'LayoutData' on type 'ReportLayout' has kind 'Structural', but it is expected to be of kind 'Navigation'.
Although there is no problem retrieving the data in the browser: ReportService.svc/Reports()?$expand=LayoutData
partial stack trace:
Microsoft.Data.OData.ReaderValidationUtils.ValidateNavigationPropertyDefined(String propertyName, IEdmEntityType owningEntityType, ODataMessageReaderSettings messageReaderSettings)
at
Microsoft.Data.OData.Atom.ODataAtomEntryAndFeedDeserializer.TryReadNavigationLinkInEntry(IODataAtomReaderEntryState entryState, String linkRelation, String linkHRef)
I was able to remove the above error by not exposing 2 dbSets through the service. Will consider a service operation to return what I need from EF, shame it isn't so elegant.
In the end my solution was to split the table so as to create a navigation property exposed as an ICollection
I was able to implement queries such as Reports/ReportService.svc/Reports(1) $expand=LayoutData as a result (AddQueryOption("$expand", "LayoutData") and wrote a service method to do this for multiples.
[WebGet]
public IQueryable<ReportLayout> GetReportsByID(string ids)
I only exposed a single Dbset via the service - the children are not accessible directly.
Client side updates to the dependent entities can be achieved using the DataServiceContext methods:
AddObject, AddLink
AttachTo, UpdateObject, AttachLink //UpdateObject on the child ensures the entity state changes to modified (see DataServiceContext.Entites collection).
In retrospect I probably did not need to split the table but do not have time to play with this.