How To Save Navigation Properties in the Entity Framework - asp.net-mvc

I'm trying to use the repository pattern to save an entity using the Entity Framework. I'm unclear on how to save the Navigation Properties (like Account below). Can anyone shed some light on this. Especially how one would set the AccountId from an MVC controller all the way through to the repository where it's saved.
Thanks!
--- Sample Code ---
public void SavePerson(Person person)
{
if (person != null)
{
using (xxxxxxEntities bbEntities = new xxxxxxEntities())
{
//see if it's in the db
Person cPerson;
ObjectQuery<Person> persons = bbEntities.Person;
cPerson = (from p in persons
where p.PersonId == person.PersonId
select p).FirstOrDefault() ?? new Person();
//synch it
cPerson.Account.AccountId = person.Account.AccountId; //<=== ????
cPerson.Active = person.Active;
cPerson.BirthDay = person.BirthDay;
cPerson.BirthMonth = person.BirthMonth;
cPerson.BirthYear = person.BirthYear;
cPerson.CellPhone = person.CellPhone;
cPerson.CreatedBy = person.CreatedBy;
cPerson.CScore = person.CScore;
Etc....

I think you may be going about this the hard way. There are lots of posts on the repository pattern, the way that works best with MVC is to get the item, then update it with the form, then save it. What you're doing is passing the item through to your repository, getting it again and then updating it with the object.
But that's not the problem you asked about;
cPerson.Account = (from a in Account
where a.AccountId.Equals(person.Account.AccountId)
select a).FirstOrDefault();
You need to set the Account object to an instance of the account you're trying to reference like this. You could, and probably should, extract this code into a seperate repository for the account, just make sure they share the same Entity context.

Related

Audit Log to Maintain History Against Records using Repository Pattern.

I want to implement audit log against each single record, so its looks like an history for records so user can view what operation perform against it,what is previous value? what is current value? like this, using a Repository pattern in MVC.
Someone please help me.
Thank you.
Disclaimer: I'm the owner of the project EF+ (EntityFramework Plus)
You can use EF+ Audit which allow to easily track changes, exclude/include entity or property and auto save audit entries in the database.
// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntityContext();
// ... ctx changes ...
var audit = new Audit();
audit.CreatedBy = "ZZZ Projects"; // Optional
ctx.SaveChanges(audit);
// Access to all auditing information
var entries = audit.Entries;
foreach(var entry in entries)
{
foreach(var property in entry.Properties)
{
}
}
Project: http://entityframework-plus.net/
Documentation: http://entityframework-plus.net/audit

Entity framework ID order in DB isn't the same everytime?

In my Seeder, I do the below, the order of which they land in the DB seems to be different now and then! I need the order to be consistent because I refer to the IDs using static variables stored. Why is the order different? Can I make it consistent? Thank you.
List<BadgeGroup> BGs = new List<BadgeGroup>();
BadgeGroup Unclassified = new BadgeGroup() { Description = "Unclassified" };
BGs.Add(Unclassified);
BadgeGroup NumVotesOnPost = new BadgeGroup() { Description = "Number of votes on a post" };
BGs.Add(NumVotesOnPost);
foreach (BadgeGroup BG in BGs)
db.BadgeGroups.AddOrUpdate(BG);
db.SaveChanges();
When you save a list of objects, EF doesn't guarantee that it'll be saved in that order. After you save, you can iterate through the objects to get their ids.

Additional criteria to symfony filter

i'm working on a symfony project to manage a database. First i explain how it works:
In the database, all elements are associated to an unique element 'scene'. When a user accesses the application, chooses what scene he wants to see (it saves that in a user parameter). So when listing elements, the application should only list elements associated with the scene selected by the user.
*Note: all elements have an scene attribute in the table definition.
So my problem comes here:
I developed a listing of an element entities using the help of a sfPropelPager class to paginate. Also added some filters to search in the list, and for that i used the filter system provided by symfony (<element>FormFilter.class.php and stuff).
Now i want the list to not show elements from other scenes than the selected by the user.
How can i do to add additional criteria to the criteria given by the filter class?
or How would you solve the problem?
here is my action code:
public function executeUnidadfilter(sfWebRequest $request){
$this->filter = new BaUnidadorganizativaTblFormFilter();
$c = $this->filter->getCriteria();
$this->filter->bind($request->getParameter($this->filter->getName()));
if($this->filter->isValid()){
$this->pager = new sfPropelPager('BaUnidadorganizativaTbl',$this->sfPropelPagerLines);
echo $this->getUser()->getEscenario();
$this->pager->setCriteria($c);
$this->pager->init();
}else{
$this->pager = new sfPropelPager('BaUnidadorganizativaTbl',$this->sfPropelPagerLines);
$this->pager->init();
}
$this->setTemplate('Unidadlist');
}
*Note: 'scene' mentioned below is Escenario in the code
thank you very much for your time
I solved the problem. The trouble was that i assigned the formfilter generated criteria to my criteria var Before the filter was filled. That's why of the error.
The resulting code is that:
public function executeUnidadfilter(sfWebRequest $request){
$this->filter = new BaUnidadorganizativaTblFormFilter();
$this->filter->bind($request->getParameter($this->filter->getName()));
if($this->filter->isValid()){
$this->pager = new sfPropelPager('BaUnidadorganizativaTbl',$this->sfPropelPagerLines);
$esc = $this->getUser()->getEscenario();
$c = new Criteria();
$c = $this->filter->getCriteria();
$c->addAnd('codigo_escenario',$esc);
$this->pager->setCriteria($c);
$this->pager->init();
}else{
$this->pager = new sfPropelPager('BaUnidadorganizativaTbl',$this->sfPropelPagerLines);
$this->pager->setCriteria($this->filter->getCriteria());
$this->pager->init();
}
$this->message=null;
$this->messageType=null;
$this->setTemplate('Unidadlist');
}

Filter BindingSource with entity framework

Hi
How can i filter results exists in BindingSource filled with entities ( using EF 4)?
I tried this:
mybindingsource.Filter = "cityID = 1"
But it seems that binding source with entity framework doesn't support filtering .. am i right ?,is there another way to filter(search) data in binding source .
PS:
- I'm working on windows application not ASP.NET.
- I'm using list box to show the results.
Thanx
Maybe a better one than Leonid:
private BindingSource _bs;
private List<Entity> _list;
_list = context.Entities;
_bs.DataSource = _list;
Now when filtering is required:
_bs.DataSource = _list.Where<Entity>(e => e.cityID == 1).ToList<Entity>;
This way you keep the original list (which is retrieved once from the context) and then use this original list to query against in memory (without going back and forth to the database). This way you can perform all kinds of queries against your original list.
I think, you have made mistake in syntax. You should write Filter like this:
mybindingsource.Filter = "cityID = '1'"
Another way is to use LINQ expressions.
(About LINQ)
Why do you have to call Entety again?
Simple solution:
public List<object> bindingSource;
public IEnumerable FiltredSource
{
get{ return bindingSource.Where(c => c.cityID==1);
}
.where (Function (c) c.cityID = 1)

StackOverflowException caused by a linq query

edit #2: Question solved halfways. Look below
As a follow-up question, does anyone know of a non-intrusive way to solve what i'm trying to do below (namely, linking objects to each other without triggering infinite loops)?
I try to create a asp.net-mvc web application, and get a StackOverFlowException. A controller triggers the following command:
public ActionResult ShowCountry(int id)
{
Country country = _gameService.GetCountry(id);
return View(country);
}
The GameService handles it like this (WithCountryId is an extension):
public Country GetCountry(int id)
{
return _gameRepository.GetCountries().WithCountryId(id).SingleOrDefault();
}
The GameRepository handles it like this:
public IQueryable<Country> GetCountries()
{
var countries = from c in _db.Countries
select new Country
{
Id = c.Id,
Name = c.Name,
ShortDescription = c.ShortDescription,
FlagImage = c.FlagImage,
Game = GetGames().Where(g => g.Id == c.GameId).SingleOrDefault(),
SubRegion = GetSubRegions().Where(sr => sr.Id == c.SubRegionId).SingleOrDefault(),
};
return countries;
}
The GetGames() method causes the StackOverflowException:
public IQueryable<Game> GetGames()
{
var games = from g in _db.Games
select new Game
{
Id = g.Id,
Name = g.Name
};
return games;
}
My Business objects are different from the linq2sql classes, that's why I fill them with a select new.
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
edit #1: I have found the culprit, it's the following method, it triggers the GetCountries() method which in return triggers the GetSubRegions() again, ad nauseam:
public IQueryable<SubRegion> GetSubRegions()
{
return from sr in _db.SubRegions
select new SubRegion
{
Id = sr.Id,
Name = sr.Name,
ShortDescription = sr.ShortDescription,
Game = GetGames().Where(g => g.Id == sr.GameId).SingleOrDefault(),
Region = GetRegions().Where(r => r.Id == sr.RegionId).SingleOrDefault(),
Countries = new LazyList<Country>(GetCountries().Where(c => c.SubRegion.Id == sr.Id))
};
}
Might have to think of something else here :) That's what happens when you think in an OO mindset because of too much coffee
Hai! I think your models are recursively calling a method unintentionally, which results in the stack overflow. Like, for instance, your Subregion object is trying to get Country objects, which in turn have to get Subregions.
Anyhow, it always helps to check the stack in a StackOverflow exception. If you see a property being accessed over and over, its most likely because you're doing something like this:
public object MyProperty { set { MyProperty = value; }}
Its easier to spot situations like yours, where method A calls method B which calls method A, because you can see the same methods showing up two or more times in the call stack.
The problem might be this: countries have subregions and subregions have countries. I don't know how you implement the lazy list, but that might keep calling GetCountries and then GetSubRegions and so on. To find that out, I would launch the debugger en set breakpoints on the GetCountries and GetSubRegions method headers.
I tried similar patterns with LinqToSql, but it's hard to make bidirectional navigation work without affecting the performance to much. That's one of the reasons I'm using NHibernate right now.
To answer your edited question, namely: "linking objects to each other without triggering infinite loops":
Assuming you've got some sort of relation where both sides need to know about the other... get hold of all the relevant entities in both sides, then link them together, rather than trying to make the fetch of one side automatically fetch the other. Or just make one side fetch the other, and then fix up the remaining one. So in your case, the options would be:
Option 1:
Fetch all countries (leaving Subregions blank)
Fetch all Subregions (leaving Countries blank)
For each Subregion, look through the list of Countries and add the Subregion to the Country and the Country to the Subregion
Option 2:
Fetch all countries (leaving Subregions blank)
Fetch all Subregions, setting Subregion.Countries via the countries list fetched above
For each subregion, go through all its countries and add it to that country
(Or reverse country and subregion)
They're basically equialent answers, it just changes when you do some of the linking.

Resources