Copy EntityCollection to Dictionary in CRM - crm

I want to convert or copy my EntityCollection to Dictionary.
my code is below.
Please suggest on the same.
Dictionary<string, string> dictionary = new Dictionary<string, string>();
EntityCollection contentCollection = orgService.RetrieveMultiple(new FetchExpression(contentQuery));
if (contentCollection != null && contentCollection.Entities != null && contentCollection.Entities.Count > 0)
{
foreach (Entity contents in contentCollection.Entities)
{
dictionary.Add(content.Attributes(0).key,content.Attributes(0).value );
}
}

It can be as short as this:
EntityCollection contentCollection = orgService.RetrieveMultiple(new FetchExpression(contentQuery));
Dictionary<Guid, Entity> dictionary = contentCollection.Entities.ToDictionary(entity => entity.Id);
You do not need to check contentCollection nor its Entities collection, because method RetrieveMultiple always returns non-null values or fails throwing an exception.
Alternatively you could write it as one statement:
var dictionary = orgService
.RetrieveMultiple(new FetchExpression(contentQuery))
.Entities
.ToDictionary(entity => entity.Id);

I have an open source project could make this slightly more concise:
var dictionary = orgService
.GetEntities<Entity>(new FetchExpression(contentQuery)
.ToDictionary(e => e.Id);
You can download it from GitHub: https://github.com/daryllabar/XrmUnitTest (Reference the DLaB.Xrm library)
or NuGet: Install-Package DLaB.Xrm -Version 1.0.1
The Get Entities Handles the Retrieve and returns a List (for late bound) or List(or whatever type you are retrieving for early bound)

Related

How I can read EF DbContext metadata programmatically?

I have application which uses EF-CodeFirst 5 (dll ver 4.4.0.0, on .net 4.0).
I need to be able to read entity metadata, so that I can, for a given entry type get following information:
which properties are one-many relations (referenced entities)
which properties are many-one relations (collections of entities referencing current one)
also nice but not absolutely necessary: which properties are many-many relations (collections of relations)
I can get this info by writing foreach loops on lists of properties and then "recognizing" them by relying on all of the references being virtual, but I feel that is not "proper" way. I know that EdmxWriter can provide that information in xml format, but it does so by accessing InternalContext which is not publicly accessible and I want to get strongly typed lists/arrays directly, without using that xml. Which API should I use (if there is one for this, it seems that I cannot find it)?
Gorane, this should get you started...
(I haven't played much with it - it takes a bit of experimenting in the debugger to see which properties / info and how to get it)
using (var db = new MyDbContext())
{
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
var container = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace);
foreach (var set in container.BaseEntitySets)
{
// set.ElementType.
foreach (var metaproperty in set.MetadataProperties)
{
// metaproperty.
}
}
// ...or...
var keyName = objectContext
.MetadataWorkspace
.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace)
.BaseEntitySets
.First(meta => meta.ElementType.Name == "Question")
.ElementType
.KeyMembers
.Select(k => k.Name)
.FirstOrDefault();
}
and more specifically...
foreach (var set in container.BaseEntitySets)
{
var dependents = ((EntitySet)(set)).ForeignKeyDependents;
var principals = ((EntitySet)(set)).ForeignKeyPrincipals;
var navigationProperties = ((EntityType)(set.ElementType)).NavigationProperties;
foreach (var nav in navigationProperties)
{
// nav.RelationshipType;
}
}
Some of these properties seem to not be exposed to 'general public' so you'd need to use reflection - or find some smarter way - but a good deal of info is in there.
And some more info in these links...
How to get first EntityKey Name for an Entity in EF4
How can I extract the database table and column name for a property on an EF4 entity?
EDIT:
Using your list of navigationProperties as starting point, I got everything I needed like this:
ManyToManyReferences = navigationProperties.Where(np =>
np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many &&
np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
.Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
.ToList();
OneToManyReferences = navigationProperties.Where(np =>
(np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One ||
np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne) &&
np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
.Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
.ToList();
ManyToOneReferences = navigationProperties.Where(np =>
np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many &&
(np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One ||
np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne))
.Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
.ToList();
OneToOneReferences = navigationProperties.Where(np =>
np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One &&
np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
.Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
.ToList();
CreateLambdaExpression method is not my courtesy, credits go to Jon Skeet, code was created with help of this answer
Here is my CreateLambdaExpression method:
public static Expression<Func<TEntity, object>> CreateLambdaExpression<TEntity>(string propertyName)
{
ParameterExpression parameter = Expression.Parameter(typeof (TEntity), typeof (TEntity).Name);
Expression property = Expression.Property(parameter, propertyName);
return Expression.Lambda<Func<TEntity, object>>(property, new[] {parameter});
}

CacheDependency from 2 or more other cache items. (ASP.NET MVC3)

I'm a little puzzled over the possible cachedependencies in asp.net, and I'm not sure how to use them.
I would like to add items to the HttpRuntime.Cache in a way, that the elements should invalidate if I change other elements in the cache. The dependencies should be defined by the key.
I want a function like this:
public MyObject LoadFromCache(string itemDescriptor, IEnumerable<string> dependencies)
{
var ret = HttpRuntime.Cache[itemDescriptor] as MyObject;
if (ret == null)
{
ret = LoadFromDataBase(itemDescriptor);
//this is the part I'm not able to figure out. Adding more than one dependency items.
var dep = new CacheDependency();
dependencies.ForEach(o => dep.SomeHowAdd(o));
HttpRuntime.Cache.Add(
itemDescriptor,
ret,
dependencies,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
Caching.CacheItemPriority.Normal,
null
);
}
return ret;
}
Help me out on this one.
I didn't know you could do this, but if you look at the CacheDependency constructor here, you can see that the second parameter is an array of cache keys, so that if any of those cached items change, the whole dependency will be changed and your dependent item will be invalidated as well.
So your code would be something like:
String[] cacheKeys = new string[]{"cacheKey1","cacheKey2"};
var dep = New CacheDependency("", cacheKeys);
HttpRuntime.Cache.Add(itemDescriptor, ret, dep ...);

How to save related new objects that comes from differents DBContext CTP5

I'm working on a MVC3 application and I created my POCO classes from my database with the DbContext Code Generator. All goes fine until I got stuck in this situation. Note that I use the repository pattern and I use for every entity a dedicated repository whether get a new instance of the DbContext.
Now, I'm in this situation:
object A has a relation one-to-many with B (A can have one or many B)
object B has a relation many-to-one with C (C can have one or many B)
object B has a relation many-to-one with D (D can have one or many B)
I should add a new object B, consider that object C and D are yet existing, so I must only do the relation and the object A can be created or updated. In the specific consider that A is customer and B is subscriptions (C and D are virtual objects properties in B).
Now If I try to save I got duplicates in C and D tables, while the management of the object seems to work.
So, I thinked that I should detach the entities before do the relation, but when I call the SaveChanges() I got this error:
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.
Here's the code:
Customer customer = customerRepository.Get(ID);
if (customer == null)
{
customer = new Customer();
customer.Email = Request.Form["Email"].ToString();
}
Subscription subscription = new Subscription();
subscription.Active = true;
subscription.DateSubscription = DateTime.Today;
Object C = objectCRepository.Get(Request.Form["IDObjectC"]);//Get C object from database
Object D = objectDRepository.Get(Request.Form["IDObjectD"]);//Get D object from database
if (C != null)
{
//I tried also to detach the objects before adding to subscription
subscription.C = C;
subscription.D = D;
customer.Subscriptions.Add(subscription);
if (customer.IDCustomer == 0)
customerRepository.Add(customer);
else
UpdateModel(customer);
customerRepository.Save();
}
And here the add and the save method of the customer repository:
public override void Add(Cliente cliente)
{
db.Cliente.Add(cliente);
}
public override void Save()
{
foreach (var entry in db.ChangeTracker.Entries()
.Where(e => e.State == EntityState.Modified || e.State == EntityState.Added || e.State == EntityState.Unchanged || e.State == EntityState.Detached))
{
string state = ObjectContext.GetObjectType(entry.Entity.GetType()).Name + " " + entry.State.ToString();
if (ObjectContext.GetObjectType(entry.Entity.GetType()).Name.Equals("C") || ObjectContext.GetObjectType(entry.Entity.GetType()).Name.Equals("D"))
{
entry.State = EntityState.Unchanged;
}
dbContext.SaveChanges();
}
I tried also to use this for objects C and D.
((System.Data.Entity.Infrastructure.IObjectContextAdapter)dbContext).ObjectContext.Refresh(System.Data.Objects.RefreshMode.StoreWins, entry);
And the error received is
The element at index 0 in the collection of objects to refresh has a null EntityKey property value or is not attached to this ObjectStateManager.
I noticed that in CTP5 was added the option AsNoTracking(), I tried to use it, but nothing!
I checked also the Concurrency mode for every properties involved in the operation and all are set to None.
I finished ideas :(!
Any help would appreciated! Thanks!
Actually I solved myself using on loading the AsNoTracking() method and before saving the entities I change the state to Unchanged.
//On loading
Context.Object.AsNoTracking().SingleOrDefault(l => l.Property == Property);
//On saving
Object.State = EntityState.Unchanged;
Object.SaveChanges();
Hope this helps someone.

Error while updating Database record with Entity Framework on ASP.NET MVC Page

I have an ASP.NET Page that updates registered User Address Details for a selected record.
Below is the update method that I am calling from my controller.
When I am calling the ApplyPropertyChanges method, I am getting an error. Did anyone run into the same error while updating the record with Entity Framework?
Appreciate your responses.
Error message:
The existing object in the ObjectContext is in the Added state. Changes can only be applied when the existing object is in an unchanged or modified state.
My Update method:
[HttpPost]
public bool UpdateAddressDetail([Bind(Prefix = "RegUser")] AddressDetail regUserAddress, FormCollection formData)
{
regUserAddress.AD_Id = 3;
regUserAddress.LastUpdated = HttpContext.User.Identity.Name;
regUserAddress.UpdatedOn = DateTime.Now;
regUserAddress.AddressType = ((AddressDetail)Session["CurrentAddress"]).AddressType ?? "Primary";
regUserAddress.Phone = ((AddressDetail)Session["CurrentAddress"]).Phone;
regUserAddress.Country = ((AddressDetail)Session["CurrentAddress"]).AddressType ?? "USA";
miEntity.ApplyPropertyChanges(regUserAddress.EntityKey.EntitySetName, regUserAddress);
miEntity.SaveChanges();
return true;
}
The error is the object is detached from the context, and ApplyPropertyChanges thinks the object is added because it isn't attached. So you would need to query from the data context or get an attached form and then apply the changes then.
HTH.
What Dave Said
+
You need to Attach() the disconnected entity to your object context:
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.attach.aspx
miEntity.Attach(regUserAddress);
miEntity.SaveChanges();
Just add the following code before miEntity.SaveChanges():
miEntity.Entry(regUserAddress).State = EntityState.Modified;
First select the record (object entity), search by key through the ObjectContext. For example if the search ArticleSet EntitySet called for there to record, and once you get it modified its properties with new values and then call SaveChanges() of ObjectContext.
Example:
ObjectQuery<Article> myArt=Context.ArticleSet.Where myArt = (row => row.ArticleId == value);
myArt.Description=" new value ";
etc. ..
etc ...
Context.SaveChanges ();

ADO.NET Data Services: Non-Asynch Calls?

I have a question that I'm struggling with in ADO.NET Data Services:
When assembling an Entity for storage I need to get a related value from a lookup file. For example a person has a status code assigned of 'Pending' which is in a table called StatusCodes.
In Entity Framework, I'd need to set the value of person.StatusCode equal to an instance of the StatusCode. In the Entity Framework or in LINQ2Sql I'd so something like this:
var person = Person.CreatePerson(stuff);
var statCode = myContext.StatusCodeSet.Where(sc => sc.Description == "Pending").FirstOrDefault();
person.StatusCode = statCode;
// ...more code here...
myContext.BeginSaveChanges(SaveChangesOptions.Batch,
new AsyncCallback(OnSaveAllComplete),
null);
The query for the statCode won't work in ADO.NET Data Services and I get a runtime error saying the function is not supported. I assume it's because the statCode lookup is not an Async call.
However,
var person = Person.CreatePerson(stuff);
var query = from stat in myContext.StatusCodeSet
where stat.Description == "Pending"
select stat;
var dsQuery = (DataServiceQuery<StatusCode>)query;
dsQuery.BeginExecute(
result => tutorApplication.StatusCode = dsQuery.EndExecute(result).FirstOrDefault(), null);
// ...more code here...
myContext.BeginSaveChanges(SaveChangesOptions.Batch,
new AsyncCallback(OnSaveAllComplete),
null);
doesn't work either due to the Async nature of the query, the result won't be back before the person save happens.
Am I approaching this correctly?
Thanks
After sleeping on this I came up with the following:
var person = Person.CreatePerson(stuff);
var appStatPending = new StatusCode()
{
StatusCodeId = (int)StatusCodes.Pending,
Code = "Pending",
Description = "Pending",
EffectiveDate = DateTime.Now,
EnteredBy = "",
EnteredDate = DateTime.Now
};
myContext.AttachTo("StatusCodeSet", appStatPending);
person.StatusCode = appStatPending;
myContext.SetLink(tutorApplication, "StatusCode", appStatPending);
// ...more code here...
myContext.BeginSaveChanges(SaveChangesOptions.Batch,
new AsyncCallback(OnSaveAllComplete),
null);
I can create a local copy of the status code and link it into the context. It's important to new up the appStatPending rather than doing a StatusCode.CreateStatusCode() since doing that will add a new StatusCode to the database when the person graph persisted. For the same reason it's important to do the AttachTo("StatusCodeSet", appStatPending) since doing myContext.AddToStatusCodeSet() will also add a new entry to the StatusCodes table in the database.

Resources