I am new to CRM Development. I would like to update the custom field values in addtion to its existing values in the CRM 2011 from my C# application. If the field has some values then it is working fine, but if it null then i am receiving "The given key was not present in the dictionary." error.
The code below is what i am trying to achieve.
IOrganizationService service = (IOrganizationService)serviceProxy;
QueryByAttribute querybyattribute = new QueryByAttribute("salesorder");
querybyattribute.ColumnSet = new ColumnSet(new String[] {
"salesorderid", "new_customefield" });
querybyattribute.Attributes.AddRange("ordernumber");
querybyattribute.Values.AddRange(ordernumber);
EntityCollection retrieved = service.RetrieveMultiple(querybyattribute);
foreach (var c in retrieved.Entities)
{
OrderID = new Guid(c.Attributes["salesorderid"].ToString());
CustomFieldValue = c.Attributes["new_customefield"].ToString();
}
The error occurs because a field with no entered value won't get returned in the context object not the image. Or to put it graspably - you need to check whether a field is amongst the attributes.
It's not sufficient to have declared it and requested it via ColumnSet. It's confusing and annoying (been there myself).
Just of the top of my head, I can think of the following code snippet to manage the issue (without having to set an if clause for every read but also avoiding a bunch of methods - one for each variable type).
private Generic GetEntityValue<Generic>(
Entity entity, String field, Generic substitute = default(Generic))
{
if (entity.Contains(field))
return (Generic)entity[field];
return substitute;
}
edit: Or as an extension method
public static T GetAttributeValue<T> (this Entity e, string propertyName, T defaultValue = default(T))
{
if (e.Contains(propertyName))
{
return (T)e[propertyName];
}
else
{
return defaultValue;
}
}
Related
I have this code to log changes during a save:
foreach (var property in entityEntry.OriginalValues.Properties)
{
if (!object.Equals(entityEntry.OriginalValues.GetValue<object>(property),
entityEntry.CurrentValues.GetValue<object>(property)))
{
this.Events.Add(
new Event()
{
AppUserId = this._appUserProvider.CurrentAppUserId,
EventDate = DateTimeOffset.UtcNow,
EventTypeId = eventTypeId,
RecordId = entityEntry.OriginalValues.GetValue<int>("Id"),
ColumnName = property.Name,
OriginalValue =
entityEntry.OriginalValues.GetValue<object>(property) == null
? null
: entityEntry.OriginalValues.GetValue<object>(property).ToString(),
NewValue =
entityEntry.CurrentValues.GetValue<object>(property) == null
? null
: entityEntry.CurrentValues.GetValue<object>(property).ToString()
});
}
}
I got this from this question
However, I am getting an error on the first line:
Unable to cast object of type 'System.Func`2[Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry,System.Int32]' to type 'System.Func`2[Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry,System.Object]'.
It checking the first field which is an int Id field when the error is thrown.
How do I resolve? I don't quite understand what the issue is.
Maybe it worked in Entity Framework, but now doesn't in EntityFrameworkCore?
UPDATE: And suddenly, I realize what the issue is. This is an entity which was added from an API call and the state set to modified, so there is no original value in the StateTracker. So somehow I would need to get the original values to compare to check which values had changed.
The exception is from GetValue<object>(property) call.
The generic type argument of GetValue method is expected to be the exact property type, i.e. cannot be used to get generic value as object.
Instead, you can use one of the PropertyValues object indexers for that purpose, e.g. entityEntry.OriginalValues[property] and entityEntry.CurrentValues[property].
Regarding the update. When you attach entity instance, the OriginalValues and CurrentValues will be one and the same. In such case, instead of the OriginalValues property you could use the result of the GetDatabaseValues method call, e.g.
var originalValues = entityEntry.GetDatabaseValues();
As far as I knew, Object.GetType() should never return null. (related discussion)
Dapper .Query() return private class DapperRow instances to be treated as dynamic objects. I found a strange thing: DapperRow's .GetType() return null.
Here's the sample code to reproduce the problem. Create a C# project, reference Dapper and open a connection to SQL Server (or other database), use .Query() to execute simple select query and retrieve the first row of result. Use GetType() to get the type of result object, the return value is null.
using (SqlConnection cn = new SqlConnection(csSql))
{
var rec = cn.Query("select getdate() as D").Single();
var t = rec.GetType(); // t == null
Console.WriteLine(t.Name); // null reference exception
}
I suspect that dynamic or private type is the cause of null, so I write my class library for test:
namespace Lib
{
public class Blah
{
public static dynamic SecretObject;
static Blah()
{
SecretObject = new PrivateType();
}
}
class PrivateType
{
}
}
In another project, get the dynamic type static field and call GetType():
dynamic obj = Lib.Blah.SecretObject;
Console.WriteLine(obj.GetType().Name); // "Lib.PrivateType"
According to the test result, even cast private type as dynamic, I still can get the private type information from GetType(), why DapperRow.GetType() return null?
DapperRow is specifically built and utilized within Dapper to provide highly optimized row returns without reiterating header information. This is to help condense the size of the object and reduce redundant data, making it more efficient.
However, it would appear that the StackExchange team took the meta programming even further than a first glance would indicate.
DapperRow implements the System.Dynamic.IDynamicMetaObjectProvide interface, which requires that the GetMetaObject method be implemented:
System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
System.Linq.Expressions.Expression parameter)
{
return new DapperRowMetaObject(parameter,
System.Dynamic.BindingRestrictions.Empty, this);
}
DapperRowMetaObject is a custom implementation of DynamicMetaObject that essentially hijacks and overrides what methods can be invoked against the dynamic type and what those calls should translate to. In this case, calls to anything other than the DapperRow's IDictionary.Item getter or the DapperRow.SetValue will fail since they are always routed to those two calls, but the value will be defaulted to null for any "get" calls where the target property does not exist in the table.
public bool TryGetValue(string name, out object value)
{
var index = table.IndexOfName(name);
if (index < 0)
{ // doesn't exist
value = null;
return false;
}
...
}
At that point, any methods invoked on a null dynamic value will throw a RuntimeBinderException:
RuntimeBinderException: Cannot perform runtime binding on a null
reference
You can easily test this hypothesis by replacing GetType() with another call that will throw the exact same exception:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.AsEnumerable();
Console.WriteLine(t.ToList());
Keep in mind, the underlying type information of any properties on the dynamic object itself can still be accessed directly:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.D.GetType();
Console.WriteLine(t.Name);
Here is a portion of a code ..
public Admin_GetCourseById_spResult GetCourseById(long? courseId, short languageId)
{
ISQUserDataContext db = CreateDataContext();
Admin_GetCourseById_spResult result;
result = db.Admin_GetCourseById_sp(courseId,false,languageId).FirstOrDefault();
return result;
}
the third line in the function throws error Specified cast is not valid.
Any clue about whats happening ??
Small rewrite (needs more work)
public Course GetCourseById(long? courseId, short languageId)
{
ISQUserDataContext db = CreateDataContext();
return new Course(db.Admin_GetCourseById_sp(courseId, false, languageId).FirstOrDefault());
}
and then add a class Course with a constructor accepting the returntype of Admin_GetCourseById_sp to build a nice Course object.
Your
db.Admin_GetCourseById_sp(courseId,false,languageId).FirstOrDefault() line returns a datarow.
(It might return null if there are no data)
Therefore what you can do is this,
you can set each attribute of your
Admin_GetCourseById_spResult result object with the matching value from the returned datarow
i.e result.courseId = row["course_id"].ToString()
{ I assumed that courseId is String in your Admin_GetCourseById_spResult class + the matching db column is course_id.
You have given little data, so could answer only like this. Good Luck !
Consider the following example LINQ to entity query
from history in entities.foreignuserhistory
select new { history.displayname, login=history.username, history.foreignuserid }
ToTraceString() return string looks like:
SELECT "Extent1"."foreignuserid" AS "foreignuserid",
"Extent1"."displayname" AS "displayname",
"Extent1"."username" AS "username"
FROM "integration"."foreignuserhistory" AS "Extent1"
The problem for me is that columns come in different order from query and do not take aliases like login in the example. Where does Entity Framework store mapping information for anonymous types?
Background: I'm going to develop insert with select operation using LINQ to entity for mass operations.
Update:
Insert with select is not that hard except for an unknown column to property mapping algorithm. One can get table and column names for destination ObjectSet using metadata, build INSERT INTO tableName (column_name1, …) sql statement string and then append some ObjectQuery.ToTraceString SELECT statement. Then create a DbCommand with resulting text using ((EntityConnection)ObjectContext.Connection).StoreConnection and fill command’s parameters from ObjectQuery. So the problem is to find matching column order in inserted and selected records.
Here’s my solution all the way down of privates and internals. It travels with reflection into cached query plan which will exist after ToTraceString call or query execution to get what is called _columnMap. Column map contains ScalarColumnMap objects going in the order of anonymous object’s properties and pointing to the corresponding column position with ColumnPos property.
using System;
using System.Data.Objects;
using System.Reflection;
static class EFQueryUtils
{
public static int[] GetPropertyPositions(ObjectQuery query)
{
// get private ObjectQueryState ObjectQuery._state;
// of actual type internal class
// System.Data.Objects.ELinq.ELinqQueryState
object queryState = GetProperty(query, "QueryState");
AssertNonNullAndOfType(queryState, "System.Data.Objects.ELinq.ELinqQueryState");
// get protected ObjectQueryExecutionPlan ObjectQueryState._cachedPlan;
// of actual type internal sealed class
// System.Data.Objects.Internal.ObjectQueryExecutionPlan
object plan = GetField(queryState, "_cachedPlan");
AssertNonNullAndOfType(plan, "System.Data.Objects.Internal.ObjectQueryExecutionPlan");
// get internal readonly DbCommandDefinition ObjectQueryExecutionPlan.CommandDefinition;
// of actual type internal sealed class
// System.Data.EntityClient.EntityCommandDefinition
object commandDefinition = GetField(plan, "CommandDefinition");
AssertNonNullAndOfType(commandDefinition, "System.Data.EntityClient.EntityCommandDefinition");
// get private readonly IColumnMapGenerator EntityCommandDefinition._columnMapGenerator;
// of actual type private sealed class
// System.Data.EntityClient.EntityCommandDefinition.ConstantColumnMapGenerator
object columnMapGenerator = GetField(commandDefinition, "_columnMapGenerator");
AssertNonNullAndOfType(columnMapGenerator, "System.Data.EntityClient.EntityCommandDefinition+ConstantColumnMapGenerator");
// get private readonly ColumnMap ConstantColumnMapGenerator._columnMap;
// of actual type internal class
// System.Data.Query.InternalTrees.SimpleCollectionColumnMap
object columnMap = GetField(columnMapGenerator, "_columnMap");
AssertNonNullAndOfType(columnMap, "System.Data.Query.InternalTrees.SimpleCollectionColumnMap");
// get internal ColumnMap CollectionColumnMap.Element;
// of actual type internal class
// System.Data.Query.InternalTrees.RecordColumnMap
object columnMapElement = GetProperty(columnMap, "Element");
AssertNonNullAndOfType(columnMapElement, "System.Data.Query.InternalTrees.RecordColumnMap");
// get internal ColumnMap[] StructuredColumnMap.Properties;
// array of internal abstract class
// System.Data.Query.InternalTrees.ColumnMap
Array columnMapProperties = GetProperty(columnMapElement, "Properties") as Array;
AssertNonNullAndOfType(columnMapProperties, "System.Data.Query.InternalTrees.ColumnMap[]");
int n = columnMapProperties.Length;
int[] propertyPositions = new int[n];
for (int i = 0; i < n; ++i)
{
// get value at index i in array
// of actual type internal class
// System.Data.Query.InternalTrees.ScalarColumnMap
object column = columnMapProperties.GetValue(i);
AssertNonNullAndOfType(column, "System.Data.Query.InternalTrees.ScalarColumnMap");
//string colName = (string)GetProp(column, "Name");
// can be used for more advanced bingings
// get internal int ScalarColumnMap.ColumnPos;
object columnPositionOfAProperty = GetProperty(column, "ColumnPos");
AssertNonNullAndOfType(columnPositionOfAProperty, "System.Int32");
propertyPositions[i] = (int)columnPositionOfAProperty;
}
return propertyPositions;
}
static object GetProperty(object obj, string propName)
{
PropertyInfo prop = obj.GetType().GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Instance);
if (prop == null) throw EFChangedException();
return prop.GetValue(obj, new object[0]);
}
static object GetField(object obj, string fieldName)
{
FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field == null) throw EFChangedException();
return field.GetValue(obj);
}
static void AssertNonNullAndOfType(object obj, string fullName)
{
if (obj == null) throw EFChangedException();
string typeFullName = obj.GetType().FullName;
if (typeFullName != fullName) throw EFChangedException();
}
static InvalidOperationException EFChangedException()
{
return new InvalidOperationException("Entity Framework internals has changed, please review and fix reflection code");
}
}
I think some assertions can be relaxed to check not the exact type but base type containing necessary property.
Is there a solution without reflection?
How the columns are aliased in the query shouldn't matter, and neither should their order. Entity Framework handles populating a new instance of your anonymous type with each result, and that's where you get the alias like login.
As a side note, I think Entity Framework may not work quite how you think. You can't do a select/insert in a single operation like you can using a normal SQL query. Entity Framework will execute your select, return back the results, use those results to create new instances of your entities (or in your case, an anonymous type), and you would then have to use each result to create a new instance of your target type, adding each one to your entity/object context, and finally call save changes on your entity/object context. This will cause an individual insert statement to be executed for each new entity that you've added.
If you want to do it all in a single operation without instantiating a new entity for every record, you'll need to either use a stored procedure that you map in your context, or else execute an in-line SQL query using ObjectContext.ExecuteStoreCommand
UPDATE: Based on your responses, what you're really getting into is closer to meta-programming that relies on your entity model more so than actually using entity framework. I don't know what version of EF you're using (EF 4.0? 4.1 w/ code first and DbContext?), but I've had a lot of success using the C# POCO template with EF 4.0 (the POCO template is a download from the online visual studio gallery). It uses a T4 template to generate POCO classes from the .edmx data model. In your T4 template, you could add methods to your context that would essentially call ExecuteStoreCommand, but the difference would be you can generate the query that gets executed based on your data model. That way any time your data model changes, your query would stay in sync with the changes.
Updated the reflection on this for EF 4.4 (5-RC)
full post at http://imaginarydevelopment.blogspot.com/2012/06/compose-complex-inserts-from-select.html
using this functionality/logic for doing a bulk insert from a select with some parameters provided
int Insert<T>(IQueryable query,IQueryable<T> targetSet)
{
var oQuery=(ObjectQuery)this.QueryProvider.CreateQuery(query.Expression);
var sql=oQuery.ToTraceString();
var propertyPositions = GetPropertyPositions(oQuery);
var targetSql=((ObjectQuery)targetSet).ToTraceString();
var queryParams=oQuery.Parameters.ToArray();
System.Diagnostics.Debug.Assert(targetSql.StartsWith("SELECT"));
var queryProperties=query.ElementType.GetProperties();
var selectParams=sql.Substring(0,sql.IndexOf("FROM "));
var selectAliases=Regex.Matches(selectParams,#"\sAS \[([a-zA-Z0-9_]+)\]").Cast<Match>().Select(m=>m.Groups[1].Value).ToArray();
var from=targetSql.Substring(targetSql.LastIndexOf("FROM [")+("FROM [".Length-1));
var fromAlias=from.Substring(from.LastIndexOf("AS ")+"AS ".Length);
var target=targetSql.Substring(0,targetSql.LastIndexOf("FROM ["));
target=target.Replace("SELECT","INSERT INTO "+from+" (")+")";
target=target.Replace(fromAlias+".",string.Empty);
target=Regex.Replace(target,#"\sAS \[[a-zA-z0-9]+\]",string.Empty);
var insertParams=target.Substring(target.IndexOf('('));
target = target.Substring(0, target.IndexOf('('));
var names=Regex.Matches(insertParams,#"\[([a-zA-Z0-9]+)\]");
var remaining=names.Cast<Match>().Select(m=>m.Groups[1].Value).Where(m=>queryProperties.Select(qp=>qp.Name).Contains(m)).ToArray(); //scrape out items that the anonymous select doesn't include a name/value for
//selectAliases[propertyPositions[10]]
//remaining[10]
var insertParamsOrdered = remaining.Select((s, i) => new { Position = propertyPositions[i], s })
.OrderBy(o => o.Position).Select(x => x.s).ToArray();
var insertParamsDelimited = insertParamsOrdered.Aggregate((s1, s2) => s1 + "," + s2);
var commandText = target + "(" + insertParamsDelimited + ")" + sql;
var result=this.ExecuteStoreCommand(commandText,queryParams.Select(qp=>new System.Data.SqlClient.SqlParameter{ ParameterName=qp.Name, Value=qp.Value}).ToArray());
return result;
}
Is there a way to force LINQ-to-SQL to treat a column as dirty? Globally would suffice....
Basically, I've got a problem with some audit code on a legacy system that I'm talking to with L2S, imagine:
var ctx = new SomeDataContext(); // disposed etc - keeping it simple for illustration
var cust = ctx.Customers.First(); // just for illustration
cust.SomeRandomProperty = 17; // whatever
cust.LastUpdated = DateTime.UtcNowl;
cust.UpdatedBy = currentUser;
ctx.SubmitChanges(); // uses auto-generated TSQL
This is fine, but if the same user updates it twice in a row, the UpdatedBy is a NOP, and the TSQL will be (roughly):
UPDATE [dbo].[Customers]
SET SomeRandomColumn = #p0 , LastUpdated = #p1 -- note no UpdatedBy
WHERE Id = #p2 AND Version = #p3
In my case, the problem is that there is currently a belt-and-braces audit trigger on all tables, which checks to see if the audit column has been updated, and if not assumes the developer is at fault (substituting SUSER_SNAME(), although it could just as readily throw an error).
What I'd really like to be able to do is say "always update this column, even if it isn't dirty" - is this possible?
Based on KristoferA's answer, I ended up with something like below; this is evil and brittle (reflection often is), but may have to suffice for now. The other side of the battle is to change the triggers to behave:
partial class MyDataContext // or a base-class
{
public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode)
{
this.MakeUpdatesDirty("UpdatedBy", "Updated_By");
base.SubmitChanges(failureMode);
}
}
public static class DataContextExtensions
{
public static void MakeUpdatesDirty(
this DataContext dataContext,
params string[] members)
{
if (dataContext == null) throw new ArgumentNullException("dataContext");
if (members == null) throw new ArgumentNullException("members");
if (members.Length == 0) return; // nothing to do
foreach (object instance in dataContext.GetChangeSet().Updates)
{
MakeDirty(dataContext, instance, members);
}
}
public static void MakeDirty(
this DataContext dataContext, object instance ,
params string[] members)
{
if (dataContext == null) throw new ArgumentNullException("dataContext");
if (instance == null) throw new ArgumentNullException("instance");
if (members == null) throw new ArgumentNullException("members");
if (members.Length == 0) return; // nothing to do
const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
object commonDataServices = typeof(DataContext)
.GetField("services", AllInstance)
.GetValue(dataContext);
object changeTracker = commonDataServices.GetType()
.GetProperty("ChangeTracker", AllInstance)
.GetValue(commonDataServices, null);
object trackedObject = changeTracker.GetType()
.GetMethod("GetTrackedObject", AllInstance)
.Invoke(changeTracker, new object[] { instance });
var memberCache = trackedObject.GetType()
.GetField("dirtyMemberCache", AllInstance)
.GetValue(trackedObject) as BitArray;
var entityType = instance.GetType();
var metaType = dataContext.Mapping.GetMetaType(entityType);
for(int i = 0 ; i < members.Length ; i++) {
var member = entityType.GetMember(members[i], AllInstance);
if(member != null && member.Length == 1) {
var metaMember = metaType.GetDataMember(member[0]);
if (metaMember != null)
{
memberCache.Set(metaMember.Ordinal, true);
}
}
}
}
}
Unfortunately, I think you will have to use a new DataContext
Details at: http://blog.benhall.me.uk/2008/01/custom-insert-logic-with-linq-to-sql.html
You can override the default update behavior. There are 2 ways of doing this
The easiest is to create a stored procedure (if you can't do that on your database, the second method should work) which takes the parameters of your customer object and updates the table:
Create the stored procedure that has a parameter for each property of Customers that needs to be updated.
Import that stored procedure into your Linq To SQL DBML file.
Now you can right click on your customers entity and select "Configure Behavior".
Select your Customers class under the Class dropdown and "Update" on the behavior drop down.
Select the "Customize" radio button and choose the stored procedure you just created.
Now you can map class's properties to the stored procedure.
Now when Linq to SQL tries to update your Customers table, it'll use your stored procedure instead. Just be careful because this will override the update behavior for Customers everywhere.
The second method is to use partial methods. I haven't actually tried this, so hopefully this might just give you some general direction to pursue. In a partial class for your data context, make a partial method for the update (It'll be Update_____ with whatever your class is in the blank. I'd suggest searching in your data context's designer file to make sure you get the right one)
public partial SomeDataContext
{
partial void UpdateCustomer(Customer instance)
{
// this is where you'd do the update, but I'm not sure exactly how it's suppose to work, though. :(
}
}
If you want to go down the [dirty] reflection route, you could try something along the lines of:
1) Override SubmitChanges
2) Go through the change set
3) Use reflection to get hold of the change tracker for each updated object (see What's the cleanest way to make a Linq object "dirty"? )
4) Make the column dirty (there's a dirtyMemberCache field in the StandardTrackedObject class)
The following works for me. Note though that I'm using the linq2sql provider from DevArt, but that may not matter:
MyDataContext dc = new MyDataContext();
Message msg = dc.Messages.Single(m => m.Id == 1);
Message attachingMsg = new Message();
attachingMsg.Id = msg.Id;
dc.Messages.Attach(attachingMsg);
attachingMsg.MessageSubject = msg.MessageSubject + " is now changed"; // changed
attachingMsg.MessageBody = msg.MessageBody; // not changed
dc.SubmitChanges();
This produces the following sql:
UPDATE messages SET messageSubject = :p1, messageBody = :p2 WHERE Id = :key1
So, messageBody is updated even though its value is not changed.
One other change necessary for this, is that for each property (column) of my entity Message, I have set UpdatedCheck = UpdateCheck.Never, except for its ID, which is the primary key.