Help with IQueryables and lazyLoading in asp.net mvc - asp.net-mvc

Say i want to add pagination support.
My app is separated in Web, Services and repositories.
The Controller only speaks to the Service and the Service gets Data and does business logic.
Say i want pagination support.. I have this method in my ContributorService
public IQueryable<Contributor> GetContributors(int page, int pageSize)
{
return _repository.GetAll().OrderBy(c=>c.ACC_CREATEDATE).Skip((page)*pageSize).Take(pageSize);//solo temporalmente
}
is that ok?? or should the OrderBy, Skip, Take be done in the repository?
which currently only does this
public IQueryable<Contributor> GetAll()
{
return db.Contributors;
}

I would add the query to the business object ( I think you dont have one, do you), there you may have a base version of it, and another one for the paged data. And would expect to service to execute that query by calling the ToList, I find it dangerous to return a query object to the controller.
Your repository might have a GetPartial Method, with the sort, from, to, and a filter params. If you have a generic service, you might also implement this in that generic service.
public List<Contributor> GetPartial<TSortBy>(Expression<Func<Contributor, TSortBy>> sortByExpr, Expression<Func<Contributor, bool>> filterExpr, int pageNo, int pageSize)
{
var query = db.Contributors;
if (filterExpr != null)
query.Where(filterExpr);
query.orderBy(sortByExpr).Skip (...).Take(...).ToList();
}
if you have a Repository class, you can add this method.
By the way, I am using Dynamic LINQ, which makes it easier to pass order by expression as plain text (just like sql)

Related

Multiple entities with one web api controller

I am building a mvc based website with ef6 code first type db. Right now i have a web api controller named Categories Controller that works nicely and shells out json data to my js requests.
// GET: api/Categories
public IQueryable<Category> GetCategories()
{
return db.Categories;
}
Now I need to wire up same for products, materials and some other entities. What I would like is something like :
//GET: api/Data/Categories
//GET: api/Data/Products
etc. all wired into one DataController. Is there a way to accomplish this?
For example one Data Controller, with separate region of code for all category specific api actions, product specific api actions and so on. Then I could do /api/Data/Categories/Create or api/Data/Products/Create
Instead of bringing in a new technology, you could handle this by creating a new class that contains both. I am assuming there is no relation between them, such as a Product has Categories, but rather you want both exclusively.
// GET: api/Data/ProductCategories
public IQueryable<ProductsCategory> GetProductsCategories()
{
return GetProductsCategories();
}
...
public class ProductsCategory
{
public IEnumerable<Category> Categories { get; set;}
public IEnumerable<Product> Products{ get; set;}
}
...
public ProductsCategory GetProductsCategories()
{
var products = db.Products.ToList();
var categories = db.Categories.ToList();
var productCategories = new ProductsCategory()
{
Products = products,
Categories = categories
};
return productCategories;
}
Or something to that degree.
Also don't return IQueryable directly, it's redundant and ill advised unless the caller of that API is going to somehow be executing some Query against what has been returned which is unlikey seeing as its WebAPI and not some method. Instead return a List or IEnumerable.
And if you are looking to improve what you have a little bit as well, be sure to wrap that repository that you have in some sort of service, so you can say something along the lines of:
productsService.GetProductsCategories()
Rather than accessing the context directly in your API.
Extended Reading for Repositories and Service Layers
https://codereview.stackexchange.com/questions/33109/repository-service-design-pattern
Difference between Repository and Service Layer?
The easiest way to do what you want is to implement an OData controller, or a Breeze controller. They will do all the heavylifting to expose your EF model to in Web APi endpoints:
OData
Breeze
They're wuite easy to setup and OData is a recognized standard for this kind of task. They both had prebuilt support for oldering, filtering, paging, including related conllections and so. There are Nuget packages to use both of them.
Breeze also has feature-rich clients for JS and C#, and a lot of extra functionality.

How can I set a maximum row count for IQueryable

I am missing two topics in the Breeze documentation, Security and how to set limits on the data returned.
I don't want someone to delete everything in the database and don't want to return everything.
Should I use OData? with Odata I can set the restrictions I want.
"Security" is a huge, sprawling topic about which there is never enough to say. I hope to chip away at it.
As for limiting the max records, I'd use the [Queryable] attribute's PageSize option. The [BreezeQueryable] attribute inherits this option so you could write your Web API controller method or supporting repository method like this:
[HttpGet]
[BreezeQueryable(PageSize=100)] // returns a maximum of 100 orders
public IQueryable Orders() {
return ContextProvider.Context.Orders;
}
You might also be able to limit the results by adding top to the query inside the method.
[HttpGet]
public IQueryable Orders() {
return ContextProvider.Context.Orders.take(100);
}
However, certain query conditions may not get through. Try it first.

MVC, Repository Pattern and DataLoadOptions

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
}
}

IMultipleResults Using Custom Model & Repository

I'm following Steve Sanderson's example from this ASP.NET MVC book on creating a model by hand instead of using diagramming tools to do it for me. So in my model namespace I place a class called MySystemModel with something like the following in it
[Table(Name="tblCC_Business")]
public class Business
{
[Column(IsPrimaryKey=true, IsDbGenerated=false)]
public string BusinessID { get; set; }
// this is done because Business column and Business have interfering names
[Column(Name="Business")] public string BusinessCol { get; set; }
}
This part of it is all fine. The problem however is returning multiple result sets from a stored procedure, but mixing and matching SQL with LINQ modelling. We do this because the LINQ to SQL translation is too slow for some of our queries (there's really no point arguing this point here, it's a business requirement). So basically I use actual SQL statements along with my LINQ models in my "repository" like so:
public IEnumerable<MyType> ListData(int? arg)
{
string query = "SELECT * FROM MyTable WHERE argument = {0}";
return _dc.ExecuteQuery<MyType>(query, arg);
//c.GetTable<MyType>(); <-- this is another way of getting all data out quickly
}
Now the problem I'm having is how to return multiple result sets as I'm not extending DataContext, like so:
public ContractsControlRepository()
{
_dc = new DataContext(ConfigurationManager.ConnectionStrings["MyConnectionString"].ToString());
}
This link describes how multiple result sets are returned from stored procedures.
[Function(Name="dbo.VariableResultShapes")]
[ResultType(typeof(VariableResultShapesResult1))]
[ResultType(typeof(VariableResultShapesResult2))]
public IMultipleResults VariableResultShapes([Parameter(DbType="Int")] System.Nullable<int> shape)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), shape);
return ((IMultipleResults)(result.ReturnValue));
}
So how do I turn this into something that can be used by my repository? I just need to be able to return multiple result sets from a repository which contains DataContext, and doesn't extend it. If you copied and pasted the previous extract into a repository like I've got it will just state how ExecuteMethodCall isn't available, but that's only available if you extend DataContext.
Resources
Guy Berstein's Blog
Every time I ask a question that has been hindering me for days on end I end up finding the answer within minutes. Anyway, the answer to this issue is that you have to extend DataContext in your repository. If like me you're worried about having to specify the connection string in every single controller then you can change the constructor in the repository class to something like this:
public ContractsControlRepository()
: base(ConfigurationManager.ConnectionStrings["AccountsConnectionString"].ToString()) { }
This way when you instantiate your repository the connection is set up for you already, which gives you less to worry about, and actually centralizes specifying the connection string. Extending DataContext also means you have access to all of the protected methods such as ExecuteMethodCall used for calling stored procedures and bringing back, if you will, multiple result sets.

Implementing pager through WCF service

I am developing an application which includes a WCF service and its ASP.NET MVC client. The ASP.NET MVC website must display a grid of objects - say, products. These products are stored in database which is accessible through the WCF service. So somewhere inside an MVC controller I call WCF service's method that returns me an array of products that I need to display.
So what is my question? I want to implement a pager functionality for my products grid, because it is possible that there will be a lot of products. So there are several ways to do that:
My controller can get the whole list of products and just do in-memory paging
WCF can select all the products and store them somewhere in its cache, then pass to the controller only part of them, according to the requested page number.
WCF can select only part of the products from the database, according to the requested page number.
WCF can return IQueryable to the controller, and then the controller will select whatever he wants whenever he wants.
As far as I understand (and correct me if it is not true), the first option is useless, so I must choose between the others.
The second option wastes my server's memory.
The third option is OK, but it seems a little bit ugly to implement paging on the WCF side.
And the fourth option sounds confusing. I actually pass some kind of query to the client, and then he queries my database by himself, through the WCF service. I can't figure out how to implement this correctly.
So can you please help me to choose the correct way to implement this?
What is your back-end database layer look like? If you're using LINQ (-to-SQL or -to-Entities), you could implement paging through WCF by specifying the page size and the page number you want, and then use LINQ's "Skip" and "Take" operators to fetch the page requested - something roughly like:
[ServiceContract]
public interface IFetchData
{
[OperationContract]
public List<Data> GetData(int pageSize, int pageNumber)
}
and then implement it something like this (simplified):
public class FetchDataService : IFetchData
{
public List<Data> GetData(int pageSize, int pageNumber)
{
var query = yourContext.DataTable
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize);
return query.ToList();
}
}
Would that be helpful for you??
Marc

Resources