How can i use seed method for create initial data for my context? - ef-code-first

I'm trying to use seed method like this;
context.Reeves.AddOrUpdate(
p => new { p.FirstName, p.LastName },
new Reeve { FirstName = "A", LastName = "A" },
new Reeve { FirstName = "B", LastName = "B" });
context.SaveChanges();
context.Districts.AddOrUpdate(
p => p.Name,
new District() { Name = "X", ReeveId = context.Reeves.First(r => r.FirstName == "A" && r.LastName == "A").Id },
new District() { Name = "Y", ReeveId = context.Reeves.First(r => r.FirstName == "B" && r.LastName == "B").Id });
context.SaveChanges();
I'm receiving error message that "The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.District_dbo.Reeve_Id". The conflict occurred in database "ProjectTracking", table "dbo.Reeve", column 'Id'."
If i change the code like below;
context.Districts.AddOrUpdate(
p => p.Name,
new District() { Name = "X", Reeve = context.Reeves.First(r => r.FirstName == "A" && r.LastName == "A") },
new District() { Name = "Y", Reeve = context.Reeves.First(r => r.FirstName == "B" && r.LastName == "B") });
context.SaveChanges();
Error message disapear but when i check the districts table i see all ReeveId columns are 0.
What is my mistake, any idea?
PS: I dont want to create inline Reeve's inside District's AddOrUpdate methods. Something like; context.Districts.AddOrUpdate(p => p.Name, new District() { Name = "X", Reeve = new Reeve () { FirstName = "A", LastName = "A" });
My Entities
public class Reeve
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get { return string.Format("{0} {1}", FirstName, LastName); }
}
public virtual District District { get; set; }
public byte[] RowVersion { get; set; }
}
public class District
{
public District()
{
Projects = new HashSet<Project>();
ProjectRequests = new HashSet<ProjectRequest>();
}
public int Id { get; set; }
public string Name { get; set; }
public int ReeveId { get; set; }
public virtual Reeve Reeve { get; set; }
public byte[] RowVersion { get; set; }
public virtual ICollection<Project> Projects { get; set; }
public virtual ICollection<ProjectRequest> ProjectRequests { get; set; }
}
Entity Configurations
public class ReeveConfiguration : EntityTypeConfiguration<Reeve>
{
public ReeveConfiguration()
{
HasKey<int>(p => p.Id);
Ignore(p => p.FullName);
Property(p => p.FirstName).HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("FullName", 1) { IsUnique = true })).HasMaxLength(50).IsRequired();
Property(p => p.LastName).HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("FullName", 2) { IsUnique = true })).HasMaxLength(50).IsRequired();
Property(p => p.RowVersion).IsRowVersion();
}
}
public class DistrictConfiguration : EntityTypeConfiguration<District>
{
public DistrictConfiguration()
{
HasKey<int>(p => p.Id);
HasRequired(p => p.Reeve).WithOptional(p => p.District);
HasMany(p => p.Projects).WithRequired(p => p.District).HasForeignKey(p => p.DistrictId);
HasMany(p => p.ProjectRequests).WithRequired(p => p.District).HasForeignKey(p => p.DistrictId);
Property(p => p.Name).HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute() { IsUnique = true })).HasMaxLength(100).IsRequired();
Property(p => p.RowVersion).IsRowVersion();
}
}
I hate entity framework team's 1 to 1 relationship rules. Delegated entity is must be same name PK with principle entity and also delegated entity PK must be also FK. This must be a joke. All 1 to 1 relationship can not be like Person -> PersonPhoto or Car -> Steering wheel. Am i right or i misunderstand their logic. For example i have also project and project request entities, project's request and request's project can be null i mean they have 0..1 to 0..1 relationship and they must be own PK Id's. Also how about that if i have Entity base class that have Id primary key field. How can i derived my 1 to 1 relation entities from that.

Ok, i solved my problem myself. Let's talk again my expectations. I want to write base class for my entities, at first step it's only contain Id property. Also i want to control my foreign key names, i don't want to ef do it for me automatically.
Also let's talk about received error too. I can not seed data and receive an error because of ef creating foreign key for me automatically and in district entity ReeveId column creating something like other data column. So when i set District.ReeveId with existing reeve and save changes ef throw foreign key error.
I do below changes to my code for solve the problem toward ef and my expectations;
Delete ReeveId from district entity because there is no need
Add HasRequired(p => p.Reeve).WithOptional(p => p.District).Map(m => m.MapKey("ReeveId")); code to my district configuration
So both table contain own Id column for primary key, district also have ReeveId column for foreign key. As a result ef and my expectations met.

Related

RavenDB array search returns random results

I'm trying to perform a search on top of a dictionary using the Search method from RavenDB 4. Strangely, if the search term is the word in or it I get random results back. I'm absolutely sure that none of the records contains those words. It also happens when executing the equivalent lucene query on the studio. It works as expected when I enter a valid search term like the employee's name, number, etc.
I've managed to create this simple scenario based on the real one.
Here's the index:
public class Search : AbstractIndexCreationTask<Employee, Page>
{
public Search()
{
Map = employees => from employee in employees
select new
{
Id = employee.Id,
Details = employee.Details
};
Reduce = results => from result in results
group result by new
{
result.Id,
result.Details
}
into g
select new
{
g.Key.Id,
g.Key.Details
};
Index("Details", FieldIndexing.Search);
}
}
Employee class:
public class Employee
{
public string Id { get; set; }
public Dictionary<string, object> Details { get; set; }
}
Adding employees:
details = new Dictionary<string, object>();
details.Add("EmployeeNo", 25);
details.Add("FirstNames", "Yuri");
details.Add("Surname", "Cardoso");
details.Add("PositionCode", "XYZ");
details.Add("PositionTitle", "Developer");
employee = new Employee
{
Details = details
};
session.Store(employee);
session.SaveChanges();
Search method:
var searchTerm = "in";
var result = session
.Query<Page, Search>()
.Search(i => i.Details, $"EmployeeNo:({searchTerm})")
.Search(i => i.Details, $"FirstNames:({searchTerm})", options: SearchOptions.Or)
.Search(i => i.Details, $"Surname:({searchTerm})", options: SearchOptions.Or)
.Search(i => i.Details, $"PositionCode:({searchTerm})", options: SearchOptions.Or)
.Search(i => i.Details, $"PositionTitle:({searchTerm})", options: SearchOptions.Or)
.ToList();
Lucene query outputed:
from index 'Search' where search(Details, "EmployeeNo:(it)")
or search(Details, "FirstNames:(it)")
or search(Details, "Surname:(it)")
or search(Details, "PositionCode:(it)")
or search(Details, "PositionTitle:(it)")
Any idea why random results are returned when those specific words are enterered?
The issue is stop words. Certain terms are so common, that they are meaningless for searching using full text search.
is, it, they, are, etc.
They are erased by the query analyzer.
See the discussion here: https://ravendb.net/docs/article-page/4.2/Csharp/indexes/using-analyzers
You can use a whitespace analyzer, instead of the Standard Analyzer, since the former doesn't eliminate stop words.
After getting help from the RavenDB group guys, we've managed to find a solution for my scenario.
Employee:
public class Employee
{
public string Id { get; set; }
public string DepartmentId { get; set; }
public Dictionary<string, object> Details { get; set; }
}
Department:
public class Department
{
public string Id { get; set; }
public string Name { get; set; }
}
Page:
public class Page
{
public string Id { get; set; }
public string Department { get; set; }
public Dictionary<string, object> Details { get; set; }
}
Index (with dynamic fields):
public class Search : AbstractIndexCreationTask<Employee, Page>
{
public Search()
{
Map = employees => from employee in employees
let dept = LoadDocument<Department>(employee.DepartmentId)
select new
{
employee.Id,
Department = dept.Name,
_ = employee.Details.Select(x => CreateField(x.Key, x.Value))
};
Store(x => x.Department, FieldStorage.Yes);
Index(Constants.Documents.Indexing.Fields.AllFields, FieldIndexing.Search);
}
}
Query:
using (var session = DocumentStoreHolder.Store.OpenAsyncSession())
{
var searchTearm = "*yu* *dev*";
var result = await session
.Advanced
.AsyncDocumentQuery<Page, Search>()
.Search("Department", searchTearm)
.Search("EmployeeNo", searchTearm)
.Search("FirstNames", searchTearm)
.Search("Surname", searchTearm)
.Search("PositionCode", searchTearm)
.Search("PositionTitle", searchTearm)
.SelectFields<Page>()
.ToListAsync();
}
Everything seems to be working fine this way, no more random results.
Big thanks to Ayende and Egor.

A circular reference was detected while serializing entities with one to many relationship

How to solve one to many relational issue in asp.net?
I have Topic which contain many playlists.
My code:
public class Topic
{
public int Id { get; set; }
public String Name { get; set; }
public String Image { get; set; }
---> public virtual List<Playlist> Playlist { get; set; }
}
and
public class Playlist
{
public int Id { get; set; }
public String Title { get; set; }
public int TopicId { get; set; }
---> public virtual Topic Topic { get; set; }
}
My controller function
[Route("data/binding/search")]
public JsonResult Search()
{
var search = Request["term"];
var result= from m in _context.Topics where m.Name.Contains(search) select m;
return Json(result, JsonRequestBehavior.AllowGet);
}
When I debug my code I will see an infinite data because Topics will call playlist then playlist will call Topics , again the last called Topic will recall playlist and etc ... !
In general when I just use this relation to print my data in view I got no error and ASP.NET MVC 5 handle the problem .
The problem happens when I tried to print the data as Json I got
Is there any way to prevent an infinite data loop in JSON? I only need the first time of data without call of reference again and again
You are getting the error because your entity classes has circular property references.
To resolve the issue, you should do a projection in your LINQ query to get only the data needed (Topic entity data).
Here is how you project it to an anonymous object with Id, Name and Image properties.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you have a view model to represent the Topic entity data, you can use that in the projection part instead of the anonymous object
public class TopicVm
{
public int Id { set;get;}
public string Name { set;get;}
public string Image { set;get;}
}
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new TopicVm
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you want to include the Playlist property data as well, you can do that in your projection part.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image,
Playlist = x.Playlist
.Select(p=>new
{
Id = p.Id,
Title = p.Title
})
});
return Json(result, JsonRequestBehavior.AllowGet);
}

Linq Order list within a list based on parent Id

Having a list of items I need to sort/order items within that list based on outter list's id
public class Venue
{
public int Id { get; set; }
public string Code { get; set; }
public IEnumerable<Machines> Machines { get; set; }
}
public class Machine
{
public string Name { get; set; }
}
Suppose I have a list of 3 venues and I want to sort only Venue's Machines where Venue.Id = 1;
Having a list of venues, I want to always sort by Venue Code, THEN sort machines by Name of Venue with Id = 1;
I tried this but doesn't work correctly:
query = query.OrderBy(x => x.Code).ThenBy(y => y.Machines.OrderBy(q => q.Name).Where(x => y.Id == 1))
If you want to sort a property of an object which is an IEnumerable<T> you need to select a new:
query = query
.Select(v => new Venue
{
Id = v.Id,
Code = v.Code,
Machines = v.Id == 1 ? v.Machines.OrderBy(m => m.Name) : v.Machines
})
.OrderBy(v => v.Code);

Code First relationship one to many / many to many

I have a table restaurant. A restaurant can have multiple specialties. And specialties are for example chinees, spahnish, greeks, etc.
Restaurant table/class:
[Table("Restaurant")]
public class Restaurant
{
[Key]
[Column(Order = 0)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(40)]
[Column(Order = 2)]
public string Name { get; set; }
[MaxLength(1000)]
[Column(Order = 6)]
public string Comments { get; set; }
public virtual ICollection<Specialties> Specialties { get; set; }
}
And this is my specialties table/class:
public enum Specialty
{
Chinees = 0,
Spanish = 1,
Etc.. = 2,
}
[Table("Specialties")]
public class Specialties
{
[Key]
[Column(Order = 0)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[Column(Order = 1)]
public Specialty Specialty { get; set; }
//[Required]
[MaxLength(20)]
[Column(Order = 2)]
public string Name { get; set; }
public virtual ICollection<Restaurant> Restaurant { get; set; }
}
The specialties table is pre-filled with data.
This is my table/class for the many to many relationship:
[Table("RestaurantSpecialties")]
public class RestaurantSpecialties
{
[Key]
public int RestaurantId { get; set; }
[Key]
public int SpecialtyId { get; set; }
[ForeignKey("RestaurantId")]
public virtual Restaurant Restaurant{ get; set; }
[ForeignKey("SpecialtyId")]
public virtual Specialties Specialty { get; set; }
}
With Fluent Api I tried to define the model like this:
// Primary keys
modelBuilder.Entity<Restaurant>().HasKey(q => q.Id);
modelBuilder.Entity<Specialties>().HasKey(q => q.Id);
// Relationship
modelBuilder.Entity<Restaurant>()
.HasMany(q => q.Specialties)
.WithMany(q => q.Restaurant)
.Map(q =>
{
q.ToTable("RestaurantSpecialty");
q.MapLeftKey("RestaurantId");
q.MapRightKey("SpecialtyId");
});
And in my Seed function i'm seeding data like this:
context.Specialties.AddOrUpdate(
p => p.Specialty,
new Specialties { Specialty = Specialty.Chinees, Name = "Chinees" },
new Specialties { Specialty = Specialty.Spanish, Name = "Spanish" },
);
ICollection<Specialties> lstSpecialties = new List<Specialties>();
lstSpecialties.Add(context.Specialties.FirstOrDefault(x => x.Name == "Chinees"));
context.Restaurants.AddOrUpdate(
p => p.Name,
new Restaurant{ Name = "Ctr test1", Specialties = lstSpecialties, Comments = "sfasd" },
new Restaurant{ Name = "Ctr test2", Specialties = lstSpecialties, Comments = "asd" },
);
But my table RestaurantSpecialties doesn't contain anything. I was expecting to see Id's of both tables.
What am I doing wrong?
[EDIT]
The enum has been renamed from 'Specialty' to 'RestaurantSpecialty' like Gert suggested to do (just the rename part, not the actual name).
The plural class 'Specialties' has also been renamed to 'Specialty'. Also because Gert suggested it.
The join table has also been removed from the model. The Fluent Api part in my original post has been updated accordingly.
Catering(s) has been renamed to Restaurant(s) which i forgot to do so in the beginning. So to avoind confusions i've done this too in my original code post.
context.Specialties.AddOrUpdate(
new Specialty { CateringSpecialty = RestaurantSpecialty.Chinese, Name = "Chinese" },
new Specialty { CateringSpecialty = CateringSpecialty.Greeks, Name = "Greeks" },
);
var aSpecialty = context.Specialties.FirstOrDefault(x => x.Name == "Chinese");
var restaurant1 = context.Restaurants.Include(c => c.Specialties).FirstOrDefault(x => x.Name == "Ctr test1");
if (restaurant1 == null)
{
restaurant1 = new Restaurant
{
Name = "Ctr test4",
Specialties = new List<Specialty> { aSpecialty },
Comments = "testtttttttt"
};
context.Restaurants.Add(restaurant1);
}
else
{
if (!restaurant1.Specialties.Any(s => s.Id == aSpecialty.Id))
restaurant1.Specialties.Add(aSpecialty);
restaurant1.Name = "Ctr test4";
restaurant1.Comments = "testtttt";
}
Try to add/update manually. I suspect that the AddOrUpdate method does not support updating a full object graph of related entities. Probably it supports adding related entities together with its parent entity if the parent entity doesn't exist yet. But if the "Chinees" specialty was already in the database without related entities the relationships possibly don't get updated.
A "manual update" would look like this:
context.Specialties.AddOrUpdate(
p => p.Specialty,
new Specialties { Specialty = Specialty.Chinees, Name = "Chinees" },
new Specialties { Specialty = Specialty.Spanish, Name = "Spanish" },
);
var chineesSpecialty = context.Specialties
.FirstOrDefault(x => x.Name == "Chinees");
var catering1 = context.Caterings.Include(c => c.Specialties)
.FirstOrDefault(x => x.Name == "Ctr test1");
if (catering1 == null)
{
catering1 = new Catering
{
Name = "Ctr test1",
Specialties = new List<Specialties> { chineesSpecialty },
Comments = "sfasd"
};
context.Caterings.Add(catering1);
}
else
{
if (!catering1.Specialties.Any(s => s.Id == chineesSpecialty.Id))
catering1.Specialties.Add(chineesSpecialty);
catering1.Comments = "sfasd";
}
// and the same for "Ctr test2"
This is also mentioned in this answer but it was during the early preview releases of Code-First Migrations, so I am not sure if this limitation still exists. But it would explain why you don't get entries in the relationship table.
Your mapping itself looks correct to me. (But as already recommended by Gert Arnold in the comments I would really remove the RestaurantSpecialties entity which looks redundant and only complicates your model.)

ASP.NET MVC 3 Telerik Razor grid with editable foreign key dropdown column

I am trying to create an Ajax Telerik grid in Razor that has an updateable foreign key column that shows a dropdown list. I've copied my page pretty much like the example, and everything works. I can add new records, delete them and edit them. The only thing that doesn't work is that I get a textfield with the integer when I update a record in my grid, instead of a dropdown list with all the possibilities of the foreign key table.
Anyone have any ideas on how I could fix this? See code below.
Telerik grid:
#(Html.Telerik().Grid<EditableAccount>()
.Name("Grid")
.ToolBar(commands => commands.Insert().ButtonType(GridButtonType.Text).ImageHtmlAttributes(new { style = "margin-left:0" }))
.DataBinding(dataBinding => dataBinding.Ajax()
.Insert("InsertAccount", "Administration")
.Update("SaveAccount", "Administration")
.Delete("DeleteAccount", "Administration"))
.DataKeys(keys => { keys.Add(a => a.AccountId); })
.Columns(columns =>
{
columns.ForeignKey(b => b.BankId, (IEnumerable)ViewData["Banks"], "ID", "Name").Width(50);
columns.Bound(a => a.AccountNumber).Width(110);
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Image);
commands.Delete().ButtonType(GridButtonType.Image);
}).Width(16);
})
.Editable(editing => editing.Mode(GridEditMode.InLine))
.Pageable()
.Scrollable()
.Sortable()
)
Controller:
[GridAction]
public ActionResult Accounts()
{
ViewData["Banks"] = db.Banks.Select(b => new { Id = b.BankId, Name = b.Name });
return View(new GridModel(accountRepository.All()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult InsertAccount()
{
//Create a new instance of the EditableProduct class.
EditableAccount account = new EditableAccount();
//Perform model binding (fill the product properties and validate it).
if (TryUpdateModel(account))
{
//The model is valid - insert the product.
accountRepository.Insert(account);
}
//Rebind the grid
return View(new GridModel(accountRepository.All()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult SaveAccount(int id, int bankId)
{
EditableAccount account = new EditableAccount
{
AccountId = id,
Bank = db.Banks
.Where(b => b.BankId == bankId)
.Select(b => b.Name).SingleOrDefault(),
BankId = bankId
};
TryUpdateModel(account);
accountRepository.Update(account);
return View(new GridModel(accountRepository.All()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult DeleteAccount(int id)
{
//Find a customer with ProductID equal to the id action parameter
EditableAccount account = accountRepository.One(a => a.AccountId == id);
if (account != null)
{
//Delete the record
accountRepository.Delete(account);
}
//Rebind the grid
return View(new GridModel(accountRepository.All()));
}
Model:
public class EditableAccount
{
[ScaffoldColumn(false)]
public int AccountId { get; set; }
[Required]
[UIHint("GridForeignKey")]
[DisplayName("Bank")]
public int BankId { get; set; }
public string Bank { get; set; }
[Required]
[DisplayName("AccountNumber")]
public int AccountNumber { get; set; }
}
Repository:
public IList<EditableAccount> All()
{
IList<EditableAccount> result =
(from account in db.Accounts
select new EditableAccount
{
AccountId = account.AccountId,
Bank = account.Bank.Name,
BankId = account.BankId,
AccountNumber = account.AccountNr
}).ToList();
return result;
}
public EditableAccount One(Func<EditableAccount, bool> predicate)
{
return All().Where(predicate).FirstOrDefault();
}
public void Insert(EditableAccount insertedAccount)
{
Account account = new Account();
account.BankId = insertedAccount.BankId;
account.AccountNr = insertedAccount.AccountNumber;
db.Accounts.InsertOnSubmit(account);
db.SubmitChanges();
}
public void Update(EditableAccount updatedAccount)
{
Account account = db.Accounts.SingleOrDefault(a => a.AccountId == updatedAccount.AccountId);
account.BankId = updatedAccount.BankId;
account.AccountNr = updatedAccount.AccountNumber;
db.SubmitChanges();
}
public void Delete(EditableAccount deletedAccount)
{
Account account = db.Accounts.SingleOrDefault(a => a.AccountId == deletedAccount.AccountId);
db.Accounts.DeleteOnSubmit(account);
db.SubmitChanges();
}
Someone answered my question on the Telerik forums:
http://www.telerik.com/community/forums/aspnet-ajax/grid/asp-net-mvc-razor-grid-with-editable-foreign-key-dropdown-column.aspx

Resources