Possible Bug in Migration for EF 6 Alpha 3 on recursive relationship? - recursive-datastructures

I had to define a recursive relationship on a composite key. After much trials, I ended up with this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Category>()
.Property(t => t.WhichAmazon).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Category>()
.Property(t => t.IdCategory).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Category>()
.HasKey(c => new {c.WhichAmazon, c.IdCategory})
.HasOptional(p => p.Children)
.WithMany()
.HasForeignKey(c => new { c.WhichChildrenAmazon, c.ChildrenId });
}
for this table
public class Category
{
// Keys and relationships defined in BooksDataLayer
[MaxLength(2)]
public string WhichAmazon { get; set; }
public int IdCategory { get; set; }
public string Name { get; set; }
[Timestamp]
public Byte[] TimeStamp { get; set; }
public DateTime LastCheck { get; set; }
public virtual List<Book> Books { get; set; }
public string WhichChildrenAmazon { get; set; }
public int? ChildrenId { get; set; }
public virtual List<Category> Children { get; set; }
}
While trying to Add-Migration I was constantly having the same error: "Sequence contains no elements". As I was "almost" sure this definition was right, I went ahead a re-created a new Db, WITHOUT migration. Was perfectly OK, no problems with the Db at all. So there is "something" in there which EF 6 does not like. I had a confirmation, as EF power tools bombs if I try to get a schema "Exception has been thrown by the target of an invocation".
I'll see what happens now with migration if I restart from there, but I am afraid to not be able to use anymore with this Db. I do like the tool, a lot, so I hope this can be fixed.

The issue is an invalid configuration of the relationship.
The Fluent API call includes this:
.HasOptional(p => p.Children)
.WithMany()
This is invalid because Children is a collection navigation. The correct config is:
.HasMany(p => p.Children)
.WithOptional()
We are planning to take a fix to provide a better exception message post-EF6.

Opened a bug for this on the EF codeplex site: http://entityframework.codeplex.com/workitem/1015

Related

Posting DropDownList value from View to Controller in MVC4

I have a MVC4 application and although I have get parameters for my DropDownList from the database, I encounter some kind of problems while posting the DropDownList value to the database. There is lots of samples for different approach, but I would like to apply a method without using an extra approach i.e. Ajax, Javascript, etc. On the other hand, I have run into "FormCollection" to pass data, but I am not sure if FormCollection is the best way in this scene. Here are some part of the view, controller and model I use:
View:
#using (Html.BeginForm("Add", "Product", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
<p>Product Type : #Html.DropDownListFor(m => m.SelectedLookupId, new SelectList(Model.Lookups.Where(x => x.LookupType == "Product Type"), "LookupID", "LookupValue"), "--- Select ---") </p>
Controller:
[HttpPost]
public ActionResult Add(Product product)
{
if (ModelState.IsValid)
{
product.ProductType = // ??? Cannot get the SelectedLookupId
...
repository.SaveProduct (product);
TempData["message"] = string.Format("{0} has been saved", product.Name);
return View("Completed");
}
else
{
//there is something wrong with the data values
return View(product);
}
}
ViewModel:
public class ProductViewModel
{
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Lookup> Lookups { get; set; } //Lookup for Product Types
public int SelectedLookupId { get; set; }
public Product Product { get; set; }
}
Thanks in advance for your helps.
Your action method should be receiving the view model, not the Product itself, like so:
[HttpPost]
public ActionResult Add(ProductViewModel productViewModel)
Unless I'm confused. But I assume the view snippet you posted above is from the Add view and that view's model is of type ProductViewModel. In your action method you are returning the Add view when the model state is invalid however you are passing a Product to that view. Again I may be confused because this should give you a runtime error that the types don't match.
Thanks for reply. Actually by using ViewModel rather than View, I have managed to solve the problem. On the other hand, after some research, I have applied another effective method in order to populate Dropdownlist without needing ViewModel. Furthermore with this example, I could use multiple foreign keys on the same Lookup table as shown below. Here is an an Applicant entity having 3 foreign keys and Lookup entity related to these keys. What I wanted to achieve with this example is exactly to use a Lookup table for only several Dropdownlist parameters i.e. Gender, Yes/No, Status,... due to no needing to create a table for the several parameters (these parameters are distinguished LookupType property on Lookup table). Here is the full example (I have shorted unrelated properties for brevity) below:
Applicant Entity:
public class Applicant
{
[Key]
public int ApplicantID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
// for using "Multiple foreign keys within same table using Fluent API"
public int? HasDoneAnyProject { get; set; }
public int? IsInterestedAnyProgramme { get; set; }
public int? InterestedProgrammeId { get; set; }
public virtual Lookup PrimaryLookup { get; set; }
public virtual Lookup SecondaryLookup { get; set; }
public virtual Lookup TertiaryLookup { get; set; }
}
Lookup Entity:
public class Lookup
{
[Key]
public int LookupID { get; set; }
public string LookupType { get; set; }
public string LookupValue { get; set; }
// for using "Multiple foreign keys within same table using Fluent API"
public virtual ICollection<Applicant> PrimaryLookupFor { get; set; }
public virtual ICollection<Applicant> SecondaryLookupFor { get; set; }
public virtual ICollection<Applicant> TertiaryLookupFor { get; set; }
}
DbContext:
public class EFDbContext : DbContext
{
public DbSet<Applicant> Applicants { get; set; }
public DbSet<Lookup> Lookups { get; set; }
//for using "Multiple foreign keys within same table using Fluent API"
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.PrimaryLookup)
.WithMany(a => a.PrimaryLookupFor)
.HasForeignKey(b => b.HasDoneAnyProject)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.SecondaryLookup)
.WithMany(a => a.SecondaryLookupFor)
.HasForeignKey(b => b.IsInterestedAnyProgramme)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.TertiaryLookup)
.WithMany(a => a.TertiaryLookupFor)
.HasForeignKey(b => b.InterestedProgrammeId)
.WillCascadeOnDelete(false);
}
}
Controller:
private void PopulateLookupsDropDownList(string lookupType, string foreignKey, object selectedLookups = null)
{
var lookupsQuery = repository.Lookups
.Select(x => x)
.Where(x => x.LookupType == lookupType)
.OrderBy(x => x.ParentLookupID).ToList();
ViewData[foreignKey] = new SelectList(lookupsQuery, "LookupID", "LookupValue", selectedLookups);
}
and for calling the Method for each of three Dropdownlist:
PopulateLookupsDropDownList("YesNo", "HasDoneAnyProject", applicant.HasDoneAnyProject);
PopulateLookupsDropDownList("YesNo", "IsInterestedAnyProgramme", applicant.IsInterestedAnyProgramme);
PopulateLookupsDropDownList("Programme", "InterestedProgrammeId", applicant.InterestedProgrammeId);
View: : Populating each of three Dropdownlist from the same Lookup table with different LookupType parameter:
<label>Has done any project before?</label>
#Html.DropDownList("HasDoneAnyProject", "---- Select ----")
<label>Are you interested in any programme?</label>
#Html.DropDownList("IsInterestedAnyProgramme", "---- Select ----")
<label>Interested programme name?</label>
#Html.DropDownList("InterestedProgrammeId", "---- Select ----")
I hope this approach will be useful for those who want to populate Dropdownlists from the same Lookup table. On the other hand, it is not only suitable for this, also can be used for populating Dropdownlists from different tables.
Regards.

Doesn't update many to many attribute using NHibernate

Suppose I have only two classes: Group and User. User has groups and Group has members (instance of users)
public class User {
public virtual int id { set; get; }
public virtual string username { set; get; }
public virtual IList<Group> groups { set; get; }
public User()
{
groups = new List<Group>();
}
public virtual void joinGroup(Group group)
{
if (this.groups.Contains(group))
throw new AlreadyJoinedException();
group.members.Add(this);
this.groups.Add(group);
}
public class Group
{
public virtual int id { set; get; }
public virtual string name { set; get; }
public virtual User administrator { set; get; }
public virtual IList<User> members { set; get; }
public Group()
{
members = new List<User>();
}
As you can see the domain it's quite simple. I've already mapped both classes correctly using Fluent NHibernate,
public class UserMapping : ClassMap<User>
{
public UserMapping()
{
this.Id(user => user.id).GeneratedBy.Identity();
this.Map(user => user.username).Not.Nullable().Length(50).Not.LazyLoad();
this.HasManyToMany(user => user.groups).Table("MemberPerGroup").ParentKeyColumn("id_user").ChildKeyColumn("id_group").Not.LazyLoad();
}
}
public class GroupMapping : ClassMap<Group>
{
public GroupMapping()
{
this.Id(group => group.id).GeneratedBy.Identity();
this.Map(group => group.name).Not.Nullable().Length(50).Not.LazyLoad();
this.References(group => group.administrator).Not.Nullable().Not.LazyLoad();
this.HasManyToMany(group => group.members).Table("MemberPerGroup").ParentKeyColumn("id_group").ChildKeyColumn("id_user").Not.LazyLoad();
}
}
I'm progamming a web application using ASP MVC 4. My problem shows up when a user tries to join group. It doesn't break but it neither works fine (doesn't insert into the table the new row in MemberPerGroup). I'm doing something like it:
public void JoinGroup(User user,Group group){
this.userRepository.GetSessionFactory().TransactionalInterceptor(() =>
{
user.joinGroup(group);
});
}
Thanks in advance.
Ivan.
It seems your mapping has no cascading set?
this.HasManyToMany(group => group.members)
.Table("MemberPerGroup")
.ParentKeyColumn("id_group")
.ChildKeyColumn("id_user")
.Not.LazyLoad()
.Cascade.SaveUpdate();
I'm curious - why do you use GetSessionFactory()? our repositories take an ISession object in the constructor, (injected by autofac, but that's irrelevant) from which we start our queries:
// even better to use a transaction, but this is just a sample
_session.SaveOrUpdate(user);
_session.Flush();

References in fluent hibernate mapping seems dosnt work correctly

I'm using fluent hibernate 1.2.0.712 and nhibernate 3.2.0.4000 in my mvc project as an OR mapper, the problem is :
this is my oJob object:
public class Job{
virtual public Enquiry Enquiry { get; set; }
virtual public long Id { get; set; }
}
and this is Enquiry:
public class Enquiry {
virtual public long Id { get; set; }
}
and here is JobMap:
public class JobMap: ClassMap<Job>
{
public JobMap()
{
Schema("dbo");
Id(p => p.Id)
.Column("Id");
References(p => p.Enquiry);
}
}
i expect that each job has exactly one enquiry
but sometimes that i check sql server i see there are some records in job table with different ids that all have the same enquiryid
and i checked it many times and dont know when exactly it happens, what is the problem?
You must change your mapping : change
References(p => p.Enquiry);
by
HasOne(p => p.Enquiry);

Why my EF collection are not lazy?

I'm using EF 4.1 Code first. I have a model of user and a model of setting.
Each time the repository returns a user the Setting is also loaded. I've marked the Setting as virtual all my access modifiers are public LazyLoadingEnabled and ProxyCreationEnabled are enabled by default.
What am I missing?
public class User : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual ICollection<Setting> Settings { get; set; }
}
public class Setting
{
public int UserID { get; set; }
public int SettingID { get; set; }
public string Value { get; set; }
}
The User might have several setting, so there is a one to many relationship with a foreign key in setting.
The user configuration is
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasKey(u => u.ID);
HasMany(u => u.Settings).WithOptional().HasForeignKey(u => u.UserID);
}
}
and the Setting configuration is:
public class SettingsConfiguration : EntityTypeConfiguration<Setting>
{
public SettingsConfiguration()
{
ToTable("UserSettings");
HasKey(s => new { s.UserID, s.SettingID });
}
}
Lazy loading means the opposite of what you think it means.
With lazy loading (virtual property and defaults)
Settings is not retrieved immediately when querying User
Settings is retrieved when it's accessed for the first time. The DbContext must be open at that time for this to happen; otherwise you'll get an exception
Without lazy loading (non-virtual property and/or explicitly disabled)
Settings is not retrieved immediately when querying User
Settings will never be retrieved automatically; it will return null (which, in my opinion, is a terrible design decision: null is a wrong value and you shouldn't be able to get it)
In both cases, you can load Settings eagerly by using .Include(x => x.Settings), or when needed, by calling context.Entry(user).Collection(x => x.Settings).Load()

Fluent NHibernate with ManyToMany and Custom Link Table

I have the following schema, and when I delete one of the objects on one many side, it seems to be trying to delete the objects on the other many side. Am somewhat confused about the proper Cascade options to use, and I don't find Oren's brief description of them to be useful, so please don't quote those back.
public class Store {
public virtual IList<StoreProduct> StoreProducts { get; set; }
}
public class Product {
public virtual IList<StoreProduct> StoreProducts { get; set; }
}
public class StoreProduct {
public virtual Store Store { get; set; }
public virtual Product Product { get; set; }
public virtual Decimal Cost { get; set; } //this is why I have a custom linking class
}
In my mapping overrides, I have:
For Store:
mapping.HasMany(x => x.StoreProducts).Cascade.AllDeleteOrphan().Inverse;
For Product:
mapping.HasMany(x => x.StoreProducts).Cascade.AllDeleteOrphan().Inverse;
When I try to delete a Store that has associated StoreProducts, it seems that NHIbernate tries to delete not only the StoreProducts, but the Products.
Here are my conventions:
return c =>
{
c.Add<ForeignKeyConvention>();
c.Add<HasManyConvention>();
c.Add<HasManyToManyConvention>();
c.Add<ManyToManyTableNameConvention>();
c.Add<PrimaryKeyConvention>();
c.Add<ReferenceConvention>();
c.Add<EnumConvention>();
c.Add<TableNameConvention>();
c.Add<CascadeAll>();
c.Add(DefaultCascade.All());
};
HasManyConvention:
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.Column(instance.EntityType.Name + "Fk");
instance.Cascade.AllDeleteOrphan();
instance.Inverse();
}
What am I doing wrong?
Thanks!
p.s.: I don't want to overwhelm people w/code, but can post more if needed.
Thanks, CrazyDart - I think that is among the things I tried without success. What I ended up doing was adding a StoreProducts override that looks like this:
public class StoreProductOverride: IAutoMappingOverride<StoreProduct>
{
#region IAutoMappingOverride<StoreProduct> Members
public void Override(AutoMapping<IndicatorStrategy> mapping)
{
mapping.References(x => x.Store).ForeignKey("StoreFk").Cascade.SaveUpdate();
mapping.References(x => x.Producty).ForeignKey("ProductFk").Cascade.SaveUpdate();
}
#endregion
}
Seems to work, but QA hasn't tried to break it yet (-:
You need to turn off the cascading on StoreProduct is my guess. Its hard to test without setting it up. I see the cascade on Store and Product, but turn it off on StoreProduct.