Retrieval of Single Entity + Ria Services - c#-3.0

I am reading and doing some RnD on RIA as a solution for a new Silverlight project.
I have read alot of the documentation and decided to do a small mockup of a system using .Net RIA Services.
I want to know how to get a Single Entity from the Domain Service?
example:
I want to get a person and populate a form:
public Person GetSinglePerson()
{
return new Person { ID = 4, FirstName = "Cyanide", LastName = "Happiness", Status=3 };
}
Say I use the the DomainDataSource:
<riaControls:DomainDataSource x:Name="source2" QueryName="GetSinglePersonQuery" AutoLoad="True">
<riaControls:DomainDataSource.DomainContext>
<web:DataContext/>
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
This only returns a EntityCollectionView? How do I bind for example in a form to properties that are in the Person Class?
Like:
<TextBox Text="{Binding FirstName, ElementName=source2}"/>
Everything seems to come back as IEnumerable or as CollectionViews (like the DATA binding in the samples) which aren't useful for a single entity.
I want a single persons entry, why do I want a CollectionView in which I cannot access properties directly.
I have also use the:
LoadOperation<Person> oLoadOperation = oDataContext.Load(oDataContext.GetSinglePersonQuery());
I am very close to giving up on this RIA idea and just going with a normal WCF service as it is more predictable and manageable at this stage.

ctxt.Load(ctxt.GetEmployeeByNumberQuery("ABC123")).Completed += new System.EventHandler(EmployeeLoad_Completed);
void EmployeeLoad_Completed(object sender, System.EventArgs e)
{
Employee myEmployee = (sender as LoadOperation<Employee>).Entities.FirstOrDefault();
}

hey just found this check it out I think this is what you want to do
http://jeffhandley.com/archive/2009/11/10/domaindatasource-single-record.aspx

HumanResourceContext context = new HumanResourceContext();
var addressquery = context.GetAddressesQuery();
addressquery = addressquery.Where(a => a.AddressId == 1);
context.Load(addressquery, (op) =>
{
Address address = op.Entities.FirstOrDefault();
MessageBox.Show(address.Street1);
}, null);

I presume you have your class decorated with [EnableClientAccess] ?
try
<TextBlock Text="{Binding Path=Person.FirstName}"

Related

Entity Framework Z Plus Batch Update

I am using Entity Framework Z Plus Batch Update method. I am unable to proceed due to below issue. Actually Update method works good when i give static values like tagName="amir1". But I need to get the Tagdescription from a web service or from another collection based on the tagId, Update method is not accepting a extension method or any other method to accomplish my requirement.Its saying
"LINQ to Entities does not recognize the method 'System.String GetTagDescription(Int32)' method, and this method cannot be translated into a store expression.".
Hope my requirement is clear now. Please guide me if there is any other approach for my requirement.
Here is my code:
using (var context = new TrialsDBEntities())
{
context.tblTags.Where(x => (tagIdCollection.Contains(x.tagId))).
Update(m => new tblTag { tagDescription = m.tagId.GetTagDescription(), tagName = "amir1" });
}
public static string GetTagDescription(this int i)
{
return "test" + i;
///Replace above line with call to database or web service call
getting some text by giving i as input
}
Disclaimer: I'm the owner of the project Entity Framework Plus
Unfortunately, that's not possible to use BatchUpdate with a value that changes from a row to another.
Disclaimer: I'm the owner of the project Entity Framework Extensions
In this situation, we normally recommend using our paid library that's build for this kind of situation and offer high-performance saving operation.
Example
// Easiest way
context.BulkSaveChanges();
// Fastest way
context.BulkUpdate(tags);
EDIT: Answer comment
If I can't use updated row so why the signature of action is misleading and give me possibility to write like this: e => new Entity { Name = e.Name + "Edited" }
For most providers such as SQL Server, your expression is supported. You give a global expression so we can apply. Your expression doesn't doesn't change from a row to another, it's the same expression.
What is not supported is giving a specific expression from a row to another.
Write your code as follows:
using (var context = new TrialsDBEntities())
{
var tagsToBeUpdated = context.tblTags.Where(x => (tagIdCollection.Contains(x.tagId))).AsNoTracking().ToList();
//Only use this code block if your tagsToBeUpdated list is too large
Parallel.ForEach(tagsToBeUpdated, tagToBeUpdated =>
{
var tagDescription = GetTagDescription(tagToBeUpdated.tagId);
tagToBeUpdated.tagDescription = tagDescription;
context.Entry(tagToBeUpdated).State = EntityState.Modified;
});
//Only use this code block if your tagsToBeUpdated list is not too large
foreach(var tagToBeUpdated in tagsToBeUpdated)
{
var tagDescription = GetTagDescription(tagToBeUpdated.tagId);
tagToBeUpdated.tagDescription = tagDescription;
context.Entry(tagToBeUpdated).State = EntityState.Modified;
}
context.SaveChanges();
}
public static string GetTagDescription(int i)
{
return "test" + i;
///Replace above line with call to database or web service call
//getting some text by giving i as input
}

Loading particular peoperties for entity and assigning it as reference to another entity

Example:
I am entering a new invoice. For this invoice I need to enter a customer. Lets assume that we retrieved a list of customers:
var list = Context.Set<Customer>().ToList();
Here I see two issues:
1) I do not need to bring all information for customer, I only need Id, Code and Name
2) Customer in current DbContext is read-only, so it would be nice if it is possible to tell DbContext not to monitor their states, to improve performance.
Questions:
1) Can we load only partial data for customer, but still be able to assign it to Invoice (see code bellow)?
2) Can we tell DbContext not to monitor Customers for changes, and still be able to do this:
Invoice.Customer = CustomerList[10];
There's not a direct way to do exactly what you want, but you might be able to achieve your goals with some compromise.
I do not need to bring all information for customer, I only need Id,
Code and Name
There isn't a way for EF to create a partially loaded entity, but you could create an anonymous type:
Context.Customers.Select(c => new {Id = c.CustomerId, Code = c.Code, Name = c.Name}).Tolist()
If you could live with the new anonymous type then use that, or you could then iterate through that list, creating actual customer objects.
Customer in current DbContext is read-only, so it would be nice if it
is possible to tell DbContext not to monitor their states, to improve
performance.
EF provides an Extension of AsNoTracking() which will do exactly what you're looking for:
var list = Context.Set<Customer>().AsNoTracking().ToList();
Depending on what you choose from above, the following code may change, but this code does achieve what you're looking for. Partially loads the customer, but still allows you to attach the customer to the invoice.
Note: You'll need to attach the customer to your context before you can use it, and then setting it to a state of Unchanged will prevent it from overwriting exiting data.
m = new Model();
var list = m.Customers.Select(c => new {Id = c.CustomerId, Code = c.Code, Name = c.Name});
List<Customer> customerList = new List<Customer>();
foreach (var item in list)
{
customerList.Add(new Customer()
{
CustomerId = item.Id,
Code = item.Code,
Name = item.Name
});
}
Invoice i = new Invoice();
var customer = customerList.First();
m.Customers.Attach(customer);
m.Entry(customer).State = EntityState.Unchanged;
i.Customer = customer;
m.Invoices.Add(i);
m.SaveChanges();

EF and WPF Datagrid binding issue

Is it possible to change the name of the server the EF connect to on the fly?
I have the same database on 5 servers (different environments, Dev, Test, UAT...) and would like to write a dashboard application to extract the same information from each server in turn by just selecting the environment from a DDL.
I am using Entity Framework 4/WPF/C#. The new ObservableCollection(context.EntitySet) is bound to the WPF DataGrid in XAML. This works fine.
the xaml grid binding is as follow
<grid:RadGridView ItemsSource="{Binding EPolicies}" IsReadOnly="True" RowDetailsVisibilityMode="VisibleWhenSelected" RowIndicatorVisibility="Collapsed" AutoGenerateColumns="False">
in the view model I call the following code
entities = new EpolicyEntities(environmentConnStr);
customTexts = new ObservableCollection<C_CustomTextType>(from i in entities.C_CustomTextType select i);
languages = new ObservableCollection<C_Language>(from i in entities.C_Language select i);
userTypes = new ObservableCollection<C_UserType>(from i in entities.C_UserType select i);
EPolicies = new ObservableCollection<EPolicy>(from e in entities.EPolicies select e);
entities is the ObjectContext representing the database that I connect to
The first time it works fine but the second time even thoguh I can see the new values in the EPolicies object, the grid is not being refreshed as I would want
EPolicies is a property defined as below
public ObservableCollection EPolicies
{
get { return ePolicies; }
set
{
ePolicies = value;
OnPropertyChanged(() => EPolicies);
}
}
Can somebody help me please?
thanks
Anyone done this?
Okay I found out.
The issue was that the property
public ObservableCollection EPolicies {
get { return ePolicies; }
set {
ePolicies = value; OnPropertyChanged(() => EPolicies);
}
}
is calling the OnpropertyChanged but not the Base.OnPropertyChanged.
Not sure why it works that way might be that the event is not being bubbled up.
Does anybody has an answer?

I get the error : “An entity object cannot be referenced by multiple instances of IEntityChangeTracker.” with .net MVC2 and the Entity Framework4

I have a big problem since some days and I’m a very beginner in the Entity Framework.
I have 2 entities : Group and News. A news is visible by one or many groups. I use two repositories (newsRepository and groupsRepository).
This is my Create method for the news :
public ActionResult Create()
{
return View(new CreateNewsViewModel(new News()));
}
[HttpPost]
public ActionResult Create(CreateNewsViewModel model)
{
model.news.CategoryId = Int32.Parse(Request.Form["news.CategoryId"]);
if (ModelState.IsValid)
{
News news = new News();
DateTime date = DateTime.Now;
//AuthorId a recuperer
news.AuthorId = 1;
news.Title = IntranetTools.UppercaseFirst(model.news.Title.Trim());
news.Content = model.news.Content;
news.IsVisible = Request.Form["news.IsVisible"].Contains("true");
news.CreateDate = date;
news.PublicationDate = date;
news.LastChangedDate = date;
news.CategoryId = model.news.CategoryId;
// Collection des groupes concernés
foreach (var c in model.allGroups)
{
if (Request.Form["" + c.GroupId].Contains("true"))
{
News.Groups.Add(c);
}
}
_newsRepository.AddToNewsSet(news);
_newsRepository.SaveChanges();
return Redirect("/NewsAdmin/Index/");
}
return View(model);
}
I say that all my groups are already created. I just want to insert the groups (chosen by the user via checkboxes). In my “CreateNewsViewModel”, I create a list of groups that contains all existing groups in my DB. I get the list in my view, via a “foreach” loop and create a checkbox for each group.
I reuse the same list in my controller to compare if checkboxes have been checked.
For each “true” value, I add groups in the groups collection of my news (just created).
With this, I get this Error Message :
“An entity object cannot be referenced by multiple instances of IEntityChangeTracker.” (at line _newsRepository.AddToNewsSet(news);)
I try some solutions but I still don’t understand how I can solve this problem.
Thanks for all
Edit
Actually, if I use explicitly two contexts and detach/attach my objects to other context it's fine and I have no erros.
ObjectContext context = _newsRepository.Context;
ObjectContext context2 = _groupsRepository.Context;
foreach (var c in groups)
{
if (Request.Form["" + c.GroupId].Contains("true"))
{
context2.Detach(c);
context.Attach(c);
news.Groups.Add(c);
}
}
I would like to use the Ladislav Mrnka's solution and use the dependency injection (I use Ninject framework) to give the same ObjectContext to my repositories (in single request processing).
I understand the concept but I don't know how to code it.
The error message says that News object or any of related Group objects is attached to different ObjectContext instance. How is your repository implemented and how did you get model.allGroups? If you loaded allGroups from GroupsRepository which has its own ObjectContext instance then it is probably source of the problem. The solution would be:
(Preferred) Share single ObjectContext for all repositories in single request processing
Detach objects when you load them from database (ObjectContext has Detach method)
Close ObjectContext when you load objects from database

Entity Framework and Entity Tracker Problems

If I run the following code it throws the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
public void Save(Category category)
{
using(var db = new NorthwindContext())
{
if(category.CategoryID == 0)
{
db.AddToCategorySet(category);
}
else
{
//category.RemoveTracker();
db.Attach(category);
}
db.SaveChanges();
}
}
The reason is of course that the category is sent from interface which we got from GetById method which already attached the EntityChangeTracker to the category object. I also tried to set the entity tracker to null but it did not update the category object.
protected void Btn_Update_Category_Click(object sender, EventArgs e)
{
_categoryRepository = new CategoryRepository();
int categoryId = Int32.Parse(txtCategoryId.Text);
var category = _categoryRepository.GetById(categoryId);
category.CategoryName = txtUpdateCategoryName.Text;
_categoryRepository.Save(category);
}
I'm still learning Entity Framework myself, but maybe I can help a little. When working with the Entity Framework, you need to be aware of how you're handling different contexts. It looks like you're trying to localize your context as much as possible by saying:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
...
}
}
... within your data access method. Did you do the same thing in your GetById method? If so, did you remember to detach the object you got back so that it could be attached later in a different context?
public Category GetById(int categoryId)
{
using (var db = new NorthwindContext())
{
Category category = (from c in db.Category where Category.ID == categoryId select c).First();
db.Detach(category);
}
}
That way when you call Attach it isn't trying to step on an already-attached context. Does that help?
As you pointed out in your comment, this poses a problem when you're trying to modify an item and then tell your database layer to save it, because once an item is detached from its context, it no longer keeps track of the changes that were made to it. There are a few ways I can think of to get around this problem, none of them perfect.
If your architecture supports it, you could expand the scope of your context enough that your Save method could use the same context that your GetById method uses. This helps to avoid the whole attach/detach problem entirely, but it might push your data layer a little closer to your business logic than you would like.
You can load a new instance of the item out of the new context based on its ID, set all of its properties based on the category that is passed in, and then save it. This costs two database round-trips for what should really only need one, and it isn't very maintainable.
You can dig into the context itself to mark the Category's properties as changed.
For example:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
db.Attach(category);
var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
stateEntry.SetModifiedProperty(propertyName);
}
db.SaveChanges();
}
}
This looks a little uglier, but should be more performant and maintainable overall. Plus, if you want, you could make it generic enough to throw into an extension method somewhere so you don't have to see or repeat the ugly code, but you still get the functionality out of it.