I have simple NHibernate interceptor and override method OnSave().
Now what I am trying to do here is to get SQL length for string properties.
Is that possible.
I can see that property IType[] types contains SqlType where Length is available, but just cannot find how to read it. Example from debug:
This is example of the code which I have, and where I am trying to get Sql length of property.
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
for (int i = 0; i < propertyNames.Length; i++)
{
//If type is string
if (types[i].GetType() == typeof(NHibernate.Type.StringType))
{
//Get SQL length of string property
}
}
return false;
}
Any help how I can get this?
Let's just try to cast the IType into intended one:
//If type is string
var stringType = types[i] as NHibernate.Type.StringType;
//if (types[i].GetType() == typeof(NHibernate.Type.StringType))
if(stringType != null)
{
//Get SQL length of string property
var length = stringType.SqlType.Length;
}
Related
I have a CreateMap:
CreateMap<ExtractInsertRequestViewModel, Extract>()
.ConstructUsing(source => new Extract(source.MemberId,
source.IsChargeBack,
source.Type,
source.Description,
source.Value,
source.VerifyBalanceBeforeRedeem))
Then I execute the transfomation (vm is my viewmodel):
var extract = Mapper.Map<Extract>(vm);
But inside my Extract ctor, I have 2 functions that may change the values (SetStatus and SetValue):
public Extract(int memberId, bool isChargeBack, ExtractType type, string description, int value, bool verifyBalanceBeforeRedeem)
{
MemberId = memberId;
IsChargeBack = isChargeBack;
Type = type;
Description = description;
SetStatus(Status.A);
IsAble = true;
VerifyBalanceBeforeRedeem = verifyBalanceBeforeRedeem;
SetValue(value);
}
public void SetValue(int newValue)
{
if (Type == ExtractType.DEBIT && newValue > 0 || Type == ExtractType.CREDIT && newValue < 0)
newValue = newValue * -1;
Value = newValue;
}
The issue is, after the value was changed by any of this functions, the Extract object result is created with the original values.
For ex: I passed a Debit operation with a positive value. The SetValue method should convert to negative, but in the final object, the value still positive.
Seems to me that the ConstructUsing runs after the constructor. The constructor set the correct values but ConstructUsing overwrite the values with the original ones.
Any thoughts of what's going on here ??
Thanks in advance!
I have a ViewModel which contains properties that can optionally be filled from the user of my web application (e.g. first name, last name, e-mail, date of birth etc). These properties include several types like string, bool int, decimal, double, DateTime. All these types can be both nullable and non-nullable in the ViewModel.
If at least 1 of these properties has been filled in, then I have to create a database record. Do you have any idea for a way to check if any of these optional values has been filled?
You can implement this code, pass your viewmodel, then count how much fields are not null, if the return value>0, then you can implement your Db work.
public int MethodToCheckNotNull(ViewModel obj)
{
int i = 0;
PropertyInfo[] properties = typeof(obj).GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.GetValue(obj) != null)
{
i++;
}
}
return i;
}
By convention, primitive data types are required. You can make the properties nullable which are primitive like int, double, DateTime vs. Then:
PropertyInfo[] infos = yourObjectInstance.GetType().GetProperties();
int count = 0;
for(int i = 0; i < infos.Length; i++)
{
if(infos[i].PropertyType == typeof(string))
{
string stringValue = infos[i].GetValue(yourObjectInstance).ToString().Trim();
if(!string.IsNullOrEmpty(body))
{
count++;
}
continue;
}
if(infos[i].GetValue(yourObjectInstance) != null)
{
count++;
}
}
if(count == 0)
{
// Handle error
}
// Create record
I'm porting an open source library from the regular .NET 4 Client Profile to DNX Core 5.0. There are quite a few library changes, with properties or methods being moved around or altogether removed. I have looked at this answer but it doesn't work in my case because the method was removed.
One of the issue I have a piece of code where MethodBase.GetCurrentMethod() is called. This method no longer exists in the API. The only methods left that are similar are:
public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle);
public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle, RuntimeTypeHandle declaringType);
But I'm not sure what the 'handle' is. I need to get the MethodBase in order to access its parameters to then process them for a REST API query. This is the code that builds the object in .NET 4:
public static Annotation Annotation(string query = null, string text = null, string type = null, string name = null, string entity = null, int limit = 25, int offset = 0)
{
var search = Help.SearchToString(MethodBase.GetCurrentMethod(), query, text, type, name, entity);
return Help.Find<Annotation>(search, limit, offset, "annotation");
}
And it is then used here:
public static string SearchToString(MethodBase m, params object[] search)
{
var paras = m.GetParameters();
var result = string.Empty;
for (var i = 0; i < search.Length; i++)
{
if (search[i] != null)
{
if (i == 0)
{
result += search[i] + "%20AND%20";
}
else
{
result += paras[i].Name.ToLower() + ":" + search[i] + "%20AND%20";
}
}
}
return result.LastIndexOf("%20AND%20", StringComparison.Ordinal) > 0
? result.Substring(0, result.LastIndexOf("%20AND%20", StringComparison.Ordinal))
: result;
}
What other way could I have to access the MethodBase object parameters in SearchToString() method if I can't easily pass the said MethodBase as a parameter?
Assuming the method Annotation is in the class TestClass, use
typeof(TestClass).GetMethod(nameof(Annotation))
I'm using reflection to read in an xml file and keep coming across an error telling me that I cannot convert a string to a string[] (which I don't want to do!) I think the cause of my problem is I'm unable to tell if the type of the object is an array or not. Below is what I'm currently using (doesn't work right) but I've also tried to use if(mi[i].GetType() == typeof(string[])) which also doesnt work..
MemberInfo[] mi = objType.GetProperties();
for (int i = 0; i < mi.Length; i++)
{
if (mi[i].GetType().IsArray)
{
}
else
{
//Code path is running through here
}
The file is read in correctly..
EDIT: I thought I'd better add the structure to my objType to better explain..
objType is a class that contains a string[] variable that in this case is referred to as mi[i]
You need to use PropertyType rather than GetType() on the MemberInfo to get the underlying type of the property.
var mi = objType.GetProperties();
for (int i = 0; i < mi.Length; i++)
{
var type = mi[i].PropertyType;
//Check for string array
if (type.IsArray && type.GetElementType() == typeof(string))
{
}
}
Or you can do
if(type == typeof(string[]))
{
}
My question is if I can predict or choose the exact order of the columns the sql generation through ToTraceString() returns.
I use ToTraceString() for an IQueryable to get the resulted SQL command and then insert the results directly in a database table.
So, I kind of need the generated SQL to be consistent with my table structure...
string insertQuery = string.Format("INSERT INTO {0} {1}", sqlTableName ((System.Data.Objects.ObjectQuery<TRow>)results).ToTraceString());
Context.ExecuteStoreCommand(string.Format("TRUNCATE TABLE {0}", sqlTableName));
Context.ExecuteStoreCommand(insertQuery);
results = IQueryable<Row> where Row is a type with the same properties as the table columns
I choose to do a direct insert into a table because i see no point in getting a ToList() enumerable on the webserver, just to send it back to SQL through some kind of bulk insert (which EF doesn;t support for the moment...) My query returns a considerable amount of rows and I do not want to use Stored Procedures.
Hope I make sense...thanks
I had this issue but the answer here still required a fair amount of work to get going. I used the part of How does Entity Framework manage mapping query result to anonymous type? to get the order and returned the names, then a simple parsing to extract the field names.
I made an extension method that pulls everything together:
public static string ToWrappedString(this ObjectQuery query, out ObjectParameterCollection parameters)
{
var trace = query.ToTraceString();
parameters = query.Parameters;
var positions = query.GetPropertyPositions();
// the query should be SELECT\n
// Column AS NNN
// FROM
// so we regex this out
var regex = new Regex("^SELECT(?<columns>.*?)FROM", RegexOptions.Multiline);
var result = regex.Match(trace.Replace(Environment.NewLine, ""));
var cols = result.Groups["columns"];
// then we have the columns so split to get each
const string As = " AS ";
var colNames = cols.Value.Split(',').Select(a => a.Substring(a.IndexOf(As, StringComparison.InvariantCulture) + As.Length)).ToArray();
var wrapped = "SELECT " + String.Join(Environment.NewLine + ", ", colNames.Select((a, i) => string.Format("{0}{1} [{2}]", a, As, positions[i]))) + " FROM (" + trace
+ ") WrappedQuery ";
return wrapped;
}
This is the code from the other link, updated to EF6 internals and returning the name in column order rather than the indexes.
public static string[] GetPropertyPositions(this 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.Entity.Core.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.Entity.Core.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.Entity.Core.EntityClient.Internal.EntityCommandDefinition");
// get private readonly IColumnMapGenerator EntityCommandDefinition._columnMapGenerator;
// of actual type private sealed class
// System.Data.EntityClient.EntityCommandDefinition.ConstantColumnMapGenerator
var columnMapGeneratorArray = GetField(commandDefinition, "_columnMapGenerators") as object[];
AssertNonNullAndOfType(columnMapGeneratorArray, "System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition+IColumnMapGenerator[]");
var columnMapGenerator = columnMapGeneratorArray[0];
// 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.Entity.Core.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.Entity.Core.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.Entity.Core.Query.InternalTrees.ColumnMap[]");
int n = columnMapProperties.Length;
string[] propertyPositions = new string[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.Entity.Core.Query.InternalTrees.ScalarColumnMap");
string colName = (string)GetProperty(column, "Name");
// can be used for more advanced bingings
// get internal int ScalarColumnMap.ColumnPos;
object columnPositionOfAProperty = GetProperty(column, "ColumnPos");
AssertNonNullAndOfType(columnPositionOfAProperty, "System.Int32");
propertyPositions[(int)columnPositionOfAProperty] = colName;
}
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");
}
The accepted answer to this question contains two links which describe how to figure out what order the various properties of the entity type appear in the SQL generated by ToTraceString(). With that information, you can do some simple parsing/restructuring of the raw SQL to replace the weird column names EF uses (e. g. C1, C2, etc) with the property column names. Then, you could wrap the generated SQL in a subquery that selects the relevant columns in the order you want:
SELECT prop1, prop2
FROM
(
// the result of ToTraceString(), with EF's generated column names replaced by the property names of the query type
) x