Using Find in CodeFirst (EntityFramework) to get non-primary keys - entity-framework-4.1

My understanding is that find only takes the primary key as the parameter. That works great if the value you are looking for is actually the primary key. In my case, I have a class like this:
public class Chamber
{
[Key]
public int Id {get;set;}
public string ChamberName { get; set; }
}
I want to check whether a given ChamberName exists in either my context or the database itself. How can I do that? Do I have to somehow enumerate of the context myself first, then, look it up in the database with a call like db.Chambers.where(a=>a.ChamberName.equals...?
I can see it working well if ChamberName is my primary key, but it is not.
THanks,

There is a property called Local in the DbSet. You can query that first to find entities loaded to the context.
var entity = db.Chambers.Local.Where(/**/).SingleOrDefault();
if (entity == null)
{
entity = db.Chambers.Where(/**/).SingleOrDefault();
}

You can't use the .Find() method - but how about:
public Chamber FindByChamberName(string chamberName)
{
using(MyDbContext ctx = new MyDbContext())
{
Chamber result = ctx.Chambers
.FirstOrDefault(c => string.Compare(c.ChamberName, chamberName, true));
return result;
}
}
You don't have to manually enumerate anything - just retrieve the first occurence of a chamber by that name - or none.
If you just need to know whether a given chamber (specified by its ChamberName) exists or not, you could use the .Any() method in Linq:
using(MyDbContext ctx = new MyDbContext())
{
return ctx.Chambers.Any(c => string.Compare(c.ChamberName, chamberName, true));
}

Related

Invalid object graph in Linq to SQL

I have a GiftCards table in my DBML that has a related property called Audit. The Audits are stored in a separate table. Each Audit has a related Person associated to it. There is also a Persons table. The relationships are set up and are valid in my DBML.
The problem is that when I instantiate a new Gift Card I also create a new related Audit in the OnCreated() method. But at the same time, I also create a related Person when I instantiate a new Audit. The Person is the current user. Actually the Audit's OnCreated method checks if the user already exists.
The problem is that when I instantiate a new gift Card, it also creates an associated Audit, which is fine, and the Audit creates an associated Person. But the Person already exists in the database. When I look at the data context's GetChangeSet(), it shows 3 inserts. The Persion should not show as an insert because he already exists in the database.
Here is how I implemented this. It is an MVC application where the Controller receives a gift card:
[HttpPost]
public ActionResult Save(GiftCardViewModel giftCard)
{
if (ModelState.IsValid)
{
GiftCard gc = GiftCardViewModel.Build(giftCard);
repository.InsertOrUpdate(gc);
repository.Save();
return View("Consult", new GiftCardViewModel(repository.Find(gc.GiftCardID)));
}
else
SetupContext();
return View("_Form", giftCard);
}
The Gift Card has:
partial class GiftCard
{
partial void OnCreated()
{
// Set up default audit.
this.Audit = new Audit();
}
}
The Audit class has:
partial void OnCreated()
{
// Setup timestamp
this.Timestamp = DateTime.Now;
this.Person = Person.GetPerson(Membership.GetUser().UserName);
}
And finally, my Person class has:
public static Person GetPerson(String username)
{
using (GiftCardDBDataContext database = new GiftCardDBDataContext())
{
// Try to get the person from database
Person person = database.Persons.SingleOrDefault(personData => SqlMethods.Like(personData.Username, username));
if (person == null)
{
person = new Person()
{
Username = username,
FullName = "Full name TBD"
};
database.Persons.InsertOnSubmit(person);
database.SubmitChanges();
}
// Return person data
return person;
}
}
When I create a new gift card, I always get an error saying that it's attempting to insert a duplicate person in the Persons table. I don't understand because my static class specifically checks if the Person already exists, if yes, I return the Person and I don't create a new one. Yet, the GetChangeSet() shows three inserts including the Person, which is wrong.
What am I doing wrong here?
I believe your issue here is that you're using multiple contexts. You have one being created by your repository, and another is created in the static method on your Person object. You also aren't making any effort to attach the Person created/retrieved from the other context to the context of your Audit class.
You should look at a single unit of work, a single DataContext class, and perform all your work in that.

linq to sql insert failed due to FK constraint, how can I submit all related objects at the same time?

I am using linq to sql and trying to insert new objects. Here's an example of my code:
public class Farm(){
public List<FarmAnimals> FarmAnimals ();
public string FarmName;
}
Public class FarmAnimal(){
public string name;
}
public void Insert(FarmModel farm)
{
using (var context = new FarmDataClassesDataContext())
{
context.Farms.InsertOnSubmit(new Farm { FarmName = farm.FarmName });
foreach (var animal in farm.FarmAnimals)
{
context.Responses.InsertOnSubmit(new FarmAnimal {name = animal.name, farmID = farm.Id });
}
context.SubmitChanges();
}
}
I get a FK constraint error when it tries to insert a farm animal, referencing the farmID (which equals 0). Since the farm hasn't been inserted yet, it doesn't have an ID for the farmanimals to refer to. How do I get the farm submitted so that the farm animals FK can be properly set?
Thanks,
The problem is you are thinking SQL way, and not ORM way.
The SQL way assigns a foreign key:
InsertOnSubmit(new FarmAnimal {name = animal.name, farmID = farm.Id });
The ORM way assigns entities. Notice the part between ** ** in the following code sample.
var myFarm = new Farm { FarmName = farm.FarmName };
Con...InsertOnSubmit(myFarm)
Con...InsertOnSubmit(new FarmAnimal {name = animal.name, **farm = myFarm**});
Because you assign the entity, proper insertions will be handled and as a bonus in one transaction.
You have to submitChanges before inserting the FarmAnimals, and you need to have the column auto creating the key with autoincrement. Also make sure that the column in the table object in the DBML-file auto updated on insert.
public class Farm(){
public List<FarmAnimals> FarmAnimals ();
public string FarmName;
}
Public class FarmAnimal(){
public string name;
}
public void Insert(FarmModel farm)
{
using (var context = new FarmDataClassesDataContext())
{
Farm newFarm = new Farm { FarmName = farm.FarmName }; <--- New
context.Farms.InsertOnSubmit(newFarm); <---Edited
context.SubmitChanges(); <--- New
foreach (var animal in farm.FarmAnimals)
{
context.Responses.InsertOnSubmit(new FarmAnimal {name = animal.name, farmID = newFarm.Id }); <--- Edited
}
context.SubmitChanges();
}
}
To expand on Pleun's answer: You need to assign entities rather than IDs. The property that you're trying to assign to is mapped to a column with a foreign-key constraint, so it won't work for assigning an entity--to do that you instead need a property that maps to the relationship between two tables. How you do that varies by the tool you're using.
For the purposes of this explanation, I'll assume that you have a Farm table with a primary-key column called ID and another column called Name; and a FarmAnimal table with a foreign-key column named FarmFK that points to the Farm table and another column called Name.
Based on the DataContext part of the name I assume you're using the O/R Designer tool built in to Visual Studio, right? If so, go to the O/R Designer by opening your dbml file, select the association (represented as an arrow) between Farm and FarmAnimal (if there's not already an arrow, select the Association tool from the Toolbox and drag from Farm to FarmAnimal), and view the association's properties. You'll see properties called "Child Property" and "Parent Property". (The parent table is the table with the primary key in the relationship.) Expand those to see the "Name" sub-property of each. Those are the property names you'd use in code to access the two ends of the relationship. Typically they have poorly-chosen names based on the automatic generation, so rename them as needed. In this case let's rename the parent property's name to Animals and the child property's name to 'Farm'. You'd then be able to do the following in your code:
public void Insert(FarmModel farmModel)
{
using (var context = new FarmDataClassesDataContext())
{
var farm = new Farm
{
Name = farmModel.FarmName
};
context.Farms.InsertOnSubmit(farm);
foreach (var animalModel in farmModel.FarmAnimals)
{
var critter = new FarmAnimal
{
Name = animalModel.name,
Farm = farm
}
context.Responses.InsertOnSubmit(critter);
}
context.SubmitChanges();
}
}
Does that answer your need?

Hibernate saving self Reference parent/child

I'm using Spring 3.2, Hibernate 4 and MySQL. I have a self referencing class called Lecturers which has annotations implementing a parent/child one to many relationship. I have a problem with implementing a controller and form for saving a parent and child from the same table. It's a self-referencing class.
My DB:
CREATE TABLE `lecturers` (
`lecturer_id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NULL DEFAULT NULL,
`email` VARCHAR(255) NULL DEFAULT NULL,
`checker_id` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`lecturer_id`),
FOREIGN KEY (`checker_id`) REFERENCES `lecturers` (`lecturer_id`)
The Java class
#ManyToOne
#JoinColumn(name="checker_id")
private Lecturer checker;
#OneToMany(cascade = CascadeType.ALL, mappedBy="checker", orphanRemoval=true)
private List<Lecturer> lecturers = new ArrayList<Lecturer>();
And the class also has this method
#Transient
public void addLecturer(Lecturer lecturer) {
if(lecturers == null) {
lecturers = new ArrayList<Lecturer>();
//lecturers = new HashSet<Lecturer>();
}
lecturer.setChecker(this);
lecturer.setLecturers(lecturers);
//lecturer.setLecturers(lecturers);
lecturers.add(lecturer);
// TODO Auto-generated method stub
}
I then set up a DAO and Service layer for implementing a CRUD operations. The create method is this:
Session session = sessionFactory.getCurrentSession();
transaction = session.beginTransaction();
// Create new lecturers
Lecturer lecturer1 = new Lecturer();
lecturer1.setName(name);
lecturer1.setEmail(email);
Lecturer lecturer2 = new Lecturer();
lecturer2.setName(name);
lecturer2.setEmail(email);
// Create new checker
Lecturer checker = new Lecturer();
checker.setName(name);
checker.setEmail(email);
checker.setChecker(checker);
List<Lecturer> lecturers = new ArrayList<Lecturer>();
lecturers.add(lecturer1);
lecturers.add(lecturer2);
lecturer1.setChecker(checker);
lecturer2.setChecker(checker);
checker.addLecturer(lecturer1);
checker.addLecturer(lecturer2);
checker.setLecturers(lecturers);
session.save(checker);
session.save(lecturer1);
session.save(lecturer2);
My requirement is now to provide a form that will be used to match a parent (Checker) to one or more children (Lecturers) and save the match to the database. I'm asking how I should go about saving the relationship. Should I create the parent and children separately, then match a parent using the id to a children selected from say a drop down list? I'm not sure how to make sure the relationship between a checker and its respective lecturers is saved.
I then created a main class for testing the relationship and to see if it works. Inserting data into the db works but when I want to list it I get this:
Name: Mark
Email: ma#msn.com
Checker: com.professional.project.domain.Lecturer#439942
ID: 22
I should get the name of the checker back which I already added but it's not coming back.
I would appreciate some help on how to proceed.
First of all, your addLecturer() method has a bug. It shouldn't set the lecturers list of the child to the current lecturer's list:
public void addLecturer(Lecturer lecturer) {
if (lecturers == null) {
lecturers = new ArrayList<Lecturer>(); // OK : lazy initialization
}
lecturer.setChecker(this); // OK : set the parent of the child to this
lecturer.setLecturers(lecturers); // this line should be removed : the child's children shouldn't be the same as this lecturer's children
lecturers.add(lecturer); // OK : ad the child to the list of children
}
When you get a lecturer, you obtain the following as the checker :
Checker: com.professional.project.domain.Lecturer#439942
The above is just the result of the call to the default toString() method on the checker. To get its name, call getName() on the checker. If you want the toString() method to return the name, then implement it that way:
#Override
public String toString() {
return name;
}

PetaPoco and Many-to-One, One-to-Many and Many-to-Many relations

PetaPoco has introduced Multi-POCO queries in experimental form (for now). As their blog post suggests and the code it provides this looks nice and all in One-to-One relations when we load multi POCOs per row as long as they don't repeat over the records.
What happens when at least one side is many relation? Actually example code is Many-to-One relational data.
Example code is clearly a Many-to-One relation. I haven't tested any PetaPoco code but what does the provided code on the blog post do? Does every Article have their own User object instance even though some may be the same user or do they share the same user object instance?
And what about other Many relation types? How do they work of they work at all?
Usually I map these one-to-many queries myself like the following example.
[TableName("Blogs"), PrimaryKey("BlogId")]
public class Blog {
public int BlogId {get;set;}
public string Title {get;set;}
[Ignore]
public IList<Post> Posts {get;set;}
}
[TableName("Posts"), PrimaryKey("PostId")]
public class Post {
public int PostId {get;set;}
public int BlogId {get;set;}
public string Subject {get;set;}
public string Content {get;set;}
}
public class FlatBlogPost {
public int BlogId {get;set;}
public string Title {get;set;}
public int PostId {get;set;}
public string Subject {get;set;}
public string Content {get;set;}
}
There are two ways I could display a list of posts for one blog or without too much work, all blogs.
1.Two queries -
var Blog = Db.Query<Blog>(1);
var Posts = Db.Query<Post>("where BlogId = #0", 1);
2.One query =
var flat = Db.Query<FlatBlogPost>("select b.blogid, b.title, p.postid, p.subject,
p.content from blogs b inner join posts p on b.blogid = p.blogid where
b.blogid = #0", 1);
var blog = flat
.GroupBy(x=> new { x.BlogId, x.Title })
.Select(x=> new Blog {
BlogId = x.Key.BlogId,
Title = x.Key.Title,
Posts = x.Select(y=> new Post{
PostId = y.PostId,
BlogId = x.Key.BlogId,
Subject = y.Subject,
Content = y.Content
}).ToList()
});
However usually in number 2 I would map directly from the FlatBlogPost object to my viewmodel for which I need to display the data.
Update
Check out these helpers which extend PetaPoco to support basic One-to-Many and Many-to-One queries. schotime.net/blog/index.php/2011/08/21/petapoco-one-to-many-and-many-to-one/ https://schotime.wordpress.com/2011/08/21/petapoco-one-to-many-and-many-to-one/
My 'One to Many' recipe for Petapoco is below. The docs are not clear enough for me. Create a db connection in Linqpad, it will show you all Navigation properties you can add to generated Petapoco poco classes. Execute the same SQL in Linqpad, to make sure it gets the data you expect.
// subclass the generated Parent table pocos, add navigation prop for children
[ResultColumn] public List<DecoratedChild> Child { get; set; }
// subclass the generated Child table pocos, add navigation prop for parent
[ResultColumn] public DecoratedParent Parent { get; set; }
// to get children with parent info
List<DecoratedChild> children = db.Fetch<DecoratedChild, DecoratedParent>(SELECT child.*, parent.* from ...)
// to get children with parent info, using PetapocoRelationExtensions
List<Child> children = db.FetchManyToOne<Child, Parent>(child => child.ID, "select child.*, parent.* from ...
// to get parents with children info, using PetapocoRelationExtensions
List<Parent> parents = db.FetchOneToMany<Parent, Child>(par => par.ID, child => child.ID != int.MinValue, "select parent.*, child.* from ...
SQL select order important, same as in Fetch types list !!!
navigation props will have parent or children data ...
with 3 levels the call will be like:
List<DecoratedGrandChild> grandChildColl = db.Fetch<DecoratedGrandChild, DecoratedChild, DecoratedParent>(SELECT grandch.* , child.*, parent.* from ...)
Personally I don't think you can avoid another database call to get the comments. You could get a list of all comments for the 10 articles (in the same order the articles are stored) by using an IN clause, and loop through them adding them to each article.comments as you go along and the comment.articleid changes. The only way I can see getting this information in a single sql call would be to use a join but then you'd get duplicate article details for each comment, so maybe this isn't a problem with petapoco, just one of those things that'll never be perfect

enumerate entity children

I need to create a method that will take Linq-to-sql entity and return a list of all it's children(only 1st generation) entitysets, without any data that entitysets contain, just names. Is there any way I can do that? Thanks
Well, if I understand your question correctly, you can inspect the meta-model for a data context instance. The meta-model describes the tables, columns and associations in your model. Basically you want to look at associations on a table where the association is 1-to-many.
This doesn't involve retrieving any data, as you are not actually working with entity instances, just the information that describes them.
This code should do it:
public static string[] GetChildEntities<T>(DataContext context, T entity)
{
var mapping = context.Mapping.GetTable(typeof(T));
return mapping.RowType.Associations.Where(a => a.IsMany)
.Select(a => a.ThisMember.Name).ToArray();
}
This will return the names of any properties that expose the EntitySet instances for the given parent entity.
EDIT
This code finds the first 1->* association between the parent and child entities based on the meta-model, retrieves the value of the EntitySet property on the parent entity, and adds the child entity to that set. This should work for most basic LINQ to SQL implementations.
public static void AddChild<P, C>(DataContext context, P parent, C child)
where P : class
where C : class
{
var parentMapping = context.Mapping.GetTable(typeof(P));
var childAssociation =
parentMapping.RowType.Associations
.Where(a => a.IsMany && a.OtherType.Type == typeof(C))
.FirstOrDefault();
if (childAssociation != null)
{
var entitySet = (EntitySet<C>) childAssociation.ThisMember
.MemberAccessor
.GetBoxedValue(parent);
entitySet.Add(child);
}
}