I'm reading Pro ASP.Net MVC2 and I've gotten to a point where nothing is explained well enough. For example, the following tells me to create this C# code manually:
Implementing the Auctions Domain Model
With LINQ to SQL, you can set up mappings between C# classes and an implied database schema either
by decorating the classes with special attributes or by writing an XML configuration file. The XML option
has the advantage that persistence artifacts are totally removed from your domain classes,4 but the
disadvantage that it’s not so obvious at first glance. For simplicity, I’ll compromise here and use
attributes.
Here are the Auctions domain model classes now fully marked up for LINQ to SQL:5
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq.Mapping;
using System.Data.Linq;
[Table(Name="Members")]
public class Member
{
[Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
internal int MemberID { get; set; }
[Column]
public string LoginName { get; set; }
[Column]
public int ReputationPoints { get; set; }
}
[Table(Name = "Items")]
public class Item
{
[Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
public int ItemID { get; internal set; }
[Column]
public string Title { get; set; }
[Column]
public string Description { get; set; }
[Column]
public DateTime AuctionEndDate { get; set; }
[Association(OtherKey = "ItemID")]
private EntitySet<Bid> _bids = new EntitySet<Bid>();
public IList<Bid> Bids { get { return _bids.ToList().AsReadOnly(); } }
}
Where exactly do I have to write this in? Or is he just displaying generated code by the Linq-to-sql DBML?
That's not generated code. That's how you use Linq mappings to map your classes to your database.
You just write it in a CS file. It can go anywhere, but if you're using ASP.NET MVC you'd usually put it in the Models folder.
See this: http://msdn.microsoft.com/en-us/library/bb386971.aspx
Related
I've manay-to-many relationship between two entities: Categories <--> Items
public class CategoryMaster
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<SubCategoryMaster> SubCategories { get; set; }
public List<ItemMaster> Items { get; set; }
}
public class ItemMaster
{
public long Id { get; set; }
public string Name { get; set; }
public List<CategoryMaster> Categories { get; set; }
}
Whenever I try to explicit load related items to all/certain categories, it gives me
all related items
related categories to those items
related items to those categories and so on...nested/circular references
db.CategoryMaster
.Include(x=>x.Items)
.Include(x=>x.SubCategories.Select(y=>y.Items))
.ToList();
Hence results in below error while serializing it to JSON on *.cshtml with Json.Encode();
A circular reference was detected while serializing an object of type 'GoGreen.Data.Entities.SubCategoryMaster'.
Since I've disabled the lazy loading at property level, I'm not expecting it to load all nested entities(circular references) at any point of time. Is there a way to load all related level one records i.e. Categories and related items.
Related question - But Iodon't want to go with any of the two ways suggested.
NOTE : I'm more interested in knowing why EF behaves like this. It seems a bug to me.
First approach: you can add attribute above properties you don't want to exclude it from being serialized using [ScriptIgnore], you can create partial class and add your customization if your entities are auto generated
Second approach: Create a Model with only properties you need in your view and select only this model and set your properties
EFcontext.Tabel.include(x=>x...).Select(x=>new MyModel { ... });
One workaround, and please don't kill me :-) After object loading and before serializing, just set the loaded objects which are causing the circular reference to null. I tried it and worked like a charm.
use meta data redirection. figured I would help anyone who stumbled here.
[MetadataType(typeof(CategoryMasterMetadata))]
public partial class CategoryMaster
{
}
public class CategoryMasterMetadata
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
public virtual List<SubCategoryMaster> SubCategories { get; set; }
public List<ItemMaster> Items { get; set; }
}
When I try to use Microsoft ADO.NET Entity Framework 4.1 in our project, I can not control the generated model's name.
For example, see the code first,
public class Bank
{
[Key]
public Guid BankID { get; set; }
public string BankCardNumber { get; set; }
public string BankName { get; set; }
} public class MyContext : DbContext
{
public DbSet<Bank> Banks { get; set; }
} public class MyContextInitializer : DropCreateDatabaseIfModelChanges<MyContext>
{
protected override void Seed(MyContext context)
{
}
}
The code above is ok and it will be generated a table name called "Banks" in our database, but we want to control the generated table name, like "bank" or "opbanks", so could tell me how to do this?
Thank you very much.
Try using the TableAttribute class. For reference, you can find a list of data annotations for EF 4.1 here.
public class Car {
public string SomeProperty { get; set; }
public Manufacturer Manufacturer { get; set; }
public IList<Color> Colors { get; set; }
}
public class Manufacturer {
public int Id { get; set; }
public string Name { get; set; }
}
public class Color {
public int Id { get; set; }
public string Name { get; set; }
}
I already have tables full of Colors and Manufacturers. When I create a new car I want to be able to assign it a Color and Manufacturer bound from .net MVC.
When I save my new car with
context.Cars.Add(car);
A new Car is created (great) but a new Color and Manufacturer are also created even though these objects already had an Id and Name set that matched the content in the database.
The two solutions I see are to either write a custom save method for car, and tell the context that the Manufacturer and Color are Unchanged.
context.Cars.Add(car);
context.Entry(car.Manufacturer).State = EntityState.Unchanged;
foreach (Color color in car.Colors)
context.Entry(car.Color).State = EntityState.Unchanged;
Alternatively, to load the Manufacturer and Color from EF and then link them to the Car, instead of using the MVC bound objects.
car.Manufacturer = carRepository.GetManufacturer(car.Manufacturer.Id);
car.Colors = carRepository.GetColorsById(car.Colors);
I am not thrilled by either solution as this example is very trivial, but my real cases are far more complicated. I don't really want to have to fiddle around with EF in detail for each object I save. I have lots of complex object graphs to save and this seems very error prone.
Is there a way of making EF behave more like NHibernate, whereby you can give it something with an ID already assigned and it will assume without your intervention that it already exists?
Edit - question clarified to show collection of existing entities as well as many-to-one relationships.
Unfortunately, EF does not have anything like session.Load in NHibernate that allows you to get a proxy from an id.
The usual way to deal with this in EF is create a separate FK field containing the scalar value that corresponds to the reference. For example:
public virtual Manufacturer Manufacturer { get; set; }
public int ManufacturerId { get; set; }
Then you only have to set ManufacturerId and it will be saved correctly.
(So much for "POCO" and "code first". Pffffff)
You can define scalar properties in your entities and bind the values to them instead. Eg add
ManufacturerId and ColorId
public class Car {
public string SomeProperty { get; set; }
public int? ManufacturerId { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public int? ColorId { get; set; }
public virtual Color Color { get; set; }
}
Then set those scalar properties when you assign (eg through a DropDownList)
This way you can avoid loading many related entities to populate the entity.
As I already have classes for my LINQ to SQL data access solution, what trouble might I run into if I wanted to migrate them over to EFCF instead? I'm hesistant to call this code first as the database does already exist. To be clear, the application is not yet in production so if EFCF wipes out the data it's no real loss.
Can I take a class such as the one that follows and simply use it in EFCF? Should I or must I remove the data annotation attributes?
What needs to change where I have EntityRef and EntitySet?
[Table]
public class PlanMember {
private EntityRef<SystemUser> _caseManager;
private EntityRef<PlanMemberStatus> _status;
public PlanMember() {
this.PlanMemberView = new Views.PlanMember();
}
[Column(
IsPrimaryKey = true,
IsDbGenerated = true,
AutoSync = AutoSync.OnInsert
)]
public Int64 ID { get; set; }
[Column]
public DateTime? BirthDate { get; set; }
[Column]
public String City { get; set; }
[Column]
public String FirstName { get; set; }
[Association(ThisKey = "CaseManagerID", Storage = "_caseManager")]
public SystemUser CaseManager {
get { return (this._caseManager.Entity); }
set { this._caseManager.Entity = value; }
}
[Column]
public String CaseManagerID { get; set; }
[Column]
public Boolean IsActive { get; set; }
public Boolean IsEligible {
get { return (this.PlanMemberView.IsEligible); }
}
[Column]
public String LastName { get; set; }
[Column]
public String MedicalRecord { get; set; }
[Column]
public String MemberNumber { get; set; }
[Column(Name = "PCPFullName")]
public String PrimaryCarePhysicianFullName { get; set; }
[Association(OtherKey = "PlanMemberID")]
public Views.PlanMember PlanMemberView { get; set; }
[Column]
public Int32 PostalCode { get; set; }
[Column]
public String Sex { get; set; }
[Column]
public String State { get; set; }
[Association(ThisKey = "StatusID", Storage = "_status")]
public PlanMemberStatus Status {
get { return (this._status.Entity); }
set { this._status.Entity = value; }
}
[Column]
public Int32 StatusID { get; set; }
}
We migrated an application from Linq to Sql to EF POCO generation but haven't tried code first as it wasn't baked at the time. Was really not horribly difficult. The main pain point in our case was the following differences:
Linq to Sql handles many to many relationships using a separate "bridge" object, EF treats those relationships as collections of various sorts. This changes lots of semantics and can cause lots of code to change, especially if you let entities creep into the UI.
Another pain point was nullable and non-nullable relationships. Linq to Sql was a bit more forgiving here, but for EF to play well we needed to allow nullable columns some places we traditionally had not.
Linq to Sql and EF data mapping sometimes have different ideas about what CLR types to map to. Xml columns were our major pain point but you might not have any of those.
Big trick/nightmare was how to get rid of the l2s bits without breaking everything horribly as linq to sql generates your entities.
This is not something I would try without a pretty effective set of unit tests to give you an automated basis to give you pretty regular temperature readings. Our other godsend was we had a pretty solid Repository pattern implementation -- nothing was talking directly to the EF/Linq2Sql bits but two classes implementing IRepository. Basically, this is a great test for how disciplined you were in implementing your architecture. Also, it is an occasion when you realize that resharper is worth every cent.
To answer your one direct question, I don't think the attributes will necessarily matter but I would remove them so as not to have any potential confusion and/or namespace collisions.
Assuming your classes are named the same as your database tables, and the properties of your classes match the database column names, you should be able to delete all the attributes and use these same classes as your EF code-first model (I don't think you have to delete the attributes, but unless you plan to continue using them in a Linq2Sql model, there's no reason to keep them, and since some things will probably change in the migration, it would probably be best to delete them since your new entities may not still be able to work in Linq2Sql). If your classes don't match your database schema, Scott Guthrie has a blog post about Entity Framework 4 "Code-First": Custom Database Schema Mapping.
What needs to change where I have EntityRef and EntitySet?
A relation defined as EntityRef<OtherEntity> can be replaced with a property of just type OtherEntity, and an EntitySet<OtherEntity> can become an ICollection<OtherEntity> or anything that implements ICollection<T> such as an IDbSet<OtherEntity> (I believe a DbSet<T> is what you would get if you were generating the model from your existing database).
I'm trying to learn Entity Framework Code First development with ASP.NET MVC3.
Let's say I have a simple data Model for an Auction and Bids and I'd like to query all the Auctions and their Bids.
I have turned off LazyLoadingEnabled and ProxyCreationEnabled.
Here is the code I have:
public class MiCoreDb2Context : DbContext
{
public MiCoreDb2Context()
: base()
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Auction> Auctions { get; set; }
public DbSet<Bid> Bids { get; set; }
}
public class Auction
{
public int AuctionId { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
}
public class Bid
{
public long BidId { get; set; }
public int AuctionId { get; set; }
[ForeignKeyAttribute("AuctionId")]
public virtual Auction Auction { get; set; }
}
public JsonResult Thing()
{
List<Auction> auctions;
using (var db = new MiCoreDb2Context())
{
var auctions = (from a in db.Auctions.Include("Bids") select a).ToList();
}
return Json(auctions, JsonRequestBehavior.AllowGet);
}
When I load the page, a circular reference occurs. How will I get around this?
When I load the page, a circular reference occurs. How will I get around this?
By using view models (and by the way that's the answer to any question you might have concerning ASP.NET MVC :-)). Ayende Rahien has an excellent series of blog posts on this topic.
Conclusion: absolutely always pass/take view models to/from a view. Absolutely never pass/take models (EF, domain, ...) to/from a view. Once this fundamental rule is being respected you will find out that everything works.
I solved this problem by doing a projection in the Linq to Entities query. This will create anonymous types which can be serialized to json without any circular reference issues.
var result =
from Item in dbContext.SomeEntityCollection
where SomePredicate
select new { Property1 = Item.Property1, Property2 = Item.Property2 };
Return Json(result, JsonRequestBehavior.AllowGet);
BOb