Linq to SQL DataContext Lifetime Management Issue - c#

I read Rick Strahl's article about ways to deal with the data context. My DBML is inside a class library, I keep my data context open by creating a static Current method in a separate custom partial class within library.
public partial class DataContext
{
public static DataContext Current
{
get
{
DataContext dc = HttpContext.Current.Items["dc"] as DataContext;
if (dc == null)
{
dc = new ImmediacyPageDataContext();
HttpContext.Current.Items["dc"] = dc;
}
return dc;
}
}
then access it like this
DataContext dc = DataContext.Current;
However, this causes issues whenever I update my DBML file. After editing the DBML file whenever I try and build the project my designer file doesn't regenerate/gets deleted. If I try running the custom tool option it comes back with an error.
The only way I can get round this is by renaming or deleting the custom partial class, re-generating the designer file, then adding my custom partial class back into the solution. This is a does work, but.. its a bit of a pain.
Is there a better approach to take that will make editing my DBML files easier, while prolonging my DC for as long as possible ?

Go into the code file with your partial DataContext class and move your using statements into your namespace. For some reason the tool will not generate the designer unless this is the case.
namespace MyNamespace
{
using System;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Reflection;
using System.Xml.Linq;
partial class DataContext
{
}
}
I believe this change was required when moving from VS2008 to VS2008 SP1, though I may be mixing up some versions.

You should create your partial class in a different file, not the .designer.cs file. The easiest way to do this is to right-click on your DBML in the solution explorer (or in a blank area in the DBML designer) and click "View Code". This will create a new .cs file that won't be overwritten when you save your DBML.

I don't believe your DataContext persistence and DBML issue are related. It sounds like the IDE is confused versus a conflict with a Cached DataContext (HttpContext.Current.Items is per Request so there's no long-term caching anyway).
I had trouble with DBML compilation when my data model contained a class name that conflicted with another class. For instance, a DBML object named 'Application' (an insurance application) might conflict with HttpApplicationState.Page.Application.
Check your error message and see if it's specific to a name in your DBML.

I can't think of an overly compelling reason why your new static property must be part of the DataContext class. It would be just as easy to have it in a different class.

Related

Unable to reference a design-time ViewModel in Xaml

I've done this before, but not for some time and clearly I'm missing something.
In short, I've got a design-time model that inherits from my real view model and sets some properties in the constructor.
I'm importing the namespace in Xaml and IntelliSense is suggesting the desired class name, which leads me to believe my naming is error-free.
Even after a clean/build, I'm getting an error saying that the referenced model doesn't exist.
I can refer to the model from a .cs using Aurora.UI.ViewModels.SecurityViewModel_DesignTime without issue.
(In case it matters, this project has a target of x64. This is due to a dependency on an bit-dependent library. It's the only real difference I can point to when comparing to previous implementations).
The name "SecurityViewModel_DesignTime" does not exist in the namespace "clr-namespace:Aurora.UI.ViewModels"
And the model itself:
namespace Aurora.UI.ViewModels {
public class SecurityViewModel_DesignTime : SecurityViewModel {
public SecurityViewModel_DesignTime() {
this.Sensor = new Peripherals.Kinect();
this.PrimaryFeed = Kinect.Feed.Infrared;
Feeds = new List<Kinect.Feed> {Kinect.Feed.Infrared};
this.LookDirections =
Peripherals.Kinect.DirectionsRequired.Front |
Peripherals.Kinect.DirectionsRequired.Left |
Peripherals.Kinect.DirectionsRequired.Right |
Peripherals.Kinect.DirectionsRequired.Top;
}
}
}
(The class it's inheriting from is the 'real' viewModel and is a simple POCO)
What am I missing?
As per the comment, here's an answer:
Do a solution clean, and restart visual studio. Goodness knows why it works. The designer is janky at the best of times.

MVC scaffolding is duplicating my model fields

I seem to be running into a weird issue and after hours of head scratching, I seem to have narrowed the issue down to a combination of partial classes and virtual properties. When I override a property that's in a partial class, sitting in a separate file, MVC duplicates the fields on my view. I am using Visual Studio 2013 and the issue can be duplicated by following these steps:
Open Visual Studio and create a new Project. Choose Web under the categories, then choose "ASP.NET Web Application". I am targeting .NET 4.5.
Choose "Empty" from the template selection, then check the MVC checkbox so it adds the core folders and references.
Once the project is created, right-click on the Models folder and create a new class called MyModel.cs.
Add these lines to the new file:
public abstract partial class MyOriginalModel
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public partial class MyModel : MyOriginalModel
{
}
Now right click on the Models folder again and create another new class called MyModelCustom.cs.
Add these lines to the file:
public partial class MyModel
{
[System.ComponentModel.DisplayName("First Name")]
[System.ComponentModel.DataAnnotations.Required]
public override string FirstName
{
get
{
return base.FirstName;
}
set
{
base.FirstName = value;
}
}
[System.ComponentModel.DisplayName("Last Name")]
[System.ComponentModel.DataAnnotations.Required]
public override string LastName
{
get
{
return base.LastName;
}
set
{
base.LastName = value;
}
}
}
Now build the project, then right click on the Controllers folder and add a new controller. Choose "MVC 5 Controller with read/write actions" and call it NamesController. Right click on the Create method and go to "Add View". Under the template dropdown, choose Create and for the Model Class dropdown, choose MyModel.
Once MVC creates the template, you will see that it adds First Name and Last Name twice. The issue seems to be related to partial classes because if I move the contents of MyModelCustom.cs into MyModel.cs, everything works fine. However, its not just partial classes. If I create a new property (versus overloading one) in the partial class, it does not duplicate that property. So it seems to be a combination of partial classes and overriding virtual properties.
Can someone please confirm if this is a bug or if I am doing something wrong?
It is a bit of both. Bug or not, if MVC is scaffolding incorrectly, you will either have to constantly fight the framework or change your approach to the problem.
As a general rule, I've found that when you have to fight the MVC framework to make it behave the way you want, then it is far easier to change your approach to the problem. Otherwise, you will end up fighting that particular battle repeatedly until you eventually comply. Take it from someone who's learned that lesson the hard way.
With easier approaches in mind, here are a few things you could try instead:
If you are overwriting a lot of properties, create separate classes with common names for properties (FirstName, LastName). Then use Best way to clone properties of disparate objects to marshall the data between objects.
You could also use Fody PropertyChange listeners to handle whatever logic is needed when these values are changed thereby eliminating the need for the partial overrides entirely.
A final option would be to override the scaffolding templates to skip overridden properties. Not sure how you would detect that though.
Take a look at CodePlex source for MvcScaffolding EnvDTETypeLocator.cs
/// <summary>
/// Out of a set of CodeType instances, some of them may be different partials of the same class.
/// This method filters down such a set so that you get only one partial per class.
/// </summary>
private static List<CodeType> PickArbitraryRepresentativeOfPartialClasses(IEnumerable<CodeType> codeTypes)
{
var representatives = new List<CodeType>();
foreach (var codeType in codeTypes) {
var codeClass2 = codeType as CodeClass2;
if (codeClass2 != null) {
var matchesExistingRepresentative = (from candidate in representatives.OfType<CodeClass2>()
let candidatePartials = candidate.PartialClasses.OfType<CodeClass2>()
where candidatePartials.Contains(codeClass2)
select candidate).Any();
if (!matchesExistingRepresentative)
representatives.Add(codeType);
} else {
// Can't have partials because it's not a CodeClass2, so it can't clash with others
representatives.Add(codeType);
}
}
return representatives;
}
}
:
:
1) PickArbitraryRepresentativeOfPartialClasses, the method uses Linq any() to confirm that the codeType as CodeClass2 has members.
CodeClass2 is the partial class type of EnvDTE, Visual Studio's core Automation library responsible for IDE code generation (Design Time Reflection).
2) If the class cast as CodeClass2 does have members, the class is added to the representatives
3) When the partial class is evaluated, each file will be visited within a distinct context (often leading to a consolidation of elements that should be overridden)
An interesting distinction between Run Time Reflection and Design Time Reflection: sic
An ASP.NET control has two distinct sets of functionality for when it is executed at run-time inside a page or used at design-time inside a host designer. Run-time capabilities determine, based on configuration, the markup that is output by the control. The design-time capabilities, instead, benefit of a visual designer such as Microsoft Visual Studio 2005. Design-time capabilities let the page author configure the control for run-time in a declarative and WYSIWYG (what-you-see-is-what-you-get) manner.
Conclusion:
MVC Scaffolding does use reflection, but it is the much less reliable Design Time Reflection.
Design Time Reflection is not the same as Run Time reflection. A fully compiled Class is a final result of inheritance resolves and partials combined and priority resolved. Design Time Reflection makes best guesses about how to work with complex, multi-part types.
If you want to rely on Scaffolding, better not to push it's limits. When you get errors like this, try simplifying your ViewModels:
Try consolidating your partial classes
Try removing your abstracts / virtuals

Find All Uses of a Specific Reference

I currently have a website, whose code behind references some functions within another project within the same solution. I have been tasked with pulling out this website and its existing functionality and putting it into its own solution.
Currently the code behind looks similar to this:
using BusinessObjectsProject;
namespace BatchProject.Web
{
public partial class AutoShoppingCart : System.Web.UI.Page
{
[WebMethod]
public static BatchProject LoadProjectDetails(string projectId)
{
return BusinessObjectsProject.BatchBo.ReadBatchProject(ConvertHelper.SafeConvertInt32(projectId));
}
}
}
However there are many (more than 20) functions that make use of the BusinessObjectsProject reference.
Is there a simple way to list the BusinessObjectsProject functions in the AutoShoppingCart class? I want to ensure that I copy over everything to ensure existing functionality.
If you're using Visual Studio, simply right click BusinessObjectsProject and select Find All References (or press Shift + F12).
The solution suggested by Blorgbeard is also useful, just remove/rename the class and see what breaks.

Error when trying to add MVC 4 controller: 'EntityClass' is not part of of the specified 'ContextClass' class

So I'm working on a practice project using Visual Studio 2012, MVC 4, and Entity Framework 5.0. I create an MVC 4 project using the "Basic" template. I add a simple Home/Index controller and view just to make sure everything is working. It is.
I add an edmx data model to my project under the models folder. (Connection strings are tested and good). I generate this model from a fairly simple SQL Express database. Here's some details about the model (generic names substituted):
Model edmx file structure looks like this in Solution Explorer:
[edmxFile] MyProjectModel.edmx
[SubFile] MyProjectModel.Context.cs
[Class] MyProjectEntities
[Method] MyProjectEntities()
[Method] OnModelCreating()
[Property] EntityAs: DbSet<EntityA>
[Property] EntityBs: DbSet<EntityB>
[Property] EntityCs: DbSet<EntityC>
etc...
[SubFile] MyProjectModel.Designer.cs
[SubFile] MyProjectModel.edmx.diagram
[SubFile] MyProjectModel.tt
[ClassFile] EntityA.cs
[ClassFile] EntityB.cs
[ClassFile] EntityC.cs
etc...
[ClassFile] MyProjectModel.cs
So I Rebuild my solution, then right click the "Controllers" folder and choose "Add"->"Controller" and choose the following options:
Controller name: "EntityAsController".
Scaffolding options:
Template: MVC controller with read/write actions and views, using Entity Framework
Model class: EntityA (MyProject.Models)
Data context class: MyProjectEntities (MyProject.Models)
Views: Razor (CSHTML)
Then I click "Add". I get the following error (again, generic names substituted):
'MyProject.Models.EntityA' is not part of the specified 'MyProject.Models.MyProjectEntities' class, and the 'MyProject.Models.MyProjectEntities' class could not be modified to add a 'DbSet<MyProject.Models.EntityA>' property to it. (For example, the 'MyProject.Models.MyProjectEntities' class might be in a compiled assembly.)
I originally got this error when I tried putting the edmx in a data layer project of its own and referencing that project from the main project. To solve,
I tried moving it to the main project. No luck.
I tried recreating the entire solution from scratch and creating it in the main project from the start. Same error.
I read and tried monkeying around with just about every solution or suggestion in this question/answer(s)/comment(s): Entity Framework MVC Controller no success.
I tried extending MyProjectEntities with a partial class and adding a wrapper property in order to FORCE it to have the desired "EntityA" property, like so:
public DbSet<EntityA> EntityA {
get { return this.EntityAs; }
set { this.EntityAs = value; }
}
All that did was give me an even more unhelpful error:
There was an error generating 'MyProject.Models.MyProjectEntities'. Try rebuilding your project.
I tried rebuilding the project. Same error.
Does anyone have any idea what's going on?
I found a solution to my own problem. It's a bit hacky, but it works.
The first error message above complains about "...class could not be modified to add a 'DbSet<MyProject.Models.EntityA>' property to it...". So I left my partial class for extending MyProject.Models.MyProjectEntities empty, like so:
public partial class MyProjectEntities : DbContext
{
}
Now, when I create the controller, MVC is of course still to stupid to see that the auto generated MyProjectEntities already contains the property it wants, BUT it now has a file for that class that it can modify to add a duplicate declaration of that property, modifying my code to look like this:
public partial class MyProjectEntities : DbContext
{
public DbSet<EntityA> EntityAs { get; set; }
}
It is happy, and makes my controller and views for me. Now, I just manually delete what it added to my code, so that there isn't an ambiguity error at build time. I build the project, start it, fire up my browser and go to "[siteUrl]/EntityAs", and TADA! It Works!
I hope that helps someone who has this problem. If anyone knows why MVC 4 and VS 2012 behave in this buggy way and require this hack solution, please comment or add another answer. It still bugs me a bit, and I'd love to hear more about it.

Extending System.Data.Linq.DataContext

I have a class reflecting my dbml file which extends DataContext, but for some strange reason it's telling me
System.Data.Linq.DataContext' does not contain a constructor that takes '0' arguments"
I've followed various tutorials on this and haven't encountered this problem, and VS doesn't seem to able to fix it.
Here's my implementation
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Reflection;
using System.Text;
using IntranetMvcAreas.Areas.Accounts.Models;
namespace IntranetMvcAreas
{
partial class ContractsControlDataContext : DataContext
{
[FunctionAttribute(Name="dbo.procCC_Contract_Select")]
[ResultType(typeof(Contract))]
[ResultType(typeof(ContractCostCentre))]
[ResultType(typeof(tblCC_Contract_Data_Terminal))]
[ResultType(typeof(tblCC_CDT_Data_Service))]
[ResultType(typeof(tblCC_Data_Service))]
public IMultipleResults procCC_Contract_Select(
[Parameter(Name = "ContractID", DbType = "Int")] System.Nullable<int> ContractID,
[Parameter(Name = "ResponsibilityKey", DbType = "Int")] System.Nullable<int> ResponsibilityKey,
[Parameter(Name = "ExpenseType", DbType = "Char")] System.Nullable<char> ExpenseType,
[Parameter(Name = "SupplierID", DbType = "Int")] System.Nullable<int> SupplierID)
{
IExecuteResult result = this.ExecuteMethodCall(this, (MethodInfo)(MethodInfo.GetCurrentMethod()), ContractID, ResponsibilityKey, ExpenseType, SupplierID);
return (IMultipleResults)result.ReturnValue;
}
}
}
And it's ContractsControlDataContext that's pointed at as the problem
(btw, this has no relation to a very recent post I made, it's just I'm working on the same thing)
EDIT
It's probably worth clarifying this, so please read very carefully.
If you do not extend DataContext in the partial class, then ExecuteMethodCall isn't accessible.
'Intranet.ContractsControlDataContext' does not contain a definition for 'ExecuteMethodCall' and no extension method 'ExecuteMethodCall' accepting a first argument of type 'Intranet.ContractsControlDataContext' could be found (are you missing a using directive or an assembly reference?)
Maybe I'm missing something incredibly stupid?
SOLVED
I think perhaps Visual Studio struggled here, but I've relied entirely on auto-generated code. When right clicking on the database modeling language design view and hitting "View Code" it automagically creates a partial class for you within a specific namespace, however, this namespace was wrong. If someone could clarify this for me I would be most appreciative.
The .designer.cs file sits in namespace Intranet.Areas.Accounts.Models, however the .cs file (partial class generated for the .designer.cs file by Visual Studio) was in namespace Intranet. Easy to spot for someone more experienced in this area than me.
The real problem now is, who's answer do I mark as correct? Because many of you contributed to finding this issue.
The object DataContext for linq does not have an empty constructor. Since it does not have an empty constructor you must pass one of the items it is excepting to the base.
From the MetaData for the DataContext.
// Summary:
// Initializes a new instance of the System.Data.Linq.DataContext class by referencing
// the connection used by the .NET Framework.
//
// Parameters:
// connection:
// The connection used by the .NET Framework.
public DataContext(IDbConnection connection);
//
// Summary:
// Initializes a new instance of the System.Data.Linq.DataContext class by referencing
// a file source.
//
// Parameters:
// fileOrServerOrConnection:
// This argument can be any one of the following: The name of a file where a
// SQL Server Express database resides. The name of a server where a database
// is present. In this case the provider uses the default database for a user.
// A complete connection string. LINQ to SQL just passes the string to the
// provider without modification.
public DataContext(string fileOrServerOrConnection);
//
// Summary:
// Initializes a new instance of the System.Data.Linq.DataContext class by referencing
// a connection and a mapping source.
//
// Parameters:
// connection:
// The connection used by the .NET Framework.
//
// mapping:
// The System.Data.Linq.Mapping.MappingSource.
public DataContext(IDbConnection connection, MappingSource mapping);
//
// Summary:
// Initializes a new instance of the System.Data.Linq.DataContext class by referencing
// a file source and a mapping source.
//
// Parameters:
// fileOrServerOrConnection:
// This argument can be any one of the following: The name of a file where a
// SQL Server Express database resides. The name of a server where a database
// is present. In this case the provider uses the default database for a user.
// A complete connection string. LINQ to SQL just passes the string to the
// provider without modification.
//
// mapping:
// The System.Data.Linq.Mapping.MappingSource.
public DataContext(string fileOrServerOrConnection, MappingSource mapping);
Something as simple as this would work. Any class that inherits from the DataConext must pass to the base constructor at least one of the types it is excepting.
public class SomeClass : System.Data.Linq.DataContext
{
public SomeClass(string connectionString)
:base(connectionString)
{
}
}
I'm assuming that the namespace and (data-context) type name are correct... double check that first.
It sounds to me like the codegen has failed, and so you only have your half of the data-context (not the half that the IDE is meant to provide). There is a known bug in LINQ-to-SQL where this can fail if (as in your case) the using declarations are above the namespace. No, I am not joking. Try changing the code:
namespace IntranetMvcAreas
{
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Reflection;
using System.Text;
using IntranetMvcAreas.Areas.Accounts.Models;
// the rest of your code
Now go into the designer, tweak something (for example, change the name of a property and change it back again) and hit save (this forces the codegen). Now see if it works.
David Basarab's answer is correct and should be marked as the answer.
Your class is not providing any constructor, so a default constructor is provided. Default constructors for derived classes can only be provided if the base class has a parameterless constructor. However, the DataContext class which is your base class in this example does not provide a parameterless constructor. This explains the error message the compiler returned to you.
Edit:
Example:
class A {
public A(string s) {
}
}
class B : A {
}
An attempt to compile that returns an error in class B:
'A' does not contain a constructor that takes '0' arguments
The behavior of the generator regarding constructors is controlled, to some extent, by the Connection properties of the DBML. If Application Settings is True, and there is a settings property name, it will generate a constructor that reads the connection string from the Application Settings of the assembly.
If there is a connection string, it will generate a constructor with a hardcoded connection string in the .designer.cs file.
If there is neither, it will not generate a constructor without a connection string parameter, and you can safely provide a parameter-less constructor in a partial class without causing a conflict.
These settings changes don't survive a round trip of the schema from the database, but I simply clear the connection settings from the properties after making changes and before I save the DBML.
#Sander: I think you were on the right track. Instead of using a partial class and implementing the function for the sproc I instead followed this blog and used the *.designer.cs file for implementing it. Whilst I'm still experiencing problems with invalid type casting it did get rid of the original problem.

Categories