Map extra column from stored procedure to Entity Framework code first model - linq

I am using Entity Framework code first with a generic repository pattern with ASP.NET MVC. I have two tables Category and Product.
My model class of product is like this
Public class Product
{
public int ProductID{get;set;}
Public int CategoryID{get;set;}
[ForeignKey("CategoryID")]
public virtual Category Category{get;set;}
[NotMapped]
public string CategoryName{get;set;}
}
The model is binding correctly as long as I am getting data using DBContext.
But I am having a problem when I am getting list of products from stored procedure mapped to Product object. So it is not mapping the Category property of Product object and hence I cannot able to get Category.CategoryName.
So I added a new property with [NotMapped] attribute in product class as CategoryName. But it is also not binding from stored procedure.
And if I remove the [NotMapped] attribute then it is correctly binding from stored procedure but error occurs again when getting product by DbContext (Linq).
Please help me in this regards.

You don't need to add an extra property, use the DbSet.SqlQuery method for queries that return entity types. The returned objects must be of the type expected by the DbSet object, and they are automatically tracked by the database context unless you turn tracking off.
var products= _context.Products.SqlQuery("storedProcedureName",params);
The columns returned by SP should match the properties of your entity type otherwise, it will throw an exception.
After execute your SP, you should be able of get the CategoryName through your Category navigation property:
var catName=someProduct.Category.CategoryName;
On the other hand, the returned data by the Database.SqlQuery isn't tracked by the database context, even if you use this method to retrieve entity types. If you want to track the entities that you get after execute your SP using this method, you can try this:
//Attach the entity to the DbContext
_context.Product.Attach(someProduct);
//The Category navigation property will be lazy loaded
var catName=someProduct.Category.CategoryName;
If you have disabled lazy loading you can load explicitly your navigation property:
//Load the Category navigation property explicitly
_context.Entry(someProduct).Reference(c => c.Category).Load();

Related

How can I create a LINQ view?

My team is using Entity Framework 4.3.0 - Code Only with POCO classes as our ORM. Right now we use DBSets of Classes to access our 'tables'
Public Property Customers As DbSet(Of Customers)
But often we are doing soft deletes based on a IsDeleted column in LINQ, and filtering our select statements accordingly:
Dim LiveCustomers =
From C In EM.Customers
Where C.DeleteFlag = False
What I would really like to do is, instead of writing every query to include this filter, create some lower level property (possibly at our inherited DbContext level) that provides the filtered set, while maintaining strong type.
I tried this:
Public Property Customers As DbSet(Of Customer)
Public Property Customers_Live As DbSet(Of Customer)
Get
Return From C In Customers
Where C.DeleteFlag = False
End Get
Set(value As DbSet(Of Customer))
Customers = value
End Set
End Property
However that yielded me this error:
Multiple object sets per type are not supported. The object sets 'Customers' and 'Customers_Live' can both contain instances of type '__.Customer'.
A quick check on google yielded this promising result (How to: Query Objects with Multiple Entity Sets per Type) But after updating my Connection String, I'm still getting the same error.
<add name="EntityManager"
providerName="System.Data.SqlClient"
connectionString="
Data Source=xxxxxx;
Initial Catalog=xxxxxx;
User Id=xxxxxx;
Password=xxxxxx;
MultipleActiveResultSets=True"/>
So my question is, how could I effectively create a LINQ view that allows me to apply filtering, without impacting the upstream usage too drastically?
Change your property like this:
Public Property Customers As DbSet(Of Customer)
Public Property Customers_Live As IQueryable(Of Customer)
Get
Return From C In Customers
Where C.DeleteFlag = False
End Get
End Property
This is slightly different, as you won't have things like Add() or Remove(), but for a view you typically wouldn't expect to have that kind of functionality. If you want to add a new one, or remove one you should use the normal Customers property.
You could have your POCO classes inherit from a new class that has a new method that would do the filtering for you. Add something like this to the new class
--PSEUDO CODE!--
Public Function Filtered() as IEnumerable(Of Out T)
Return (From x In Me Where x.DeleteFlag).ToList()
End Function
and you could call it like:
Dim LiveCustomers =
From C In EM.Customers.Filtered
Or you could create an Interface and do a dependancy injection when you call your linq query. You'll have to google that one :)

Few questions... ModelState.IsValid and Grouped CheckBox Values

Using ASP.NET MVC when I create my model, then a controller based on the model with CRUD operations, the CRUD views are generated. I added some code using Fluent API to require certain fields but for some reason the ModelState.IsValid passes even when these fields are not completed. What determines whether this passes or not? I thought it was based on your model property data types and other things like being required or maxlength, etc....
Also, I have manually added code to grab a list of Categories from the database and generate a checkbox for each one in the View. This is a navigation property for the Project model where there is a many-many relationship. To get the group of checked values in the Create(Project project) method in the controller I use:
var selected = Request["categories"].Split(',');
This however, throws the classic Object reference not set to an instance of an object error if no values are checked. So what I want to know is, how can I determine that this does not have any values so I can do something else once detected?
I added some code using Fluent API to require certain fields but for
some reason the ModelState.IsValid passes even when these fields are
not completed.
ASP.NET MVC doesn't know anything about the Fluent API of Entity Framework and doesn't evaluate this configuration. You only can use the data annotations which MVC will recognize:
[Required]
public string SomeProperty { get; set; }
...how can I determine that this does not have any values so I can do
something else once detected?
Not sure if I understand it correctly but I'd say:
var categories = Request["categories"];
if (categories != null)
{
var selected = categories.Split(',');
// ...
}
else
{
// do something else
}

ASP.Net MVC 3, Complex Objects and Lazy Loading

First of all, I am new to ASP.Net MVC 3, and I am also using EF 4.1.
I have a complex object, something similar to let's say a Product object containing a Category object. So we have Product.CategoryId, Product.Category and some extra properties. I also have a form to create products with a dropdown list to select the category.
In my controller, after the product has been created, I need to have access to some property of the Category to perform some extra stuff. However, although Product.CategoryId is set, I cannot access Product.Category.SomeProperty because Product.Category is null. I expected Product.Category would be loaded automatically using some lazy loading, but it does not seem to be.
The code in my Controller looks like this:
[HttpPost]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
string someString = product.Category.SomeProperty;
...
Now, this does not work because product.Category is null. What do I need to add so that I can access SomeProperty?
Lazy loading will not work in this scenario because you are adding a new object. Lazy loading will work on "Proxy" entities created by EF context.
So what you can do here is explicitly load the navigational property.
db.Products.Add(product);
db.SaveChanges();
db.Entry(product).Reference(p => p.Category).Load();
string someString = product.Category.SomeProperty;
Lazy loading doesn't work in your case because the product which is passed in into the controller action is not a proxy object but created as an ordinary Product instance by the model binder.
What you expect would work if product is created as a proxy:
var product = db.Products.Create();
product.CategoryId = 1;
db.Products.Add(product);
db.SaveChanges();
string someString = product.Category.SomeProperty;
// Category gets lazily loaded now
The Category property on the Product class must be virtual of course to have lazy loading working at all.
It doesn't help you in your situation because the model binder doesn't create a proxy.
Solutions: Either explicite loading (see #Eranga's answer) or in case you really only need to inspect the SomeProperty of the category fetch the value in a projection:
string someString = db.Entry(product).Reference(p => p.Category).Query()
.Select(c => c.SomeProperty).SingleOrDefault();
...or (because you have the key of the category)...
string someString = db.Categories.Where(c => c.Id == product.CategoryId)
.Select(c => c.SomeProperty).SingleOrDefault();
You may need to explicitly enable lazy loading in your entity framework object context, as described in the MSDN article How to: Use Lazy Loading to Load Related Objects
In the Entity Framework runtime, the default value of the
LazyLoadingEnabled property in an instance of ObjectContext is false.
db.ContextOptions.LazyLoadingEnabled = true;
More detail is provided in the Loading Related Objects article, just look in the section labeled "Lazily Loading Entity Objects".

Handle GUIDs in ASP.Net MVC 3

I have a bunch of tables in my database that have a column which is of type GUID. I do not want this to be a part of scaffolding when i generate my views, so i am using [ScaffoldColumn(false)]. I don't want the user to enter an actual GUID when "creating" a new entity. Is there any way to automate the process of automatically adding a new GUID (thru data annotations or some other way??) when a user creates a new Entity or do i have to modify the controller for every table that has a GUID so that every time a user creates a new entry i make sure i generate a new GUID before saving it?
Thanks.
This is where (IMHO) the new scaffolding features encourages bad code.
You should define separate ViewModels for your views. These contain only the fields you wish the user to change along with any view specific validation/rules.
Within your controller action, you can check whether the user input is valid (ModelState.IsValid) and if so, do some lefty-righty mapping between your ViewModel properties and those of your entity.
See ViewModel Best Practices for more details.
However, to answer your question, you can use the HiddenInput attribute on an Id field so that it is rendered as a <input type="hidden"...
With regard with setting the Id, create a base class for your entities e.g:
public abstract class BaseEntity {
public Guid Id {get;set;}
public BaseEntity() {
Id = Guid.NewGuid();
}
}
You can then inherit your entities from this class and when you call new MyEntity() the base class constructor will be invoked, thus setting the Id.

linq to sql OnLoaded() with SQL View?

I am trying to extend my Linq-to-Sql entity with a few extra properties. These are "calculated" properties based on data from the underlying SQL View. For example, think of having a Date of Birth field, which is used to calculate an extended Age field.
I tried to extend my entity class by extending the OnLoaded() method.
I get a compile time error however stating that I cannot create it. I checked the designer code for my LTS entity class, and it doesn't have a partial definition for any of the expected extension points.
I checked a few of my other LTS entity classes and they do have these extension points. The only difference I see is that the one without is loaded from a SQL View, rather than a table. Is there a way to hook into a "Loaded" event when loading from a SQL View?
TIA!
I found that I did not have a PrimaryKey specified for my Linq-to-Sql entity class. I believe without a Primary Key specified, no extension methods generated in the entity class. Once I specified a Primary Key on my LTS entity class definition (through the designer), I was able to extend the OnLoaded() event.
You can do this by means of a property. Just create a partial class with the same name as your entity. Any properties or methods that you add will automatically be part of the entity and allow to use any of its members.
Here's an example of the pattern:
public partial class [The Name of the Entity]
{
public int Age
{
get
{
return CalculateAge(this.DateOfBirth);
}
}
}
Here's some logic on how to calculate the Age (Source: Geekpedia)
public static int CalculateAge(DateTime BirthDate)
{
int YearsPassed = DateTime.Now.Year - BirthDate.Year;
// Are we before the birth date this year? If so subtract one year from the mix
if (DateTime.Now.Month < BirthDate.Month ||
(DateTime.Now.Month == BirthDate.Month && DateTime.Now.Day < BirthDate.Day))
{
YearsPassed--;
}
return YearsPassed;
}

Resources