MVC, Repository Pattern and DataLoadOptions - asp.net-mvc

I have a little project where I'm running MVC3.
I use LINQ to fetch data from the database.
I built my project with the same architectural design as the premade examples that come with MVC3.
In such a project, the application is split up and in this topic I want to focus on the Model.cs files. I have one for each controller at the moment, So as an example, I have a HighscoreController.cs and a HighscoreModels.cs. In the model class I define a Service class that has a reference to a datacontext and some methods that use this datacontext to query the database.
Now i ran into the problem that some of these methods are executing the same queries, and therefore I wanted to make a central point of access to the database so I thought I would implement the Repository Pattern, and so I did.
So instead of having a reference to a datacontext in the Service class I now have a reference to the repository as such:
private IRepository _repository;
public HighscoreService()
: this(new Repository())
{ }
public HighscoreService(IRepository repository)
{
_repository = repository;
}
Now the database calls are handled within the repository and the repository is used from the Service class through the _repository reference.
My repository is built like this:
public class Repository : IRepository
{
private MyDataContext _dataContext;
public Repository()
{
_dataContext = new MyDataContext();
}
public Member MemberByName(string memberName)
{
Member member = CompiledQueries.MemberByName(_dataContext, memberName);
return member;
}
}
The problem I face appears when I try to use DataLoadOptions in combination with this repository pattern.
Because when you use dataloadoptions, you must not have made previous queries on the datacontext before a new dataloadoptions is applied to it. And since my repository reuses the datacontext throughout all methods, this does not work out at all.
I have been trying 2 things, one is recreating the datacontext within every methods, by way of the using statement, to make sure that the datacontext is refreshed every time. But then I get problems when I have fetched the result from the repository back into my model and the scope runs out inside the repository pattern, as the using statement ends, which means that the result cannot be used with e.g. .Count() or .ToList() because the datacontext that supplied me the data has been terminated. I also tried another solution where it uses the same datacontext throughout the whole repository, but makes a new instance in each method that uses dataloadoptions. This felt very dirty ;)
So can anyone give me a suggestion on how to use DataLoadOptions with the repository pattern? and avoid the problems I just described. Or should i not use dataloadoptions and choose another way of doing it? The reason i use DataLoadOptions by the way, is that I want to have some data from related tables.
As a little question on the side: In the code example above you can see that I have placed CompiledQueries within a .cs file of its own. Is this a bad design? Are there any guidelines for where to put compiled queries in an MVC application?
Thanks for reading and hope there are some answers for my questions ;) thanks a lot in advance. If you need more information, just ask.

I am by no means an expert on DataLoadOptions, but from your question and what I've read about it, it seems that you need to use it for eager loading. In reference to this:
"Because when you use dataloadoptions, you must not have made previous queries on the datacontext before a new dataloadoptions is applied to it."
.. to me this sounds like a shortcoming or design flaw with DataLoadOptions (I personally use Entity Framework, not LINQ to SQL). While I do think it's a good idea to have a single data context per HTTP request as offered in the first 2 comments by ngm and CrazyCoderz, I don't think this will solve your problem. If you want to reuse a single data context within a single HTTP request, as soon as you execute the first query, it sounds like you will be unable to set the DataLoadOptions on the data context to a new value.
I saw a presentation at vslive vegas where the presenter offered one of the solutions you mentioned, creating a new data context in each repository method. What you would have to do here is call ToList() or ToArray() before terminating the using statement and returning the method result(s).
As you mentioned, this would make objects not pre-loaded into the enumeration inaccessible after the method returns. However, if you already have the query executed and converted to a List, Collection, Array, or some other concrete IEnumerable, you don't need access to the Count() or ToList() methods any longer. Instead, you can use Array.Length or List.Count or Collection.Count properties.
What else is stopping you from creating a new data context in each repository method? Meaning, what do you need the data context for, after executing the repository method, that you can't get because it's been disposed of?
Reply to comments
For your first query, can you do this?
public Member GetSomeRandomMember()
{
Member[] members = null;
using (var context = new MyDataContext())
{
// execute the query to get the whole table
members = context.Members.ToArray();
}
// do not need to query again
var totalRows = members.Length;
var skipThisMany = PerformRandomNumberComputation(totalRows);
return members.Skip(skipThisMany).FirstOrDefault();
}
Granted that might not be optimal if your Members table has a lot of rows. In that case you would want to execute 2 queries -- 1 to count, and a second to select the row. You could achieve that by opening 2 contexts:
public Member GetSomeRandomMember()
{
using (var context1 = new MyDataContext())
var totalRows = context1.Members.Count();
var skipThisMany = PerformRandomNumberComputation(totalRows);
Member member = null;
using (var context2 = new MyDataContext())
member = context2.Members.Skip(skipThisMany).FirstOrDefault();
return member;
}
For the second part of your comment, I'm not sure I get what you are talking about. The fetching of the data and the making changes to it should all come in a single operation with a single context anyways:
public void SaveMember(int id, string email, bool isSuspended)
{
using (var context = new MyDataContext())
{
var member = context.Members.Single(m => m.Id == id);
member.Email = email;
member.IsSuspended = isSuspended;
context.SaveChanges(); // or whatever the linq to sql equivalent is
}
}
If you want to pass the whole entity to the repository method, you should still query it out so that it is attached to the correct context:
public void SaveMember(Member member)
{
var memberDto = member;
using (var context = new MyDataContext())
{
member = context.Members.Single(m => m.Id == memberDto.Id);
member.Email = memberDto.Email;
member.IsSuspended = memberDto.IsSuspended;
context.SaveChanges(); // or whatever the linq to sql equivalent is
}
}

Related

I cant add a data into the sql via EF (Non static method requires a target)

In my MVC project, Im tryna add a bike to my db, but i cannot.
Here's my code:
public override ServiceResult New(Bike entity)
{
var bike = repository.Select(x => x.Brand == entity.Brand && x.Model == entity.Model);
if (bike.Any())
{
return new ServiceResult(ServiceResultCode.Generic, "This bike is already exists.");
}
return base.New(entity);
}
And let me show you other connected classes and maybe you'll get that where the wrong part is.
Here is my GenericRepository class.
public virtual IEnumerable<T> Select(Expression<Func<T, bool>> Filter = null)
{
if (Filter != null)
{
return _dbSet.Where(Filter);
}
return _dbSet;
}
when i press the add button. It's like "Dude stop, you dont know what you're trying!" Like a guardian. Maybe a little help?
First up, if this is your first foray into Entity Framework, start simple before trying to delve into things like repository patterns. IMHO while a repository pattern is very useful for isolating your domain from your logic to test that logic, the Generic repository pattern is an anti-pattern. Immediately the first problem is returning IEnumerable<T>, to maintain the benefit of EF with Linq expressions, repositories should return IQueryable<T>.
public virtual IEnumerable<T> Select(Expression<Func<T, bool>> Filter = null)
{
if (Filter != null)
return _dbSet.Where(Filter);
return _dbSet;
}
vs.
public virtual IQueryable<T> Select(Expression<Func<T, bool>> Filter = null)
{
if (Filter != null)
return _dbSet.Where(Filter);
return _dbSet;
}
Both methods appear to work, but they do two completely different things. Both methods will triggered a deferred execution for the query when you execute the .Any() statement, however when you return IEnumerable<T>, the query will execute as effectively a SELECT * FROM [T] where if you return IQueryable<T> the query will execute as a SELECT CASE WHEN EXISTS essentially running a query to just return whether a row exists or not. Any operation you use, the first example will always be doing a SELECT * where the second has the opportunity to generate more efficient and precise queries.
To start narrowing down the root of your problem though, start simple with just the DbContext and the entities. Generally it's not a good idea to pass entities around between client and server as it exposes your schema and generally passes more information than the client needs to display. It also leads to lazy load hits when serializing or potentially errors/#null data with cyclic references or if lazy loading is disabled to get around these problems. Data coming from a client should also never be trusted, so any code that might attach a passed in entity set state to Modified and save to the database risks harmful data injection attacks.
But looking at your original example, start by reducing it down to the simplest possible thing without getting dug right in to repositories and inheritance:
public override ServiceResult New(Bike entity)
{
using (var context = new ApplicationContext())
{
var bikeExists = context.Bikes.Any(x => x.Brand = entity.Brand
&& x.Model == entity.Model);
if (bikeExists)
return new ServiceResult(ServiceResultCode.Generic, "This bike is already exists.");
var newBike = new Bike
{
Brand = entity.Brand,
Model = entity.Model,
// Copy over only data you trust after validating it.
};
context.Bikes.Add(newBike);
context.SaveChanges();
return new ServiceResult(ServiceResultCode.Success, "Bike added.");
}
}
If that works, then re-factor to reintroduce common base class functionality, repositories, etc. This will help identify where/what elements are tripping you up. Writing code to best practices is something I recommend that developers re-factor code towards rather than try to write from scratch especially when starting out with unfamiliar technologies. Often the attempts to write efficient code make problems harder to identify and fix. The end goal would be to reintroduce a repository, a unit of work, and a view model for communication between client and server. It would end up looking something like:
public override ServiceResult New(NewBikeViewModel newBike)
{
using (var contextScope = ContextScopeFactory.Create())
{
var bikeExists = BikeRepository.Bikes.Any(x => x.Brand = newBike.Brand
&& x.Model == newBike.Model);
if (bikeExists)
return new ServiceResult(ServiceResultCode.Generic, "This bike is already exists.");
var bike = BikeRepository.Create(newBike.Brand, newBike.Model);
// Can fill in any optional details from the view model into bike here.
contextScope.SaveChanges();
return new ServiceResult(ServiceResultCode.Success, "Bike added.");
}
}
NewBikeViewModel represents a model from the view containing the data to identify a new bike. I utilize a Unit of Work pattern called Mehdime DbContextScope which coordinates a context scope between business logic (controllers) and repositories. The repository locates it's scope (throwing an exception if you call it outside of one) and uses that to get the DbContext. Context Scopes or Units of Work are nice because they form a boundary for committing changes. The issue with something like a DbContext being injected and limited to a lifetime scope such as the request is that it is all or nothing for everything. Any change that attempts to commit in the context and fails remains associated to that context and will prevent that context from saving anything else. The BikeRepository is not a Generic repository, it is a repository that services bikes. This includes retrieving data, but also creating data. It serves as a factory for Bikes to ensure that all required information is passed in. It is also in the position to ensure that any references to other entities can be set up to ensure that a new Bike is always returned in a complete enough state. Create takes parameters for all required fields/references. Any optional data can be filled in afterwards before the changes are saved. This is only an example of the patterns I use, the key thing is to start with the simplest thing then move towards the target structure you want to adopt.

Passing DbContext reference to another method from within a using statement bad practice?

I have a method that uses an EF 6 data model and is wrapped within a using statement. There are some additional and common steps that need to be done where Id rather not include them all in one method. The code below, while not the exact code Im using, seems to work. However, Im wondering if there is any "gotchyas" Im setting up related to EF-specific issues dealing with the DbContext itself. Especially in how everything must be translated to SQL?
The main goal is to do several operations on several entities before calling SaveChanges so that the implicit transactions covers all the changes rather than calling SaveChanges more than once leaving the option open for orphaned changes.
AppendFlagToOrder(ref DbContext tx, ref CustOrder o) {
var oSettings = (from a in tx.OrderSettings where a.Type == o.OrderType select a).FirstOrDefault();
if(!oSettings == null) {
o.IsFlagged = oSettings.Flagged
}
}
CheckOrderFlag(int OrderId) {
using (var ctx = new StoreDbCtx()) {
CustOrder co = (from a in ctx.Orders where a.Id == OrderId select a).FirstOrDefault();
AppendFlagToOrder(ctx,co);
//-- continue with other operations
}
}
You normally don't want to pass DbContext around like that as anyone would have the ability to call SaveChanges() on you at any point. The easiest thing to do would be to create an IStoreDbCtx interface that only has the DbSets on it you want exposed. Then you send that type through to the sub methods so they can't access SaveChanges() on you.

Add to DB using SaveChanges() in Entity Framework with object from another scope

I'm new using entity framework, and I'm trying to insert into the DB.But I'm having an issue, because I need to only SaveChanges from objects of other 3 scopes. Like this:These are my three Actions that Add the objects into my entities:
public void AddEndereco(entidade_endereco entEndereco)
{
db.entidade_endereco.Add(entEndereco);
}
public void addContato(entidade_contato entContato)
{
db.entidade_contato.Add(entContato);
}
public void addBanco(entidade_banco entBanco)
{
db.entidade_banco.Add(entBanco);
}
And in this action I need to insert all the objects into my DB:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(entidade entidade, string Grupo, string Situacao)
{
if (ModelState.IsValid)
{
if (Grupo != "")
entidade.gre_codigo = Convert.ToInt32(Grupo);
if (Situacao != "")
entidade.sie_codigo = Convert.ToInt32(Situacao);
if (entidade.ver_ativo)
entidade.ent_ativo = "S";
else
entidade.ent_ativo = "N";
if (entidade.ver_cliente)
entidade.ent_cliente = "S";
else
entidade.ent_cliente = "N";
if (entidade.ver_fornecedor)
entidade.ent_fornecedor = "S";
else
entidade.ent_fornecedor = "N";
//ADDING ANOTHER OBJECT
db.entidades.Add(entidade);
//HERE IS WHERE I NEED TO SAVE ALL (entidade_endereco, entidade_contato, entidade_banco, entidade)
db.SaveChanges();
return RedirectToAction("Index");
}
return View(entidade);
}
But it is only saving the entidade object, the others don't exist anymore when db.SaveChanges() is executed.
How can I insert into the DB with objects that were added to my entity in other scopes?
If you really want to make this work as is, you would need to store either the Context (really bad idea) or Entities (slightly less bad) across requests. Session State jumps to mind, but using it can bring in a whole load of new pain.
Ideally, you should change your design to take advantage of the stateless nature of HTTP. Each action method should be a separate transaction, saving the data from it's execution when the method is done. If those separate entities only make sense when they are all saved together, then you need to create all of them within a single action and save them to the context together. Managing the boundaries of different business entities and when they are saved is a critical part of application design, I highly recommend you read about Aggregate Roots within Domain Driven Development. Even if you don't go the full DDD route, the Aggregate Root concept will be extremely helpful to you. The CQRS Journey from Microsoft gives an in-depth tutorial of these concepts (and many others)
Im not sure, if I got your question right (excuse my poor spanish). In the Action Create you only add "entidade" to your entidades collection, and so its the only one affected by SaveChanges(). If you want to add others, include in the Create-Action or try making a EF-transaction.
Without transaction the context is lost after the Create-Method ends

NHibernate and contextual entities

I'm trying to use NHibernate for a new app with a legacy database. It's going pretty well but I'm stuck and can't find a good solution for a problem.
Let's say I have this model :
a Service table (Id, ServiceName..)
a Movie table (Id, Title, ...)
a Contents table which associates a service and a movie (IdContent, Name, IdMovie, IdService)
So I mapped this and it all went good. Now I can retrieve a movie, get all the contents associated, ...
My app is a movies shop "generator". Each "service" is in fact a different shop, when a user enter my website, he's redirected to one of the shops and obviously, I must show him only movies available for his shop. The idea is : user comes, his service is recognized, I present him movies which have contents linked to his service. I need to be able to retrieve all contents for a movie for the backoffice too.
I'm trying to find the most transparent way to accomplish this with NHibernate. I can't really make changes to the db model.
I thought about a few solutions :
Add the service condition into all my queries. Would work but it's a bit cumbersome. The model is very complex and has tons of tables/queries..
Use nhibernate filter. Seemed ideal and worked pretty good, I added the filter on serviceid in all my mappings and did the EnableFilter as soon as my user's service was recognized but.. nhibernate filtered collections don't work with 2nd lvl cache (redis in my case) and 2nd lvl cache usage is mandatory.
Add computed properties to my object like Movie.PublishedContents(Int32 serviceId). Probably would work but requires to write a lot of code and "pollutes" my domain.
Add new entities inheriting from my nhibernate entity like a PublishedMovie : Movie wich only presents the contextual data
None of these really satisfies me. Is there a good way to do this ?
Thanks !
You're asking about multi-tenancy with all the tenants in the same database. I've handled that scenario effectively using Ninject dependency injection. In my application the tenant is called "manual" and I'll use that in the sample code.
The route needs to contain the tenant e.g.
{manual}/{controller}/{action}/{id}
A constraint can be set on the tenant to limit the allowed tenants.
I use Ninject to configure and supply the ISessionFactory as a singleton and ISession in session-per-request strategy. This is encapsulated using Ninject Provider classes.
I do the filtering using lightweight repository classes, e.g.
public class ManualRepository
{
private readonly int _manualId;
private readonly ISession _session;
public ManualRepository(int manualId, ISession session)
{
_manualId = manualId;
_session = session;
}
public IQueryable<Manual> GetManual()
{
return _session.Query<Manual>().Where(m => m.ManualId == _manualId);
}
}
If you want pretty urls you'll need to translate the tenant route parameter into its corresponding database value. I have these set up in web.config and I load them into a dictionary at startup. An IRouteConstraint implementation reads the "manual" route value, looks it up, and sets the "manualId" route value.
Ninject can handle injecting the ISession into the repository and the repository into the controller. Any queries in the controller actions must be based on the repository method so that the filter is applied. The trick is injecting the manualId from the routing value. In NinjectWebCommon I have two methods to accomplish this:
private static int GetManualIdForRequest()
{
var httpContext = HttpContext.Current;
var routeValues = httpContext.Request.RequestContext.RouteData.Values;
if (routeValues.ContainsKey("manualId"))
{
return int.Parse(routeValues["manualId"].ToString());
}
const string msg = "Route does not contain 'manualId' required to construct object.";
throw new HttpException((int)HttpStatusCode.BadRequest, msg);
}
/// <summary>
/// Binding extension that injects the manualId from route data values to the ctor.
/// </summary>
private static void WithManualIdConstructor<T>(this IBindingWithSyntax<T> binding)
{
binding.WithConstructorArgument("manualId", context => GetManualIdForRequest());
}
And the repository bindings are declared to inject the manualId. There may be a better way to accomplish this through conventions.
kernel.Bind<ManualRepository>().ToSelf().WithManualIdConstructor();
The end result is that queries follow the pattern
var manual = _manualRepository
.GetManual()
.Where(m => m.EffectiveDate <= DateTime.Today)
.Select(m => new ManualView
{
ManualId = m.ManualId,
ManualName = m.Name
}).List();
and I don't need to worry about filtering per tenant in my queries.
As for the 2nd level cache, I don't use it in this app but my understanding is that you can set the cache region to segregate tenants. This should get you started: http://ayende.com/blog/1708/nhibernate-caching-the-secong-level-cache-space-is-shared

mvc in asp.net doesnt satisfy programmers

I am learning MVC4 in Visual Studio and I have many questions about it. My first statement about MVC is that MVC's Model doesnt do what I expected. I expect Model to select and return the data rows according to the needs.
But I read many tutorial and they suggest me to let Model return ALL the data from the table and then eliminate the ones I dont need in the controller, then send it to the View.
here is the code from tutorials
MODEL
public class ApartmentContext : DbContext
{
public ApartmentContext() : base("name=ApartmentContext") { }
public DbSet<Apartment> Apartments { get; set; }
}
CONTROLLER
public ActionResult Index()
{
ApartmentContext db = new ApartmentContext();
var apartments = db.Apartments.Where(a => a.no_of_rooms == 5);
return View(apartments);
}
Is this the correct way to apply "where clause" to a select statement? I dont want to select all the data and then eliminate the unwanted rows. This seems weird to me but everybody suggest this, at least the tutorials I read suggest this.
Well which ever tutorial you read that from is wrong (in my opinion). You shouldn't be returning actual entities to your view, you should be returning view models. Here's how I would re-write your example:
public class ApartmentViewModel
{
public int RoomCount { get; set; }
...
}
public ActionResult Index()
{
using (var db = new ApartmentContext())
{
var apartments = from a in db.Apartments
where a.no_of_rooms == 5
select new ApartmentViewModel()
{
RoomCount = a.no_of_rooms
...
};
return View(apartments.ToList());
}
}
Is this the correct way to apply "where clause" to a select statement?
Yes, this way is fine. However, you need to understand what's actually happening when you call Where (and various other LINQ commands) on IQueryable<T>. I assume you are using EF and as such the Where query would not execute immediately (as EF uses delayed execution). So basically you are passing your view a query which has yet to be run and only at the point of where the view attempts to render the data is when the query will run - by which time your ApartmentContext will have been disposed and as a result throw an exception.
db.Apartments.Where(...).ToList();
This causes the query to execute immediately and means your query no longer relys on the context. However, it's still not the correct thing to do in MVC, the example I have provided is considered the recommended approach.
In our project, we will add a Data Access Layer instead of accessing Domain in controller. And return view model instead of Domain.
But your code, you only select the data you need not all the data.
If you open SQL Profiler you'll see that's a select statement with a where condition.
So if it's not a big project I think it's OK.
I can't see these tutorials but are you sure it's loading all the data? It looks like your using entity framework and entity framework uses Lazy laoding. And Lazy loading states:
With lazy loading enabled, related objects are loaded when they are
accessed through a navigation property.
So it might appear that your loading all the data but the data itself is only retrieved from SQL when you access the object itself.

Resources