I am working with Umbraco 4.7.1 and I am trying to map the content-nodes to some autogenerated strong typed objects. I have tried using both valueinjecter and automapper, but OOTB neither of them map my properties. I guess it is because all properties on an Umbraco node (the cms document) are retrieved like this:
node.GetProperty("propertyName").Value;
And my strongly typed objects are in the format of MyObject.PropertyName. So how do I map the property on the node which is retrieved using a method and a string beginning with a lowercase character into a property on MyObject where the property begins with an uppercase character ?
UPDATE
I managed to create the following code which maps the umbraco node as intended, by digging around in the Umbraco sourcecode for some inspiration on how to cast string-properties to strongly typed properties:
public class UmbracoInjection : SmartConventionInjection
{
protected override bool Match(SmartConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
protected override void Inject(object source, object target)
{
if (source != null && target != null)
{
Node node = source as Node;
var props = target.GetProps();
var properties = node.Properties;
for (int i = 0; i < props.Count; i++)
{
var targetProperty = props[i];
var sourceProperty = properties[targetProperty.Name];
if (sourceProperty != null && !string.IsNullOrWhiteSpace(sourceProperty.Value))
{
var value = sourceProperty.Value;
var type = targetProperty.PropertyType;
if (targetProperty.PropertyType.IsValueType && targetProperty.PropertyType.GetGenericArguments().Length > 0 && typeof(Nullable<>).IsAssignableFrom(targetProperty.PropertyType.GetGenericTypeDefinition()))
{
type = type.GetGenericArguments()[0];
}
targetProperty.SetValue(target, Convert.ChangeType(value, type));
}
}
}
}
}
As you can see I use the SmartConventionInjection to speed things up.
It still takes approximately 20 seconds to map something like 16000 objects. Can this be done even faster ?
thanks
Thomas
with ValueInjecter you would do something like this:
public class Um : ValueInjection
{
protected override void Inject(object source, object target)
{
var node = target as Node;
var props = source.GetProps();
for (int i = 0; i < props.Count; i++)
{
var prop = props[i];
target.GetProperty(prop.Name).Value;
}
}
}
Related
Given the name of a Migrations class as a string, how can I get the current version number as stored in Orchard_Framework_DataMigrationRecord?
I can see Version in IExtensionManager, but that appears to just be the module version as defined in module.txt.
OK, so I've solved this myself-
I knew that Orchard must already be executing similar code to what I require when it fires off migration methods, so I created a new migrations file, and put a breakpoint on the Create() method. When the breakpoint hit, I looked up through the call stack to find DataMigrationManager in Orchard.Data.Migration. Everything I needed was in there, and if anyone else has similar requirements, I suggest they have a look at that class as a starting point.
This is pretty much lifted straight out of that class:
string moduleName="Your.Module.Name";
var migrations = GetDataMigrations(moduleName);
// apply update methods to each migration class for the module
var current = 0;
foreach (var migration in migrations)
{
// copy the objet for the Linq query
var tempMigration = migration;
// get current version for this migration
var dataMigrationRecord = GetDataMigrationRecord(tempMigration);
if (dataMigrationRecord != null)
{
current = dataMigrationRecord.Version.Value;
}
// do we need to call Create() ?
if (current == 0)
{
// try to resolve a Create method
var createMethod = GetCreateMethod(migration);
if (createMethod != null)
{
//create method has been written, but not executed!
current = (int)createMethod.Invoke(migration, new object[0]);
}
}
}
Context.Output.WriteLine("Version: {0}", current);
A couple of methods you may need:
private DataMigrationRecord GetDataMigrationRecord(IDataMigration tempMigration)
{
return _dataMigrationRepository.Table
.Where(dm => dm.DataMigrationClass == tempMigration.GetType().FullName)
.FirstOrDefault();
}
private static MethodInfo GetCreateMethod(IDataMigration dataMigration)
{
var methodInfo = dataMigration.GetType().GetMethod("Create", BindingFlags.Public | BindingFlags.Instance);
if (methodInfo != null && methodInfo.ReturnType == typeof(int))
{
return methodInfo;
}
return null;
}
Don't forget to inject any dependencies that you may need.
I have the following code which copies property values from one object to another objects by matching their property names:
public static void CopyProperties(object source, object target,bool caseSenstive=true)
{
PropertyInfo[] targetProperties = target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
PropertyInfo[] sourceProperties = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo tp in targetProperties)
{
var sourceProperty = sourceProperties.FirstOrDefault(p => p.Name == tp.Name);
if (sourceProperty == null && !caseSenstive)
{
sourceProperty = sourceProperties.FirstOrDefault(p => p.Name.ToUpper() == tp.Name.ToUpper());
}
// If source doesn't have this property, go for next one.
if(sourceProperty ==null)
{
continue;
}
// If target property is not writable then we can not set it;
// If source property is not readable then cannot check it's value
if (!tp.CanWrite || !sourceProperty.CanRead)
{
continue;
}
MethodInfo mget = sourceProperty.GetGetMethod(false);
MethodInfo mset = tp.GetSetMethod(false);
// Get and set methods have to be public
if (mget == null)
{
continue;
}
if (mset == null)
{
continue;
}
var sourcevalue = sourceProperty.GetValue(source, null);
tp.SetValue(target, sourcevalue, null);
}
}
This is working well when the type of properties on target and source are the same. But when there is a need for casting, the code doesn't work.
For example, I have the following object:
class MyDateTime
{
public static implicit operator DateTime?(MyDateTime myDateTime)
{
return myDateTime.DateTime;
}
public static implicit operator DateTime(MyDateTime myDateTime)
{
if (myDateTime.DateTime.HasValue)
{
return myDateTime.DateTime.Value;
}
else
{
return System.DateTime.MinValue;
}
}
public static implicit operator MyDateTime(DateTime? dateTime)
{
return FromDateTime(dateTime);
}
public static implicit operator MyDateTime(DateTime dateTime)
{
return FromDateTime(dateTime);
}
}
If I do the following, the implicit cast is called and everything works well:
MyDateTime x= DateTime.Now;
But when I have a two objects that one of them has a DateTime and the other has MyDateTime, and I am using the above code to copy properties from one object to other, it doesn't and generate an error saying that DateTime can not converted to MyTimeDate.
How can I fix this problem?
One ghastly approach which should work is to mix dynamic and reflection:
private static T ConvertValue<T>(dynamic value)
{
return value; // This will perform conversion automatically
}
Then:
var sourceValue = sourceProperty.GetValue(source, null);
if (sourceProperty.PropertyType != tp.PropertyType)
{
var method = typeof(PropertyCopier).GetMethod("ConvertValue",
BindingFlags.Static | BindingFlags.NonPublic);
method = method.MakeGenericMethod(new[] { tp.PropertyType };
sourceValue = method.Invoke(null, new[] { sourceValue });
}
tp.SetValue(target, sourceValue, null);
We need to use reflection to invoke the generic method with the right type argument, but dynamic typing will use the right conversion operator for you.
Oh, and one final request: please don't include my name anywhere near this code, whether it's in comments, commit logs. Aargh.
I've created the simple VS Package for adding new item in the context menu of solution explorer. In that I need to check Selected Project's Project Type GUID. How can i get this.
For example, One Solution contains the three different type of projects, like WindowFormsApplication, MVC Projects,WebApplication. While select the MVC Projects, we need to get that ProjectType GUID.
I've tried the followings in my Package.cs,
IVsMonitorSelection monitorSelection = (IVsMonitorSelection)Package.GetGlobalService(typeof(SVsShellMonitorSelection));
monitorSelection.GetCurrentSelection(out hierarchyPtr, out projectItemId, out mis, out selectionContainerPtr);
IVsHierarchy hierarchy = Marshal.GetTypedObjectForIUnknown(hierarchyPtr, typeof(IVsHierarchy)) as IVsHierarchy;
if (hierarchy != null)
{
object prjItemObject;
hierarchy.GetProperty(projectItemId, (int)__VSHPROPID.VSHPROPID_ExtObject, out prjItemObject);
string projectTypeGuid;
Project prjItem = prjItemObject as Project;
projectTypeGuid = prjItem.Kind;
}
In that I get GUID as "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC" for all selected Projects.
Could anyone please help me this?
I've found answer for this,
Reference: https://www.mztools.com/articles/2007/MZ2007016.aspx
public string GetProjectTypeGuids(EnvDTE.Project proj)
{
string projectTypeGuids = "";
object service = null;
Microsoft.VisualStudio.Shell.Interop.IVsSolution solution = null;
Microsoft.VisualStudio.Shell.Interop.IVsHierarchy hierarchy = null;
Microsoft.VisualStudio.Shell.Interop.IVsAggregatableProject aggregatableProject = null;
int result = 0;
service = GetService(proj.DTE, typeof(Microsoft.VisualStudio.Shell.Interop.IVsSolution));
solution = (Microsoft.VisualStudio.Shell.Interop.IVsSolution)service;
result = solution.GetProjectOfUniqueName(proj.UniqueName, out hierarchy);
if (result == 0)
{
aggregatableProject = (Microsoft.VisualStudio.Shell.Interop.IVsAggregatableProject)hierarchy;
result = aggregatableProject.GetAggregateProjectTypeGuids(out projectTypeGuids);
}
return projectTypeGuids;
}
public object GetService(object serviceProvider, System.Type type)
{
return GetService(serviceProvider, type.GUID);
}
public object GetService(object serviceProviderObject, System.Guid guid)
{
object service = null;
Microsoft.VisualStudio.OLE.Interop.IServiceProvider serviceProvider = null;
IntPtr serviceIntPtr;
int hr = 0;
Guid SIDGuid;
Guid IIDGuid;
SIDGuid = guid;
IIDGuid = SIDGuid;
serviceProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)serviceProviderObject;
hr = serviceProvider.QueryService(ref SIDGuid, ref IIDGuid, out serviceIntPtr);
if (hr != 0)
{
System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr);
}
else if (!serviceIntPtr.Equals(IntPtr.Zero))
{
service = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(serviceIntPtr);
System.Runtime.InteropServices.Marshal.Release(serviceIntPtr);
}
return service;
}
}
Its working fine for my requirement.
Using the Client Object Model (C#) in SharePoint 2010, how can I determine if a specified column (field) name exists in a given List?
Thanks, MagicAndi.
Just found this while searching for the same thing, but it looks like Sharepoint 2010 has something built in for this, at least for the Server model: list.Fields.ContainsField("fieldName");
Not sure if it exists for Client side though. Figured it would be a good place to store this information however.
Server Object Model
string siteUrl = "http://mysite";
using (SPSite site = new SPSite(siteUrl))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["my forum"];
for (int i = 0; i < list.Fields.Count; i++)
{
if (list.Fields[i].Title == "xyz")
{
-
-
}
}
}
}
Client Object Model
string siteUrl = "http://MyServer/sites/MySiteCollection";
ClientContext clientContext = new ClientContext(siteUrl);
SP.List List = clientContext.Web.Lists.GetByTitle("my forum");
for (int i = 0; i < list.Fields.Count; i++)
{
if (list.Fields[i].Title == "xyz")
{
-
-
}
}
The following method demonstrates how to determine whether a specified column exists in a List using CSOM:
static class FieldCollectionExtensions
{
public static bool ContainsField(this List list,string fieldName)
{
var ctx = list.Context;
var result = ctx.LoadQuery(list.Fields.Where(f => f.InternalName == fieldName));
ctx.ExecuteQuery();
return result.Any();
}
}
Usage
using(var ctx = new ClientContext(webUrl))
{
var list = ctx.Web.Lists.GetByTitle(listTitle);
if(list.ContainsField("Title")){
//...
}
}
Here's an extension code (CSOM) for sharepoint list
public static bool DoesFieldExist(this List list, ClientContext clientContext, string internalFieldname)
{
bool exists = false;
clientContext.Load(list.Fields, fCol => fCol.Include(
f => f.InternalName
).Where(field => field.InternalName == internalFieldname));
clientContext.ExecuteQuery();
if (list.Fields != null && list.Fields.Count > 0)
{
exists = true;
}
return exists;
}
usage
List targetList = this.Context.Web.Lists.GetById(<ListID>);
targetList.DoesFieldExist(<ClientContext>, <Field internal Name>)
enjoy :)
I ended up retrieving the details of the list's fields prior to my operation, and saving them in a generic list of structs (containing details of each field). I then query this (generic) list to see if the current field actually exists in the given (SharePoint) list.
// Retrieve detail sof all fields in specified list
using (ClientContext clientContext = new ClientContext(SharePointSiteUrl))
{
List list = clientContext.Web.Lists.GetByTitle(listName);
_listFieldDetails = new List<SPFieldDetails>();
// get fields name and their types
ClientObjectPrototype allFields = list.Fields.RetrieveItems();
allFields.Retrieve( FieldPropertyNames.Title,
FieldPropertyNames.InternalName,
FieldPropertyNames.FieldTypeKind,
FieldPropertyNames.Id,
FieldPropertyNames.ReadOnlyField);
clientContext.ExecuteQuery();
foreach (Field field in list.Fields)
{
SPFieldDetails fieldDetails = new SPFieldDetails();
fieldDetails.Title = field.Title;
fieldDetails.InternalName = field.InternalName;
fieldDetails.Type = field.FieldTypeKind;
fieldDetails.ID = field.Id;
fieldDetails.ReadOnly = field.ReadOnlyField;
listFieldDetails.Add(fieldDetails);
}
}
// Check if field name exists
_listFieldDetails.Exists(field => field.Title == fieldName);
// Struct to hold details of the field
public struct SPFieldDetails
{
public string Title { get; set; }
public string InternalName { get; set; }
public Guid ID { get; set; }
public FieldType Type { get; set; }
public bool ReadOnly { get; set; }
}
Some good answers above. I personally used this one:
List list = ctx.Web.Lists.GetByTitle("Some list");
FieldCollection fields = list.Fields;
IEnumerable<Field> fieldsColl = ctx.LoadQuery(fields.Include(f => f.InternalName));
ctx.ExecuteQuery();
bool fieldMissing = fieldsColl.Any(f => f.InternalName != "Internal_Name");
You can also use 'Where' after Include method and check if returned collection/field is null. It's about personal preference, because both options are querying on client side.
I prefer the SharePoint Plus Library as it is really clean:
http://aymkdn.github.io/SharepointPlus/symbols/%24SP%28%29.list.html
$SP().list("My List").get({
fields:"Title",
where:"Author = '[Me]'"
},function getData(row) {
console.log(row[0].getAttribute("Title"));
});
You could setup a for loop to loop through the row and check if the column you're looking for exists.
A cut down and simplified version of Mitya's extension method:
public static bool FieldExists(this List list, string internalFieldname)
{
using (ClientContext clientContext = list.Context as ClientContext)
{
clientContext.Load(list.Fields, fCol => fCol.Include(
f => f.InternalName
).Where(field => field.InternalName == internalFieldname));
clientContext.ExecuteQuery();
return (list.Fields != null) && (list.Fields.Count > 0);
}
}
There's no need to pass in a separate client context parameter when you can already use the context that comes in with the list.
to much code use this
load Fields first then
bool exists= clientContext2.Site.RootWeb.Fields.Any(o => o.Id.ToString() == a.Id.ToString());
I am building an asp.net site in .net framework 4.0, and I am stuck at the method that supposed to call a .cs class and get the query result back here is my method call and method
1: method call form aspx.cs page:
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
2: Method in helper class:
public IQueryable<VariablesForIQueryble> GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some connection_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where (gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)
select new VariablesForIQueryble(m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//select new {m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap};
return query ;
}
I tried the above code with IEnumerable too without any luck. This is the code for class VariablesForIQueryble:
3:Class it self for taking anonymouse type and cast it to proper types:
public class VariablesForIQueryble
{
private int _emailCap;
public int EmailCap
{
get { return _emailCap; }
set { _emailCap = value; }
}`....................................
4: and a constructor:
public VariablesForIQueryble(int memberID, string memberFirst, string memberLast, string memberEmail, int? validEmail, int? emailCap)
{
this.EmailCap = (int) emailCap;
.........................
}
I can't seem to get the query result back, first it told me anonymous type problem, I made a class after reading this: link text; and now it tells me constructors with parameters not supported. Now I am an intermediate developer, is there an easy solution to this or do I have to take my query back to the .aspx.cs page.
If you want to project to a specific type .NET type like this you will need to force the query to actually happen using either .AsEnumerable() or .ToList() and then use .Select() against linq to objects.
You could leave your original anonymous type in to specify what you want back from the database, then call .ToList() on it and then .Select(...) to reproject.
You can also clean up your code somewhat by using an Entity Association between Groups and Members using a FK association in the database. Then the query becomes a much simpler:
var result = ctx.Members11.Include("Group").Where(m => m.Group.groupID == incomingGroupID && m.EmailCap == incomingEmailCap);
You still have the issue of having to do a select to specify which columns to return and then calling .ToList() to force execution before reprojecting to your new type.
Another alternative is to create a view in your database and import that as an Entity into the Entity Designer.
Used reflection to solve the problem:
A: Query, not using custom made "VariablesForIQueryble" class any more:
//Method in helper class
public IEnumerable GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where ((gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)) //select m;
select new { m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap };
//select new VariablesForIQueryble (m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//List<object> lst = new List<object>();
//foreach (var i in query)
//{
// lst.Add(i.MemberEmail);
//}
//return lst;
//return query.Select(x => new{x.MemberEmail,x.MemberID,x.ValidEmail,x.MemberFirst,x.MemberLast}).ToList();
return query;
}
B:Code to catch objects and conversion of those objects using reflection
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
if (query != null)
{
foreach (var objRow in query)
{
System.Type type = objRow.GetType();
int memberId = (int)type.GetProperty("MemberID").GetValue(objRow, null);
string memberEmail = (string)type.GetProperty("MemberEmail").GetValue(objRow, null);
}
else
{
something else....
}