asp.NET MVC 3 Composite pattern model binding - asp.net-mvc-3

My application has products and vendors and both of them have similar items in a "has a" relationship...in particular they have a "favorite" so users can bookmark them.
so we have:
public class Product
{
public int ProductId {get;set;}
public int Name {get;set;}
public List<Favorite> Favorites {get;set;}
}
public class Vendor
{
public int VendorId {get;set;}
public int Name {get;set;}
public List<Favorite> Favorites {get;set;}
}
public class Favorite
{
public int FavoriteId {get;set;}
public string UserName {get;set;}
}
At first this didn't work, so I added:
public int? VendorId {get;set;}
public int? ProductId {get;set;}
Now the issue I'm having is that my Vendor.Favorites and Product.Favorites are always null.
How do I bind these so that I can work with the objects like that? Do I not make it a separate entity?
Thanks!
UPDATE: I should note that I'm using MVC 3 Code-first with POCO.
UPDATE: Solution:
I don't think this is ideal, still working out the kinks with how I want this to work because It will add redundant code for adding favorites and reviews.
public class Product
{
public int ProductId {get;set;}
public int Name {get;set;}
public virtual List<Favorite> Favorites {get;set;}
}
public class Vendor
{
public int VendorId {get;set;}
public int Name {get;set;}
public virtual List<Favorite> Favorites {get;set;}
}
Having the raw nullable int variable in the favorites class makes it work, but if I wanted to use the favorites class with other objects I'll have to modify the favorites properties with a new mapping to the key of the object. Out of curiosity, when dealing with these classes normally, how do you manage the data storage for these objects? I'm assuming you deal with it at a DAL?

You could use inheritance, make a base class containing only Favorite then derive classes based on that for the other info. This establishes an "is a" relationship
Ex.
public class baseClass
{
public list<Favorite> Favorites { get; set;}
}
public class Product : base
{
public int ProductID { get; set; }
public string Name { get; set; }
}
Then a Product Object would have all 3 properties.

Based on the assumption that you're using EF 4.1:
Your model looks pretty good except I'd change
public int? VendorId {get;set;}
public int? ProductId {get;set;}
to
public virtual Vendor VendorId {get;set;}
public virtual Product ProductId {get;set;}
Then your FK relationships should be all set. By my understanding of EF, List<Favorite> is meant to establish a relationship and is not actually populated. You have to populate it yourself, either by calling .Include() on the context or by referencing the actual FK that EF creates in the database, which in your case would be VendorId_FavoriteId or ProductId_FavoriteId

I would keep one Property to store the ID's (either VendorID or ProductId) and use another property to tell me what type it is (Vendor or Product)
public class Favorite
{
public int FavoriteId {get;set;}
public string UserName {get;set;}
public int ItemID { set;get;} //can use a better generic name
public FavoriteType Type { set;get;}
}
I have an Enum for the Types
public enum FavoriteType
{
Vendor,
Product
}
And make sure you are initializing your sub properties before accessing them / applying a method on those so that It won't throw the null reference exception!. You can do it constructor itself
public class Product
{
public int ProductId {get;set;}
public int Name {get;set;}
public List<Favorite> Favorites {get;set;}
public Product()
{
if(Favourites==null)
Favorites=new List<Favorite>();
}
}

Related

Linq to Entities: How can i filter in the where clause on fields from a derived type (Table per Type inheritance)?

I am working on a risk monitoring application with a SQL DB in the background. I use EF 4 Database First. There is a "Position" class in the model, which models holdings in various types of assets. A position holds a reference to a "Contract" class. This "Contract" class serves as the base class for various types of financial contracts, such as "Investment".
The classes look as follows:
public class Position
{
public int ContractID {get;set;}
public DateTime PositionDate {get;set;}
public decimal MarketValue {get;set;}
}
public class Contract
{
public int ID {get;set;}
public string Description {get;set;}
public int CurrencyID {get;set;}
public Instrument Instruments {get;set;}
}
public class Investment:Contract
{
public int ID {get;set;}
public int BBGIndustryID {get;set;}
}
When i try to run a query and try to filter on properties from the "Investment" Class, the system throws an StackOverFlowException:
var dataPoint = _context.Positions.Where(c => c.Contract.Instruments != null).Where(c => c.Contract.Instruments.BBGIndustryID == 24).ToList();
How do i need to write the query that i can filter on properties from the derived classes, or do i need to move the properties i need to filter on to move to the base class?

How to access an association in view from one POCO with several references to another

Sorry about the title; couldn't think of a better one.
Any way, I'm accessing an associated property in my view like so:
#Model.Company.CompanyName // No problems here...
The model is a viewmodel mapped to an EF POCO. The Model has several properties associated to the Company table. Only one of the properties in the model share the same name as the PK in the Company table. All the other properties reference the same table:
public class MyModelClass
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AnotherCompanyId { get; set; } // References CompanyId
public int AndAnotherCompanyId { get; set; } // References CompanyId
public Company Company { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public string Address { get; set; }
}
I'm obviously missing something here.
How can I get the names of the other companies in my Model?
Any help is greatly appreciated.
The model is a viewmodel mapped to an EF POCO
I think you are confusing the notion of a view model. A view model is a class that is specifically designed to meet the requirements of your view. So if in your view you need to display the company name and not the company id then your view model should directly contain a CompanyName property. Or a reference to another view model (CompanyViewModel) which contains the name directly. It is then the responsibility of your controller action to query your domain models (EF entities) and aggregate them into a single view model tat will contain all the necessary information that the view requires.
Here's how a typical view model might look like:
public class MyViewModel
{
public CompanyViewModel Company { get; set; }
public CompanyViewModel AnotherCompany { get; set; }
public CompanyViewModel AndAnotherCompany { get; set; }
}
public class CompanyViewModel
{
public string Name { get; set; }
}
Where the data comes from in this view model is not important. You could have the Company property populated from your EF stuff, the AnotherCompany property populated from a XML file and AndAnotherCompany from WCF.

How to separate large table into multiple discrete types using EF-Code-First

I am trying to separate a large table into multiple discrete types.
I'm following the example here: http://weblogs.asp.net/manavi/archive/2011/04/24/associations-in-ef-4-1-code-first-part-4-table-splitting.aspx?CommentPosted=true#commentmessage
It's working for a primary type and a sub-type, but does not work when I employ multiple types. I received an error
The Entity types 'CampaginFeedback' and 'CampaignSurvey' cannot share table 'Campaign' because they are not int he same type hierarchy
or do not have a valid one to one foreign key relationship with
matching primary keys between them.
Here are simplified versions of my classes:
public class Campaign {
[Key]
public int CampaignId {get;set;}
public string Name {get;set;}
public virtual CampaignSurvey Survey {get;set;}
public virtual CampaignFeedback Feedback {get;set;}
}
public class CampaignSurvey {
[Key]
public int CampaignId {get;set;}
public string Question {get;set;}
public string Answer {get;set;}
}
public class CampaignFeedback {
[Key]
public int CampaignId {get;set;}
public string Feedback {get;set;}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Campaign>().HasRequired(c => c.Survey).WithRequiredPrincipal();
modelBuilder.Entity<Campaign>().HasRequired(c => c.Feedback).WithRequiredPrincipal();
modelBuilder.Entity<Campaign>().ToTable("Campaign");
modelBuilder.Entity<CampaignSurvey>().ToTable("Campaign");
modelBuilder.Entity<CampaignFeedback>().ToTable("Campaign");
}
Edit: Split table to more than two entities in code first is very problematic. It works without any problem when using EDMX.
To make it work you must ensure that each entity used to split table has valid one-to-one relation with all other entities used to split table. That also means spoiling your model with navigation properties and moreover ensuring that during save all navigation properties pointing to the same entity type reference the same instance (otherwise you will get exception during call to SaveChanges).
So the solution for your example should be something like:
public class Campaign {
[Key]
public int CampaignId {get;set;}
public string Name {get;set;}
public virtual CampaignSurvey Survey {get;set;}
public virtual CampaignFeedback Feedback {get;set;}
}
public class CampaignSurvey {
[Key]
public int CampaignId {get;set;}
public string Question {get;set;}
public string Answer {get;set;}
public virtual CampaignFeedback Feedback {get;set;}
}
public class CampaignFeedback {
[Key]
public int CampaignId {get;set;}
public string Feedback {get;set;}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Campaign>().HasRequired(c => c.Survey).WithRequiredPrincipal();
modelBuilder.Entity<Campaign>().HasRequired(c => c.Feedback).WithRequiredPrincipal();
modelBuilder.Entity<CampaignSurvey>().HasRequired(c => c.Feedback).WithRequiredPrincipal();
modelBuilder.Entity<Campaign>().ToTable("Campaign");
modelBuilder.Entity<CampaignSurvey>().ToTable("Campaign");
modelBuilder.Entity<CampaignFeedback>().ToTable("Campaign");
}
I'm even not sure how this will work in the real scenario. You can find some other problems when using it.
Something I have found that works is to create a view and point your additional entities to that.

Losing DataAnnotations when using POCO Entity Framework 4

I'm using the new EntityFramework 4.1 with POCO objects, in conjunction with the DataAnnotation framework.
When EntityFramework needs to create a proxy class (for example, lazy loading virtual properties), all of my data annotations are lost. Is there some way that my ModelMetaData can come from the class that was proxied, instead of the proxy class?
I know that I have the choice to disable proxy creating (context.Configuration.ProxyCreationEnabled) but this seems like a poor answer. This should be something that's been solved, I would think.
Here's some example code:
public class Person
{
[Required, Display(Name = "Display That Name")]
public string DisplayName { get; set; }
}
And then in my model metadata in the view, the type is: Person_9C92B92D56F6F4F0FB1686526D585438A05323CC72E457C2152796E8866297E1 (FullName = "System.Data.Entity.DynamicProxies.Person_9C92B92D56F6F4F0FB1686526D585438A05323CC72E457C2152796E8866297E1"}), my metadata is gone, and the displayname renders out at "DisplayName" not "Display That Name".
Any suggestions?
You could make a Metadata version of your model classes.
We do that thisway... what EF generate is never touched by hand.
Let said you have a Person class:
public partial class Person
{
public int idPerson { get; set; }
public int idTenant { get; set; }
public string Name { get; set; }
}
Then you could make the Metadata class, that holds the metadata and won't be overrided:
[MetadataType(typeof(PersonMD))]
public partial class Person
{
//leave it empty
}
public partial class PersonMD
{
[Required]
public int idPerson { get; set; }
[Required]
public int idTenant { get; set; }
[Required, Display(Name = "Display That Name")]
public string Name { get; set; }
}
What we do is as follows:
Modify the T4 templates to generate partial classes for your entities.
For those entities that you wish to add annotations to, create a partial class of the same name of your entity.
Create a buddy class within this class that provides your annotation details.
Apply the attribute at the top of the partial class to specify that your buddy class is where the annotation details can be found.
See here for more details http://msdn.microsoft.com/en-us/library/ee256141.aspx
I figured out one possible solution. Not sure if there are better ones. First I wrote a new ModelMetadataProvider:
public class IgnoreProxiesDataAnnotationsModelMetadataProvider : System.Web.Mvc.DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
modelType = ObjectContext.GetObjectType(modelType);
containerType = ObjectContext.GetObjectType(containerType);
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
}
}
And then registered it in Global.asax application start:
ModelMetadataProviders.Current = new IgnoreProxiesDataAnnotationsModelMetadataProvider();
If there's a better solution, please let me know!

How to update a property of an abstract with an inheriting/using a subblass in MVC

I have an abstract class
public abstract class Member
{
public string ID { get; set; }
public string Username { get; set; }
public int MemberType { get; set; }
public abstract string MemberName { get; set; }
public int Status { get; set; }
}
public class Person : Member
{
public string LastName { get; set; }
public string FirstName{ get; set; }
}
public class Business : Member
{
public string BusinessName { get; set; }
public string TaxNo { get; set; }
}
The class was mapped using fluent API,
Now, is there a way to update the "Status" property from the view(having Member model) without using or going to a subclass (Person/Business)?
I just tried it, but it says "Cannot create an abstract class.", when submitting the page.
Or there is a correct way to do this?
Thanks
Not in EF. You have to instantiate an object to work with EF, and you can't instantiate an abstract class.
You could make the class not be abstract. Or you could use a stored proc to update the field, or some direct sql.
It sounds like your problem is that your action method has an abstract type as a parameter, which the default model binder can't do anything with. If you are dead set on using the same view for two different classes, you may need to implement your own model binder to inspect in the incoming request and decide which type, Person or Business, to instantiate.
Check out this link for more information on creating a custom model binder:
http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx
This seems like a similar problem to the one I've answered previously here:
ASP.NET MVC 3: DefaultModelBinder with inheritance/polymorphism

Resources