Possibly really simple question, but I'm new to IronPython. I would like use IronPython to crawl an entity I pass to it, but when I try to use any extension methods, it, as sort of expected, blows up. How do I traverse my POCOs in IronPython?
delegate bool EvaluateRule(MyEntity entity);
//Keep in mind this is just to test, no actual value provided
string expression = #"entity.Flags.FirstOrDefault() == null";
MyEntity entity = new MyEntity();
PythonEngine engine = new PythonEngine();
EvaluateRule rule = engine.CreateLambda<EvaluateRule>(expression);
bool result = rule.Invoke(entity);
I get the following: 'EntityCollection[MyEntity]' object has no attribute 'FirstOrDefault'
Thanks in advance!
You can call the extension methods as regular static methods:
string expression = #"Enumerable.FirstOrDefault(entity.Flags) == null";
Related
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
}
I've an error: "ExceptionMessage": "Self referencing loop detected for property 'Posto' with type 'UfficioServer.Posto'. Path '[0].Dipendente[0]'.",
when i call my web api, i need a list of Posti whit the association with Dipendente...
public List<Posto> GetAllPosti()
{
try
{
List<Posto> p = new List<Posto>();
UfficioPostiEntities1 db = new UfficioPostiEntities1();
db.Configuration.ProxyCreationEnabled = false;
var posto = db.Posto.Include("Dipendente").ToList();
var x = db.Posto.ToList();
return x;
}
can someone help me?
To avoid this error you should add the following to the Application_Start in Global.asax:
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
This is because (as per documentation)
ReferenceLoopHandling.Ignore: Json.NET will ignore objects in
reference loops and not serialize them. The first time an object is
encountered it will be serialized as usual but if the object is
encountered as a child object of itself the serializer will skip
serializing it.
I am using EF5, although I am sure it is a more general EF Question.
I cannot get the following to work. I keep getting casting error:
Unable to cast object of type 'System.Data.Objects.ObjectQuery`1[StdOrgUser]' to type 'System.Data.Objects.ObjectSet`1[StdOrgUser]'.
For the code:
public ObjectSet<StdOrgUser> StdOrgUser
{
get
{
if ((_StdOrgUser == null))
{
_StdOrgUser = base.CreateObjectSet<StdOrgUser>("StdOrgUser");
_StdOrgUser = (ObjectSet<StdOrgUser>) _StdOrgUser.Where(r => r.IsActive == false);
}
return _StdOrgUser;
}
}
It compiles fine. Intellisense enables me to choose LINQ operators etc. It is when I run it, that I get the above runtime error.
Where am I going wrong?
Many thanks for any help.
The ObjectSet class implements (amongst other things) IQueryable and IEnumerable, both of these interfaces have an extension method Where, see here and here. Neither IQueryable nor IEnumerable (which are the respective return types of the extension methods) can be cast back to ObjectSet.
The following line of code cannot be evaluated until run time:
_StdOrgUser = (ObjectSet<StdOrgUser>) _StdOrgUser.Where(r => r.IsActive == false);
but if you remove the cast the code will not compile:
_StdOrgUser = _StdOrgUser.Where(r => r.IsActive == false);
UPDATE
For querying you could change the return type of StdOrgUsers from ObjectSet to IQueryable but you lose all the other methods such as Add, Attach etc. You can't apply a standard filter using this technique. You could have an extension method called ActiveUsers()
public static IQueryable<StdOrgUser> ActiveUsers(this ObjectSet<StdOrgUser> users)
{
return users.Where(r => r.IsActive == false);
}
what you need to do is remember to use it in each query (not very pretty but it does clearly show intent)
var results = myContext
.StdOrgUser
.ActiveUsers()
.Where(//some filter);
My database has a 'LastModifiedUser' column on every table in which I intend to collect the logged in user from an application who makes a change. I am not talking about the database user so essentially this is just a string on each entity. I would like to find a way to default this for each entity so that other developers don't have to remember to assign it any time they instantiate the entity.
So something like this would occur:
using (EntityContext ctx = new EntityContext())
{
MyEntity foo = new MyEntity();
// Trying to avoid having the following line every time
// a new entity is created/added.
foo.LastModifiedUser = Lookupuser();
ctx.Foos.Addobject(foo);
ctx.SaveChanges();
}
There is a perfect way to accomplish this in EF 4.0 by leveraging ObjectStateManager
First, you need to create a partial class for your ObjectContext and subscribe to
ObjectContext.SavingChanges Event. The best place to subscribe to this event is inside the OnContextCreated Method. This method is called by the context object’s constructor and the constructor overloads which is a partial method with no implementation:
partial void OnContextCreated() {
this.SavingChanges += Context_SavingChanges;
}
Now the actual code that will do the job:
void Context_SavingChanges(object sender, EventArgs e) {
IEnumerable<ObjectStateEntry> objectStateEntries =
from ose
in this.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Modified)
where ose.Entity != null
select ose;
foreach (ObjectStateEntry entry in objectStateEntries) {
ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
.DataRecordInfo.FieldMetadata;
FieldMetadata modifiedField = fieldsMetaData
.Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault();
if (modifiedField.FieldType != null) {
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString()) {
entry.CurrentValues.SetString(modifiedField.Ordinal, Lookupuser());
}
}
}
}
Code Explanation:
This code locates any Added or Modified entries that have a LastModifiedUser property and then updates that property with the value coming from your custom Lookupuser() method.
In the foreach block, the query basically drills into the CurrentValues of each entry. Then, using the Where method, it looks at the names of each FieldMetaData item for that entry, picking up only those whose Name is LastModifiedUser. Next, the if statement verifies that the LastModifiedUser property is a String field; then it updates the field's value.
Another way to hook up this method (instead of subscribing to SavingChanges event) is by overriding the ObjectContext.SaveChanges Method.
By the way, the above code belongs to Julie Lerman from her Programming Entity Framework book.
EDIT for Self Tracking POCO Implementation:
If you have self tracking POCOs then what I would do is that I first change the T4 template to call the OnContextCreated() method. If you look at your ObjectContext.tt file, there is an Initialize() method that is called by all constructors, therefore a good candidate to call our OnContextCreated() method, so all we need to do is to change ObjectContext.tt file like this:
private void Initialize()
{
// Creating proxies requires the use of the ProxyDataContractResolver and
// may allow lazy loading which can expand the loaded graph during serialization.
ContextOptions.ProxyCreationEnabled = false;
ObjectMaterialized += new ObjectMaterializedEventHandler(HandleObjectMaterialized);
// We call our custom method here:
OnContextCreated();
}
And this will cause our OnContextCreated() to be called upon creation of the Context.
Now if you put your POCOs behind the service boundary, then it means that the ModifiedUserName must come with the rest of data from your WCF service consumer. You can either expose this
LastModifiedUser property to them to update or if it stores in another property and you wish to update LastModifiedUser from that property, then you can modify the 2nd code as follows:
foreach (ObjectStateEntry entry in objectStateEntries) {
ReadOnlyCollection fieldsMetaData = entry.CurrentValues
.DataRecordInfo.FieldMetadata;
FieldMetadata sourceField = fieldsMetaData
.Where(f => f.FieldType.Name == "YourPropertyName").FirstOrDefault();
FieldMetadata modifiedField = fieldsMetaData
.Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault();
if (modifiedField.FieldType != null) {
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString()) {
entry.CurrentValues.SetString(modifiedField.Ordinal,
entry.CurrentValues[sourceField.Ordinal].ToString());
}
}
}
Hope this helps.
There is a nuget package for this now : https://www.nuget.org/packages/TrackerEnabledDbContext
Github: https://github.com/bilal-fazlani/tracker-enabled-dbcontext
How to get a reference to superior ObjectContext from EntityObject class?
Have a look at the following link:
http://blogs.msdn.com/alexj/archive/2009/06/08/tip-24-how-to-get-the-objectcontext-from-an-entity.aspx
It is like the way that Nix mentioned as an extenxtion to the entity object.
Only way you can do it is via a hack using relationships, and an entity that is not detached. See below.
YourEntity someEntity = null;
RelationshipManager relationshipManager = ((IEntityWithRelationships)someEntity ).RelationshipManager;
IRelatedEnd relatedEnd = relationshipManager.GetAllRelatedEnds().FirstOrDefault();
ObjectQuery getContext = relatedEnd.CreateSourceQuery() as ObjectQuery;
YoutObjectContext c1 = (YourObjectContext)getContext .Context;
Good luck with it. If you use the code above i recommend protecting it with null checks.