Entity Framework Migrations seeds duplicate rows - entity-framework

I've got two classes
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Item
{
public int Id { get; set; }
public sting Name { get; set; }
public Category Category { get; set; }
}
I have EF Migrations and the following seed:
var instockCategory = new Category() { Name = "InStock" };
var outofStockCategory = new Category() { Name = "OutOfStock" };
context.Items.AddOrUpdate(
d => d.Name,
new Item() { Name = "Item1", Category = instockCategory },
new Item() { Name = "Item2", Category = outofStockCategory },
new Item() { Name = "Item3", Category = outofStockCategory }
);
The line "d => d.Name" makes sure that based on the name of the item, there won't be duplicate records when I reseed the database.
However, the first time I execute this, two categories are created with id 1 and 2. But the second time I run this, 3 new categories are created!
Can I fix this without manually adding every single category first?

You have to use AddOrUpdate for your categories too.
var instockCategory = default(Category);
var outofStockCategory = default(Category);
context.Set<Category>().AddOrUpdate(
c => c.Name,
instockCategory = new Category() { Name = "InStock" },
outofStockCategory = new Category() { Name = "OutOfStock" }
);
context.Items.AddOrUpdate(
d => d.Name,
new Item() { Name = "Item1", Category = instockCategory },
new Item() { Name = "Item2", Category = outofStockCategory },
new Item() { Name = "Item3", Category = outofStockCategory }
);
An explicit DbSet on your Context class is not necessary.
public class Context : DbContext
{
public DbSet<Item> Items { get; set; }
}

Related

Blazorise DataGrid DataGridSelectColumn not recording change

I am trying to add select in blazorise component. Somehow it not showing me dropdown selected values. Please go through below code
Temp.razor file
#page "/temp"
#using Blazorise.DataGrid
<h3>TempComponent</h3>
<DataGrid TItem="ClassA"
Data="#Classes"
Editable="true"
RowUpdated="#OnRowUpdatedAsync">
<DataGridCommandColumn >
<EditCommandTemplate>
<Blazorise.Button Clicked="#context.Clicked"><Icon Name="IconName.Edit" /></Blazorise.Button>
</EditCommandTemplate>
<SaveCommandTemplate>
<Blazorise.Button Clicked="#context.Clicked"><Icon Name="IconName.Save" /></Blazorise.Button>
</SaveCommandTemplate>
</DataGridCommandColumn>
<DataGridColumn TItem="ClassA" Field="#nameof(ClassA.Name)" Caption="Name" Editable="true"/>
<DataGridSelectColumn TItem="ClassA" Field="#nameof(ClassA.B)" Caption="B" Editable="true">
<DisplayTemplate>
#if (#context.B != null)
{
#context.B.Name
}
</DisplayTemplate>
<EditTemplate>
<Select TValue="int" SelectedValue="#selectValue"
SelectedValueChanged="#SelectedValueChangedHandler">
#if (ClassesB != null)
{
foreach (var classB in ClassesB)
{
<SelectItem Value="#(classB.Id)">#(classB.Name)</SelectItem>
}
}
</Select>
</EditTemplate>
</DataGridSelectColumn>
</DataGrid>
#code{
int selectValue = 0;
public class ClassA {
public string Name { get; set; }
public ClassB B { get; set;}
}
public class ClassB {
public int Id { get; set; }
public string Name { get; set; }
}
List<ClassA> Classes = new List<ClassA> {
new ClassA { Name = "Class1", B = new ClassB() { Id = 1, Name = "ClassB1" }},
new ClassA { Name = "Class2", B = new ClassB() { Id = 2, Name = "ClassB2" }}
};
List<ClassB> ClassesB = new List<ClassB> {
new ClassB { Id = 1, Name = "ClassB1" },
new ClassB { Id = 2, Name = "ClassB2" }
};
protected void OnRowUpdatedAsync(SavedRowItem<ClassA, Dictionary<string, object>> e)
{
}
private void SelectedValueChangedHandler(int value)
{
Console.WriteLine("values " + value);
selectValue = value;
}
}
And Current Output Screenshot (select dropdown value not showing )
Whenever user select column then value should be change in list and also display in datagrid.
For this I took reference from https://github.com/Megabit/Blazorise/issues/561
Please help me to solve this issue .
Thanks in advance

how to serialize entities that have a one to many relationship and a pagination in entity framework?

I have a two Model for two table having foreign key relationship. I have to get record from both table in single request.
The structure of model is as follows:-
EmployeeRecord.cs
public partial class EmployeeRecord
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public tblEmployeeRecord()
{
this.countries = new HashSet<tblCountry>();
}
public int EmployeeId { get; set; }
public string Name { get; set; }
public string userName { get; set; }
public string userRole { get; set; }
public string id { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Country> countries { get; set; }
}
2.Country.cs
public partial class Country
{
public int CountryId { get; set; }
public int EmployeeId { get; set; }
public string CountryName { get; set; }
public virtual EmployeeRecord employeeRecord { get; set; }
}
EmployeeController.cs
public class EmployeeRecordsController : ApiController
{
private EstorageEntitiesforCombineView db = new EstorageEntitiesforCombineView();
// GET: api/EmployeeRecords
public IEnumerable<EmployeeRecord> GetEmployeeRecords([FromUri]PagingParameterModel pagingparametermodel)
{
var source= db.EmployeeRecords.OrderBy(a => a.EmployeeId);
// Get's No of Rows Count
if (pagingparametermodel.userRole == "HR")
{
source= (from a in db.EmployeeRecords
where a.userRole == "HR"
select a).OrderBy(a => a.EmployeeId);
}
else if(pagingparametermodel.userRole == "eStorage Admin")
{
source = (from a in db.EmployeeRecords
where a.userRole == "eStorage Admin"
select a).OrderBy(a => a.EmployeeId);
} else if(pagingparametermodel.userRole == "SharedService")
{
source = (from a in db.EmployeeRecords
where a.userRole == "SharedService"
select a).OrderBy(a => a.EmployeeId);
}
if(pagingparametermodel.toSearch == 1 && pagingparametermodel.userRole == "ALL")
{
source = (from b in db.EmployeeRecords
where b.Name.StartsWith(pagingparametermodel.name)
select b).OrderBy(a => a.EmployeeId);
}else if (pagingparametermodel.toSearch==1) {
source= (from b in db.EmployeeRecords
where b.Name.StartsWith(pagingparametermodel.name) && b.userRole==pagingparametermodel.userRole
select b).OrderBy(a => a.EmployeeId);
}
int count = source.Count();
// Parameter is passed from Query string if it is null then it default Value will be pageNumber:1
int CurrentPage = pagingparametermodel.pageNumber;
// Parameter is passed from Query string if it is null then it default Value will be pageSize:20
int PageSize = pagingparametermodel.pageSize;
// Display TotalCount to Records to User
int TotalCount = count;
// Calculating Totalpage by Dividing (No of Records / Pagesize)
int TotalPages = (int)Math.Ceiling(count / (double)PageSize);
// Returns List of Customer after applying Paging
var items = source.Skip((CurrentPage - 1) * PageSize).Take(PageSize).ToList();
// if CurrentPage is greater than 1 means it has previousPage
var previousPage = CurrentPage > 1 ? "Yes" : "No";
// if TotalPages is greater than CurrentPage means it has nextPage
var nextPage = CurrentPage < TotalPages ? "Yes" : "No";
// Object which we are going to send in header
var paginationMetadata = new
{
totalCount = TotalCount,
pageSize = PageSize,
currentPage = CurrentPage,
totalPages = TotalPages,
previousPage,
nextPage
};
// Setting Header
System.Web.HttpContext.Current.Response.Headers.Add("Paging-Headers", Newtonsoft.Json.JsonConvert.SerializeObject(paginationMetadata));
// Returing List of Customers Collections
return items;
}
}
I want the response as follows :-
[{
"tblCountries": [
{
"CountryId": 265,
"EmployeeId": 350,
"CountryName": "INWEST"
}
],
"EmployeeId": 350,
"Name": "ABC",
"userName": "abc#mail.com",
"userRole": "HR",
"id": nbh546652n45
}]
But the response is as follows:-
[{
"tblCountries": [],
"EmployeeId": 350,
"Name": "ABC",
"userName": "abc#mail.com",
"userRole": "HR",
"id": nbh546652n45
}]
I have also tried, Include(a => a.countries) as follows:-
source= (from a in db.EmployeeRecords
where a.userRole == "HR"
select a).Include(a => a.countries).OrderBy(a => a.EmployeeId);
But i am getting following error:-
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Object graph for type 'System.Collections.Generic.HashSet`1[[eStorageApi.Models.tblCountry, eStorageApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' contains cycles and cannot be serialized if reference tracking is disabled.
</ExceptionMessage>
<ExceptionType>
System.Runtime.Serialization.SerializationException
</ExceptionType>
Tried including these LOCs into webApiConfig.cs:-
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;
Still getting the same error.
Project your domain model into something else before returning it from your api. This better controls the response data format and if a property gets added (such as ssn) it doesn't get inadvertently exposed. This also has the added benefit of fixing your cyclical references.
return items.Select(i => new {
i.Name,
i.Username,
...
Countries = i.Countries.Select(c => new {
c.CountryId,
c.CountryName,
...
}
};
You need to do the relationship between employeeRecord and countries. Try with this:
source= db.EmployeeRecords.Include(e => e.countries).Select(c => c.employeeRecord)
.Where(a => a.userRole == "HR");
And when you do the request you need specify text: Application/Json

TryGetObjectByKey() doesn't return entity with Added state (EF 6)

1. Q #1
I have POCO
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public ICollection<Version> Versions { get; set; }
}
In my DbContext I have func
public void AttachUpdated<T>( T objectDetached) where T : class
{
var objContext = ((IObjectContextAdapter)this).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, objectDetached);
object original;
if (objContext.TryGetObjectByKey(entityKey, out original))
objContext.ApplyCurrentValues(entityKey.EntitySetName, objectDetached);
else
objContext.AddObject(entityKey.EntitySetName, objectDetached);}
So i want to add some Products to context
var p1 = new Product(){Id = "1", Name = "Product 1";}
var p2 = new Product(){Id = "1", Name = "Product 1";}
ctx.AttachUpdated(p1);
And when i try to add identical Product (with same Id as first product) TryGetObjectByKey() doesn't find already added product.
ctx.AttachUpdated(p2);
Therefore I need to use ctx.SaveChanges() or AccseptAllChanges() and then
ctx.AttachUpdated(p2) work as expected.
I can't understand where i have problem in my code.
Q #2
var p1 = new Product() { Id = "1", Name = "Product 1" };
var v1 = new Version() { Number = "1.0", Type = "Release", ReleaseDate = "01/01/13" };
p1.Versions = new List<Version>();
p1.Versions.Add(v1);
ctx.AttachUpdated(p1);
And then i see that v1 was addet to DbSet(). But why? And how i could prevent such bihavior. I need to add only Product and not related Versions.
public void AttachOrUpdate<T>(T entity) where T : class
{
var objContext = ((IObjectContextAdapter)context).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);
var original = this.context.Set<T>().Find(entityKey.EntityKeyValues[0].Value);
if (original != null)
{
this.context.Entry<T>(original).CurrentValues.SetValues(entity);
}
else
objContext.AddObject(entityKey.EntitySetName, entity);
}

Why do I get different values from my EntitySet depending on how I LINQ to it?

In debugging the issue in this thread: InvalidCastException when querying nested collection with LINQ I found out that something is wrong with how my Category EntitySet is populated. After selecteding a Category and throwing this exception to see what's going on I get this:
throw new Exception("CID: " + cat.CategoryID +
" LCID: " + cat.LocalizedCategories.First().LocalizedCategoryID +
" CID from LC: " + cat.LocalizedCategories.First().Category.CategoryID);
CID: 352 LCID: 352 CID from LC: 191
What am I doing wrong that causes CategoryID to have different values depending on how I LINQ to it? It should be 191, and not the same value as the LocalizedCategoryID.
This is the code I use to get the Category:
int categoryId = 352; // In reality this comes from a parameter and is supposed
// to be 191 to get the Category.
var cat = categoriesRepository.Categories.First(c => c.CategoryID == categoryId);
This is my domain object with some unrelated stuff stripped:
[Table(Name = "products")]
public class Product
{
[HiddenInput(DisplayValue = false)]
[Column(Name = "id", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int ProductID { get; set; }
[Required(ErrorMessage = "Please enter a product name")]
[Column]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter a description")]
[DataType(DataType.MultilineText)]
[Column(Name = "info")]
public string Description { get; set; }
private EntitySet<Category> _Categories = new EntitySet<Category>();
[System.Data.Linq.Mapping.Association(Storage = "_Categories", OtherKey = "CategoryID")]
public ICollection<Category> Categories
{
get { return _Categories; }
set { _Categories.Assign(value); }
}
}
[Table(Name = "products_types")]
public class Category
{
[HiddenInput(DisplayValue = false)]
[Column(Name = "id", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int CategoryID { get; set; }
public string NameByCountryId(int countryId)
{
return _LocalizedCategories.Single(lc => lc.CountryID == countryId).Name;
}
private EntitySet<LocalizedCategory> _LocalizedCategories = new EntitySet<LocalizedCategory>();
[System.Data.Linq.Mapping.Association(Storage = "_LocalizedCategories", OtherKey = "LocalizedCategoryID")]
public ICollection<LocalizedCategory> LocalizedCategories
{
get { return _LocalizedCategories; }
set { _LocalizedCategories.Assign(value); }
}
private EntitySet<Product> _Products = new EntitySet<Product>();
[System.Data.Linq.Mapping.Association(Storage = "_Products", OtherKey = "ProductID")]
public ICollection<Product> Products
{
get { return _Products; }
set { _Products.Assign(value); }
}
}
[Table(Name = "products_types_localized")]
public class LocalizedCategory
{
[HiddenInput(DisplayValue = false)]
[Column(Name = "id", IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int LocalizedCategoryID { get; set; }
[Column(Name = "products_types_id")]
private int CategoryID;
private EntityRef<Category> _Category = new EntityRef<Category>();
[System.Data.Linq.Mapping.Association(Storage = "_Category", ThisKey = "CategoryID")]
public Category Category
{
get { return _Category.Entity; }
set { _Category.Entity = value; }
}
[Column(Name = "country_id")]
public int CountryID { get; set; }
[Column]
public string Name { get; set; }
}
This (in class Category) looks weird:
[System.Data.Linq.Mapping.Association(Storage = "_LocalizedCategories",
OtherKey = "LocalizedCategoryID" )] // ????
public ICollection<LocalizedCategory> LocalizedCategories
Category has a collection of LocalizedCategorys, which means that in the database the table products_types_localized has a foreign keyCategoryID. That field should be the "OtherKey". How was this mapping generated?

Cannot implicitly convert type 'int' to Category

Here's my model
public class Category
{
public int CategoryId { get; set; }
public string Description { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; }
}
Mapping:
modelBuilder.Entity<Category>()
.HasMany(x => x.Children)
.WithOptional(y => y.Parent)
.Map(m => m.MapKey("ParentId"));
Here's how I add data to it:
var a = new List<Category>
{
new Category { Description = "Animals" }
new Category { Description = "Dog", Parent = 1 } <<<-ERROR
}
a.ForEach(s => context.Categories.Add(s));
context.SaveChanges();
How can I add the CategoryId from the 1st row to the second row???
Sorry, I think this is a very basic question, but I cant figure out how to do it
Thanks
You cant use 1, because 1 is an integer and not of the type Category. You need to declare the category outside the lists declaration and add it to the list later:
List<Category> list = new List<Category>();
Category a = new Category();
Category b = new Category(){ Parent = a};
list.Add(a);
list.Add(b);
Note that this is just some code I made up and I didnt test it, but you get the idea!
Another way:
Category a = new Category();
Category b = new Category();
a.Children.Add(b);
context.Categories.Add(a);
context.SaveChanges();
It does the wiring up of IDs for you.
(This is also untested.)