I have my project using MVC and I've got my controller that instantiates a service, the service manages the repository and in the repository makes the CRUD operations. The problem is, once a I populate my grid control (Telerik) with the data from my service, if I make an update and I refresh the data, it appears the old data instead of the new one. I think it's a problem of the persistance of my context variable that requires to be disposed/instantiated but not quite sure about when and where (the Unit Of Work variable is located at the service).
<HttpPost()>
<GridAction()>
Function Edit(id As Integer, name As String, clientNo As String, image As String, unit As String) As ActionResult
Try
Dim org = Me._orgService.GetOrgById(id)
With org
.orgNAME = name
.orgCLIENTNO = clientNo
.orgIMAGE = image
.orgUNIT = unit
End With
TryUpdateModel(org)
Me._orgService.EditOrg(org)
Catch ex As Exception
'Log the error
ModelState.AddModelError("", MS_UNABLE_SAVE_CHANGES)
Response.StatusCode = 500
Return Content(String.Join("", (From state In ModelState Select state).SelectMany(Function(s) s.Value.Errors).Select(Function(e) e.ErrorMessage).ToArray()))
End Try
Return View(New GridModel(All()))
End Function
This is the service
Public Sub EditOrg(org As hdmtORG) Implements IOrgService.EditOrg
Me._context.OrgRepository.Edit(org)
Save()
End Sub
This is the repository (generic)
Public Overridable Sub Update(entity As TEntity) Implements IEntityRepository(Of TEntity).Edit
Me._objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified)
End Sub
Any idea?
Thanks so much.
Try to use <OutputCache(Duration:=0)> _
<OutputCache(Duration:=0)>
<HttpPost()>
<GridAction()>
Function Edit(id As Integer, name As String, clientNo As String, image As String, unit As String) As ActionResult
Related
I'm writing a simple ApiController for getting product stocks, but I'm having a strange issue. I get the data from a method that returns a System.Linq.IQueryable (In a library), but I can't apply any of the Linq methods, like Count or ToList(). The import directive is present and doesn't report any problem. Also intellisense shows Data as System.Linq.IQueryable.
The code:
Imports System.Web.Http
Imports System.Linq
Public Class ProductSearchController
Inherits ApiController
Public Function Get(...) As IQueryable
Dim nvc As NameValueCollection = HttpUtility.ParseQueryString(Request.RequestUri.Query)
Dim sEcho As String = nvc("sEcho").ToString()
Dim iDisplayStart As Integer = CType(nvc("iDisplayStart"), Integer)
'Signature of Stock: public IQueryable Stock(...)
Dim Data = New GenericSearches().Stock(...)
Dim Count As Integer = Data.Count() 'Error Here!
Dim result = New DataTableResult With {
.sEcho = sEcho,
.iTotalRecords = Count,
.iTotalDisplayRecords = Count,
.aaData = Data.ToList() 'Error Here!
}
Return result
End Function
End Class
Also, I noticed that error correction and intellisense asks me to choose those methods from a weird Devexpress library something like DevXpress.Data.Helpers.Linq.
The non-generic IQueryable interface doesn't have extension methods of Count and ToList(). Only the generic IQueryable<T> does.
If you can modify the library to return a suitable IQueryable<T>, I suggest you do so. If you can't, you may need to call Cast<T>() or OfType<T>() to create an appropriate IQueryable<T> with the right extension methods.
Ok i have a class, a dbml file and a form. My project is called "Demo Project"
Form - DriveTimeActiveVendorsFrom
dbml - Dataworld
class - tblDriveTimeActiveVendors
My form contains the following code
Imports System.Data.Linq.Mapping
Public Class DriveTimeActiveVendorsForm
Private dc As New DataworldDataContext.DataworldDataContext
Dim _insert As New tblDrivetimeActiveVendors
Private Sub insertVendor()
dc = New DataworldDataContext.DataworldDataContext
_insert.ASRVendor = TextBox1.Text
_insert.AddressLine1 = TextBox2.Text
_insert.City = TextBox3.Text
dc.tblDriveTimeActiveVendors.InsertOnSubmit(_insert)
End Sub
End Class
My class contains the following
Public Class tblDrivetimeActiveVendors
Private _ASRVendor As String = Nothing
Private _AddressLine1 As String = Nothing
Private _City As String = Nothing
Public Property ASRVendor() As String
Get
Return _ASRVendor
End Get
Set(ByVal value As String)
If value Is Nothing Then
_ASRVendor = Nothing
Else
_ASRVendor = value.Trim
End If
End Set
End Property
Public Property AddressLine1() As String
Get
Return _AddressLine1
End Get
Set(ByVal value As String)
If value Is Nothing Then
_AddressLine1 = Nothing
Else
_AddressLine1 = value.Trim
End If
End Set
End Property
Public Property City() As String
Get
Return _City
End Get
Set(ByVal value As String)
If value Is Nothing Then
_City = Nothing
Else
_City = value.Trim
End If
End Set
End Property
Im getting an error on the following line
dc.tblDriveTimeActiveVendors.InsertOnSubmit(_insert)
Its the "_insert" part and the error is
Value of type 'WindowsApplication1.tblDrivetimeActiveVendors' cannot be converted to 'WindowsApplication1.DataworldEntity.tblDriveTimeActiveVendor'. C:\Users\rmonzing\Documents\Visual Studio 2008\Projects\DemoProject\DemoProject\DriveTimeActiveVendorsForm.vb 13 53 DemoProject
What am i missing here? Thanks
The variable _insert is declared as an object of type tblDrivetimeActiveVendors (plural), but InsertOnSubmit is expecting an object of type `tblDrivetimeActiveVendor' (singular).
Somewhere in your code, something has already declared a class tblDrivetimeActiveVendor, which (presumably) contains properties that correspond to the columns in your database table. You need to use this object instead of the class that you posted above.
I've wired up the MvcMiniProfiler to my app, and it's reporting Duplicate Queries.
I've set a BreakPoint in my Repository
Public Function Read() As System.Linq.IQueryable(Of [Event]) Implements IEventRepository.Read
Dim events = (From e In dc.Events
Select e)
Return events.AsQueryable ''# BREAKPOINT HERE
End Function
And I've hit the page in question.
My code hits the Read() function twice through my service layer (this is by design since I can't figure out how to reduce the calls)
Dim eventcount = EventService.GetHotEventCount() ''# First Hit
Dim eventlist = EventService.GetHotEvents((page - 1) * 5) ''# Second Hit
Dim model As EventsIndexViewModel = New EventsIndexViewModel(eventlist, page, eventcount)
Return View("Index", model)
The EventService does a simple query against the IQueryable Read
Public Function GetHotEvents(ByVal skip As Integer) As List(Of Domain.Event) Implements IEventService.GetHotEvents
Return _EventRepository.Read() _
.Where(Function(e) e.EventDate >= Date.Today AndAlso
e.Region.Name = RegionName) _
.OrderByDescending(Function(e) (((e.TotalVotes) * 2) + e.Comments.Count)) _
.ThenBy(Function(e) e.EventDate) _
.Skip(skip) _
.Take(5) _
.ToList()
End Function
Unfortunately I can't figure out why MiniProfiler is saying there are 8 Duplicate queries (13 in total).
Revised
So it appears as though Sam has stated that I'm not pre-loading my relationships within my queries.
How do I appropriately pre-load relationships in Linq to SQL? Can anyone lend any advice?
Edit
Here's the ViewModel that's being created.
Public Class EventsIndexViewModel
Public Property Events As List(Of Domain.ViewModels.EventPreviewViewModel)
Public Property PageNumber As Integer
Public Property TotalEvents As Integer
Public Property MapEventsList As List(Of Domain.Pocos.MapPin)
Public Property JsonMapEventsList As String
Sub New()
End Sub
Sub New(ByVal eventlist As List(Of Domain.Event), ByVal page As Integer, ByVal eventcount As Integer)
_PageNumber = page
__TotalEvents = eventcount
Dim mel As New List(Of MapPin)
_Events = New List(Of Domain.ViewModels.EventPreviewViewModel)
For Each e In eventlist
_Events.Add(New Domain.ViewModels.EventPreviewViewModel(e))
mel.Add(New MapPin(e.Location.Latitude, e.Location.Longitude, e.Title, e.Location.Name, e.Location.Address))
Next
_MapEventsList = mel
_JsonMapEventsList = (New JavaScriptSerializer()).Serialize(mel)
End Sub
End Class
Edit - added screenshot
You basically have two options to avoid SELECT n+1 with LINQ to SQL:
1) Use DataLoadOptions - http://msdn.microsoft.com/en-us/library/system.data.linq.dataloadoptions.loadwith.aspx
DataLoadOptions enables you to specify per entity exactly that related tables should be eager-loaded. In your case, for the entity Event, you could specify LoadWith for both Comments and Locations. Whenever you load Events, Comments and Locations will then be preloaded.
The DataLoadOptions is a property you can set on the DataContext itself.
2) Use projection to fetch all the data you need in one specific query, instead of relying on lazy loading the related entities.
You have imposed a repository on top of your DataContext, so this might not be the approach you want to take, but:
Instead of selecting a list of Events and then using this entity's properties Comments and Locations, you could have your query return exactly what you need in a specific ViewModel class. LINQ to SQL would then fetch everything in a single SQL query.
I consider this the best approach if you don't absolutely NEED to abstract away the DataContext behind a repository interface. Even if you do, you could consider having the repository return View specific results, i.e.
dc.Events
.Where(something)
.Skip(something)
.Select(event => new EventViewModel
{
Event = event
Locations = event.Locations,
Comments = event.Comments
}
);
with EventViewModel being
public class EventViewModel
{
Event Event;
List<Location> Locations;
List<Comment> Comments;
}
you're going to want to .Include("Locations") and .Include("Comments") in the respective queries. I believe it goes before the .Where(), but I'm not positive about that.
I don't know if this is possible, I am pulling the names for TitleWindows from my database as strings.
Then from my main application I have to launch the TitleWindow. So in my function I need to convert the name of the TitleWindow which is a String to a Class, because the PopUpManager accepts a Class. Below is my code.
When launching my application and trying to launch the TitleWindow I am getting the error:
Implicit coercion of a value of type String to an unrelated type Class.
I don't want to hard code the name of my popUp in the PopUpManager, that is why I am doing it like this. Any way to work around this?
public function getScreen(screenName:String):void
{
var screen_Name:Class = new Class();
screen_Name = screenName;
var popUpWindow:TitleWindow = PopUpManager.createPopUp(this, screen_Name, false) as TitleWindow;
PopUpManager.centerPopUp(popUpWindow);
}
I have had to do something very similar recently. Here is the function I wrote to do it:
//You have to provice the package signature
private var viewPackage:String = "org.bishop";
//In my case, event.type is the name of a class
var className: String = viewPackage + "." + event.type;
try{
var classRef:Class = getDefinitionByName(className) as Class;
viewNavigator.pushView(classRef);
}
catch(e:ViewError){
trace(e.message);
logger.debug(e.message);
}
Note: for the class to be created correctly, you will need to include both an import statement:
import org.bishop.Login;
and also declare a variable of the class in the code as follows:
Login;
otherwise the classes will not be available to be created.
I’m trying to optimize my app, and I notice that one query is triggered multiple times without any apparent reason.
Is a MVC 3 App, razor and I’m using Linq and EF.
I have ViewModel class with a couple of properties.
One of these properties is the model for to view.
This is my controller (I omit all the others properties initialization):
public ActionResult companyDetail(Guid id)
{
companyDetailsViewModel myModel = new companyDetailsViewModel();
myModel.companyDetail = companiesRepository.getCompany(id);
return View(myModel);
}
This is my getCompany method:
public company getCompany(Guid id)
{
return db.companies.Single(c => c.id == id);
}
The view is too long to paste here, but is a simple view.
This is a part for example:
<div id="companyName">
<h2>
#Model.companyDetail.companyName
</h2>
</div>
<div id="companyInfoWapper">
<div class="companyInfo">
<h5>
industry: #Model.companyDetail.industry<br />
revenue: #String.Format("{0:C}", Model.companyDetail.revenue)
</h5>
</div>
</div>
I’m using AnjLab SQL Profiler to view the transactions..
When I call the view, the query it’s
called 3 times.
The Generated SQL is
the exact same on all 3.
The transaction ID is different, and also
the duration varies a little bit.
The rest are pretty much the same.
Any Idea what can be making this query to run multiple times?
Another Question!
Anyone know why db.companies.Single(c => c.id == id) ask for top 2? Like this:
SELECT TOP (2)
[Extent1].[id] AS [id], ….
Thanks in Advance!
Edgar.
Update!
The third call was my fault, and I fix it. However, I find this:
The application is Multi-language, so I write a class that implements Controller.
I trace the problem to this class. The query is triggered the second time at the end of the class when I call the Base:
base.Execute(requestContext);
and of course, the action is called again.
Any Idea how to prevent this?
Another Update!
Linkgoron ask why I call Base.Execute(), the answer is because of the localizedController implementation.
But his question make me think, and there is another part of the code:
public abstract class LocalizedControllerBase : Controller
{
public String LanguageCode { get; private set; }
private String defaultLanguage = "es";
private String supportedLanguages = "en|es|pt";
protected override void Execute(RequestContext requestContext)
{
if (requestContext.RouteData.Values["languageCode"] != null)
{
LanguageCode = requestContext.RouteData.Values["languageCode"].ToString().ToLower();
if (!supportedLanguages.ToLower().Contains(LanguageCode))
{
LanguageCode = defaultLanguage;
}
}
else {
LanguageCode = defaultLanguage;
}
System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CreateSpecificCulture(LanguageCode);
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
base.Execute(requestContext);
}
}
My controller are defined like this:
public class companiesController : LocalizedControllerBase
I put a break point in “Base.Execute” and another in the “return View(myModel)” in the controller.
When I call the view companyDetail, the first stop is in base.Execute, the second is in return view, but for some reason there is a third stop in Base.Execute and a fourth in Return View, and finally the view is render.
This is making me crazy!
Anyone know why db.companies.Single(c
=> c.id == id) ask for top 2? Like this:
SELECT TOP (2) [Extent1].[id] AS [id],
….
Single() throws an exception if there is not exactly one match - so the Linq to Entities provider translates that as a top 2 query which is enough data to make a decision - throw an exception if the query returns 2 results or none, return the only result otherwise.
This doesn't make sense. If the query is executed multiple times you must call GetCompany method multiple times. Once you call Single the query is executed and Company instance is materialized so using it multiple times in view will not cause new executions. Those another calls must be caused by different part of your code.
Btw. you can avoid them by using Find (in EF 4.1) or GetObjectByKey (in EFv1 and EFv4) instead of Single. Single always executes query in database whereas Find first checks if the entity with the same entity key was already loaded and returns the instance without executing db query:
This is code for DbContext API (EF 4.1):
public company getCompany(Guid id)
{
// Id must be primary key
return db.companies.Find(id);
}
Code for ObjectContext API is little bit complicated because you first have to build EntityKey which requires entity set name. Here I described full example which works with different key types and names.