Need examples for Entityframework.HierarchyId - entity-framework

I found EF.HierarchyId on NuGet. Looks like EF6.1.1 supports it now?
Wondering if there are any documentation I can see about how to use it.
For example:
-does it work with edmx? may it import models with hierarchyId column?
-some linq examples? what commands are supported?
Thank you.

From version 6.1.3-alpha1 you can add multiple entities, no more ArgumentException "At least one object must implement IComparable." (This is the same as version 6.1.2, only contains the fix for this problem)
But I can't reproduce the "And finding by key is not working." problem.
If you want to create the model from database you need to install a custom version of EntityFramework Tools. The source code is on CodePlex: https://entityframework.codeplex.com/SourceControl/network/forks/zgabi/EfHierarchyId
Edmx should work, if you find any problem, please report it to zavarkog(X)gmail.com

I just installed it, I think it's still under development.
When creating from database first.
The data type 'hierarchyid' is currently not supported for the target
.NET Framework version; the column 'Id' in table 'dbo.Users' was
excluded.
The column 'Id' on the table/view 'dbo.Users' was excluded, and is a
key column. The table/view has been excluded. Please fix the entity
in the schema file, and uncomment.
When creating from code first.
public class AppContext : DbContext
{
public DbSet<User> Users { get; set; }
}
public class User
{
public HierarchyId Id { get; set; }
public string Name { get; set; }
}
It worked, the database and the table were successfully created, but adding multiple objects at once throws error.
using (var db = new AppContext())
{
db.Users.Add(new User { Id = HierarchyId.Parse("/"), Name = "President" });
// Working.
db.SaveChanges();
db.Users.Add(new User { Id = HierarchyId.Parse("/1/"), Name = "VP 1" });
db.Users.Add(new User { Id = HierarchyId.Parse("/2/"), Name = "VP 2" });
db.Users.Add(new User { Id = HierarchyId.Parse("/3/"), Name = "VP 3" });
// ArgumentException "At least one object must implement IComparable."
db.SaveChanges();
}
And finding by key is not working.
using (var db = new AppContext())
{
var id = HierarchyId.Parse("/");
var user1 = db.Users.Find(id); // null
var user2 = db.Users.FirstOrDefault(u => u.Id == id); // null
var user3 = db.Users.FirstOrDefault(u => HierarchyId.Compare(u.Id, id) == 0); // null
var user4 = db.Users.AsNoTracking().ToArray()
.FirstOrDefault(u => u.Id == id); //not null
var user5 = db.Users.AsNoTracking().ToArray()
.FirstOrDefault(u => HierarchyId.Compare(u.Id, id) == 0); //not null
}

Related

ef6 set only foreign key property of navigation property while saving

When I save entity which has navigation property which I use as foreign key:
this.HasRequired<Role>(c => c.Role).WithMany().Map(c => c.MapKey("role_id"));
I set only foreign key property of this navigation property (I get it from web page) thereby other properties of this navigation property are empty, but they have required restrictions:
this.Property(c => c.RoleName).IsRequired();
It's the reason why I get "dbentityvalidationexception" exception with error "field is required".
Is it possible to solve this problem by somehow?
Or I must get full entity for that navigation property from DB, set navigation property of entity which I save and then save my initial entity (It works now, but it doesn't look like good solution)?
Thanks in advance.
This is MS MVC action where I handle the model:
[HttpPost]
public async Task<ActionResult> AddAsync(Staff staff)
{
await staffService.InsertAsync(staff);
return RedirectToAction("Index");
}
and that part of view where I set the property:
<dt>
#Html.Label("Role")
</dt>
<dd>
#Html.DropDownListFor(_=>_.Role.Id, new SelectList(ViewBag.Roles, "Id", "RoleName"), "- Please select a Role -")
</dd>
this is the type of model "Staff"
public class Staff
{
public Staff()
{
Name = new Name();
}
public int Id { get; set; }
public Name Name { get; set; }
...
public virtual Role Role { get; set; }
...
}
I have found more or less good solution for that problem.
I have set all navigation property for which I have only foreign key property as
EntityState.Unchanged.
Now I have this method for saving only specific entity (Staff)
public virtual Staff InsertStaff(Staff entity)
{
context.Entry(entity.Role).State = EntityState.Unchanged;
context.Entry(entity.Maneger).State = EntityState.Unchanged;
SetStaffNavigationPropertiesUnchanged(entity);
return dbSet.Add(entity);
}
and this for saving full graph (in base generic class):
public virtual TEntity InsertGraph(TEntity entity)
{
return dbSet.Add(entity);
}
using "EntityState.Unchanged" (I will show in simplest way) lets me saving Staff when Role has only foreign key property filled (Role has RoleName required property)
using (ClinchContext context = new ClinchContext())
{
var role = new Role { Id = 1 };
Staff staff = context.Staffs.Add(new Staff
{
Name = new Name { Firstname = "FN", Surname = "S", Patronymic = "P" },
Email = "E",
Login = "L",
IsDeleted = false,
Role = role,
Maneger = null//nullable for now
});
context.Entry(staff.Role).State = EntityState.Unchanged;
context.SaveChanges();
}
If someone has more proper solution I would be happy to know, Thank you.

Getting metadata error while trying to save values to DB using Entity Framework

I am little new to entity framework.
I created a sample applicaiton to save values to the DB table using entity framework.
I have given below my code snippet.
[HttpPost]
public ActionResult Employee(EmployeeModel model)
{
if (ModelState.IsValid)
{
// insert
var _insert = new EmployeeModel
{
FirstName = model.FirstName,
LastName = model.LastName,
Initial = model.Initial,
DOB = model.DOB,
DOJ = model.DOJ,
Role = model.Role,
City = model.City,
State = model.State,
Country = model.Country
};
db.Employee_Master.AddObject(_insert);
db.SaveChanges();
return View();
}
I am getting the below compilation error in "db.Employee_Master.AddObject(_insert);" line.
"The best overloaded method match for 'System.Data.Objects.ObjectSet.AddObject(FinancialManagementSystem.Models.Employee_Master)' has some invalid arguments S:\SourceCode\FinancialManagementSystem\FinancialManagementSystem\Controllers\EmployeeController.cs"
What am I doing wrong?
Use Add instead AddObject.
EmployeeModel is model to transfer data between View and Controller.
Which is the table where you want to insert into?
Take example, you have table Employee.
public DbSet<Blog> Employees { get; set; }
db.Employees.Add(new Employee {FirstName = model.FirstName,....});
But if you still want to use AddObject:
db.Employee_Master.AddObject(new Employee {...}); //Employee is the name of the class represent for Employee_Master, maybe is Employee_Master of your problem.
Here is the different between Add and AddObject: http://blog.cincura.net/232485-someentityset-addobject-vs-addtosomeentityset-methods/

Entity Framework many to many relationship Delete

I have a class that is called User. It has a property called Roles. Each user has many roles and each role can be assigned to many users.
The relationship is established by a 3rd table. I'd like to be able to remove a role from a User. However, I am calling the database twice. Once to fully load the "User" role and once to delte the role.
var user = this.Users.Include(f => f.Roles)
.SingleOrDefault(f => f.CustomerID == customerId && f.UserID == userId);
if (user != null)
{
var role = user.Roles.FirstOrDefault(f => f.RoleID == roleId);
if (role != null)
{
user.Roles.Remove(role);
return this.SaveChanges() > 0;
}
}
I tried doing this but it didn't work.
var user = new User { CustomerID = customerId, UserID = userId };
this.Users.Attach(user);
var role = new Role { RoleID = roleId };
this.Roles.Attach(role);
user.Roles.Add(role);
user.Roles.Remove(role);
return this.SaveChanges() > 0;
My Context has a DbSet<User> and DbSet<Role>. I don't have one for UserRole and I don't intend to have it.
Am i doing it right and do I need to always do 2 database calls?
-- User Class
[DataContract(Namespace = "urn:AES/Schemas/2014/09", IsReference = true)]
public class User
{
.....
[DataMember]
public List<Role> Roles { get; set; }
[DataMember]
public bool Active { get; set; }
}
The Mapping
this.HasMany(u => u.Roles)
.WithMany()
.Map(m =>
{
// The "left" key is the one specified in the HasMany method; the "right" key is the one specified in the WithMany method.
m.MapLeftKey(new string[] { "CustomerID", "UserID" });
m.MapRightKey("RoleID");
m.ToTable("UserRoles");
}
);
// Table & Column Mappings
this.ToTable("Users");
}
Curious why you don't model those other tables like UserRoles, are you just lazy? =)
But seriously, what you're doing is fine however for mass deletes EF doesn't have a bulk delete capability, you are better off simply executing the DELETE statement directly, like this:
using (System.Data.Common.DbCommand cmd = Context.Database.Connection.CreateCommand())
{
cmd.CommandText = #"DELETE FROM Table WHERE KeyColumn = #Id";
YourSqlHelper.AddParameter(cmd, "#Id", id);
cmd.ExecuteNonQuery();
}

Entity Framework Code First: how to map multiple self-referencing many-to-many relationships

I have created an entity type that has multiple collection properties that reference items of the same type. In other words, it reflects a single database table in which the rows are arbitrarily grouped, such that a row may appear in multiple groups.
In the following simplified example, the Person class has Brothers and Sisters collection properties that also reference Person entities:
public class Person
{
public Person()
{
Brothers = new Collection<Person>();
Sisters = new Collection<Person>();
}
[Key]
public string Name { get; set; }
public int Age { get; set; }
public virtual ICollection<Person> Brothers { get; set; }
public virtual ICollection<Person> Sisters { get; set; }
}
Entity Framework seems to think that this is a valid model, but interprets it to create a single PersonPersons join table, which fails to reflect the separation of brother and sister relationships.
I assume the solution is to use the fluent API to explicitly map separate join tables for the two relationships but, despite extensive experimentation, I have been unable to get this to work.
Any suggestions please?
Thanks,
Tim
By adding this in the DbContext.OnModelCreating method:
UPDATE Added table-naming map according to nameEqualsPNamePrubeGoldberg's comment above:
modelBuilder.Entity<Person>().HasMany(x => x.Brothers).WithMany()
.Map(x => x.ToTable("Person_Brothers"));
modelBuilder.Entity<Person>().HasMany(x => x.Sisters).WithMany()
.Map(x => x.ToTable("Person_Sisters"));
I got this unit test to pass
[TestMethod]
public void TestPersons()
{
var brother = new Person() { Name = "Brother 1", Age = 10 };
var sister = new Person() { Name = "Sister 1", Age = 12 };
var sibling = new Person() { Name = "Sibling 1", Age = 18 };
sibling.Brothers.Add(brother);
sibling.Sisters.Add(sister);
using (var db = new MyDatabase())
{
db.Persons.Add(brother);
db.Persons.Add(sister);
db.Persons.Add(sibling);
db.SaveChanges();
}
using (var db = new MyDatabase())
{
var person = db.Persons
.Include(x => x.Sisters)
.Include(x => x.Brothers)
.FirstOrDefault(x => x.Name.Equals(sibling.Name));
Assert.IsNotNull(person, "No person");
Assert.IsTrue(person.Brothers.Count == 1, "No brothers!");
Assert.IsTrue(person.Sisters.Count == 1, "No sisters");
}
}
That also creates the link tables you're talking about.

Adding a new entity to collection in attached entity causes ConcurrencyException

I have simplified the code below to show the root of the problem. My real code is using GenericRepository and UnitOfWork pattern but I get the same exception with this simplified code too.
I am using Entity Framework 6, Code First
It uses the following POCO entities
public class Order
{
public int Id {get;set;}
public virtual List<OrderProducts> OrderProducts {get;set;}
...
}
public class Product
{
public int Id {get;set;}
...
}
public class OrderProduct
{
public int OrderId {get;set;}
public int ProductId {get;set;}
public int Quantity
public virtual Order Order { get; set; }
public virtual Product Product{ get; set; }
}
The user is able to create a new product and add it to the order products on the same screen.
//Pull an order from the database:
var existingOrder = db.Orders.FirstOrDefault(x => x.Id == inputModel.OrderId);
//Iterate the OrderProductInputModels (IMs) in the Inputmodel
foreach (var orderProductIM in inputModel.OrderProductIMs )
{
var orderProduct = existingOrder.OrderProducts.SingleOrDefault(o => o.Id == orderProductIM.Id);
//if its an existing order product (already in db)
if (orderProduct != null)
{
//just update its property values
}
//if it has been added
else
{
//we need to create a new product first
var newProduct= new Product() { <set some properties> };
orderProduct= new OrderProduct()
{
Product=newProduct,
Order=existingOrder
}
//Add the OrderProduct to the order
existingOrder.OrderProducts.Add(orderProduct);
}
db.SaveChanges();
On save changes, I get the following error.
[System.Data.Entity.Infrastructure.DbUpdateConcurrencyException] = {"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."}
Why is this?
I expected entity framework to see that the existingOrders nested properties were newly added and unattached, update the order and create the new OrderProduct and Product.
Should it not be other way around in your if clause as you are checking for null ( then only it is a new order product else update. Issue is here:
//if its an existing order product (already in db)
if (orderProduct == null)
{
//just update its property values
}
//if it has been added
else
{
When you are looping around all the OrderProducts, you are constantly updating the database but the existingOrder object is not getting refreshed. Update that or add all the objects first and then update the database.
Finally solved it by creating a test project and reverse code first engineering the database. Noticed that OrderProduct entity was not generated. On inspecting the database, the primary key was not set. Once I set the primary key in the database, the issue was resolved. Thanks for all the suggestions.