I have a class and enum,how can I initilize like this way, my initializer,
SkSqlPamameter prm = new SkSqlPamameter
{
ParameterName = "#param1",
Value = "param1", SkSqlDbType.Int
};
and class and enum;
enum SkSqlDbType
{
Int,
Nvarchar,
Date,
Bool,
Decimal,
Double
}
public class SkSqlPamameter
{
public string ParameterName;
public SkSqlDbType SkDbType;
public string Value;
}
SkSqlPamameter prm = new SkSqlPamameter
{
ParameterName = "#param1",
Value = "param1",
SkDbType = SkSqlDbType.Int // you missed property name here
};
Object initializer contains member initializers, which should look like identifier = initializer-value. From C# specification 7.6.10.2 Object initializers:
Each member initializer must name an accessible field or property of
the object being initialized, followed by an equals sign and an
expression or an object initializer or collection initializer.
Related
I'm trying to pass an oracle User Defined Type to a stored procedure as an input parameter using C# (Which is a collection), but each time I receive back an InvalidCastException saying "Column contains NULL data".
I have been followed a tutorial which shows the steps needed to achieve this.
https://www.codeproject.com/Articles/1139474/ODP-NET-User-Defined-Type-Implementation
Each time, the code reaches
await command.ExecuteNonQueryAsync();
And then throws the exception
type ARTICLES_TYP is record(
article_id number(6),
article_name varchar(50),
update_date date
);
type ARTICLES_LIST_TYP is table of ARTICLES_TYP;
PROCEDURE MyProcedure (i_articles_list in ARTICLES_LIST_TYP);
// A custom class mapping to an Oracle user defined type.
// Provided all required implementations by ODP.NET developer guide to represent the Oracle UDT as custom type.
// The custom class must implement IOracleCustomType and INullable interfaces.
// Note: Any Oracle UDT name must be uppercase.
public class Article : IOracleCustomType, INullable
{
// A private member indicating whether this object is null.
private bool ObjectIsNull;
// The OracleObjectMapping attribute is required to map .NET custom type member to Oracle object attribute.
[OracleObjectMapping("ARTICLE_ID")]
public int ArticleId { get; set; }
[OracleObjectMapping("ARTICLE_NAME")]
public string ArticleName { get; set; }
[OracleObjectMapping("UPDATE_DATE")]
public DateTime UpdateDate { get; set; }
// Implementation of interface IOracleCustomType method FromCustomObject.
// Set Oracle object attribute values from .NET custom type object.
public void FromCustomObject(OracleConnection con, object udt)
{
OracleUdt.SetValue(con, udt, "ARTICLE_ID", ArticleId);
OracleUdt.SetValue(con, udt, "ARTICLE_NAME", ArticleName);
OracleUdt.SetValue(con, udt, "UPDATE_DATE", UpdateDate);
}
// Implementation of interface IOracleCustomType method ToCustomObject.
// Set .NET custom type object members from Oracle object attributes.
public void ToCustomObject(OracleConnection con, object udt)
{
ArticleId = (int)OracleUdt.GetValue(con, udt, "ARTICLE_ID");
ArticleName = (string)OracleUdt.GetValue(con, udt, "ARTICLE_NAME");
UpdateDate = (DateTime)OracleUdt.GetValue(con, udt, "UPDATE_DATE");
}
// A property of interface INullable. Indicate whether the custom type object is null.
public bool IsNull
{
get { return ObjectIsNull; }
}
// Static null property is required to return a null UDT.
public static Article Null
{
get
{
Article obj = new Article();
obj.ObjectIsNull = true;
return obj;
}
}
}
// A custom type factory class is required to create an instance of a custom type representing an Oracle object type.
// The custom type factory class must implement IOralceCustomTypeFactory class.
// The OracleCustomTypeMapping attribute is required to indicate the Oracle UDT for this factory class.
[OracleCustomTypeMapping("MYPACKAGE.ARTICLES_TYP")]
public class ArticleFactory : IOracleCustomTypeFactory
{
// Implementation of interface IOracleCustomTypeFactory method CreateObject.
// Return a new .NET custom type object representing an Oracle UDT object.
public IOracleCustomType CreateObject()
{
return new Article();
}
}
// A custom class mapping to an Oracle collection type.
public class ArticleList : IOracleCustomType, INullable
{
// The OracleArrayMapping attribute is required to map .NET class member to Oracle collection type.
[OracleArrayMapping()]
public Article[] objArticles;
// A private member indicating whether this object is null.
private bool ObjectIsNull;
// Implementation of interface IOracleCustomType method FromCustomObject.
// Set Oracle collection value from .NET custom type member with OracleArrayMapping attribute.
public void FromCustomObject(OracleConnection con, object udt)
{
OracleUdt.SetValue(con, udt, 0, objArticles);
}
// Implementation of interface IOracleCustomType method ToCustomObject.
// Set .NET custom type member with OracleArrayMapping attribute from Oracle collection value.
public void ToCustomObject(OracleConnection con, object udt)
{
objArticles = (Article[])OracleUdt.GetValue(con, udt, 0);
}
// A property of interface INullable. Indicate whether the custom type object is null.
public bool IsNull
{
get { return ObjectIsNull; }
}
// Static null property is required to return a null UDT.
public static ArticleList Null
{
get
{
ArticleList obj = new ArticleList();
obj.ObjectIsNull = true;
return obj;
}
}
}
// A custom type factory class is required to crate an instance of a custom type representing an Oracle collection type.
// The custom type factory class must implement IOralceCustomTypeFactory and IOracleArrayTypeFactory class.
// The OracleCustomTypeMapping attribute is required to indicate the Oracle UDT for this factory class.
[OracleCustomTypeMapping("MYPACKAGE.ARTICLES_LIST_TYP")]
public class ArticleListFactory : IOracleCustomTypeFactory, IOracleArrayTypeFactory
{
// Implementation of interface IOracleCustomTypeFactory method CreateObject.
// Return a new .NET custom type object representing an Oracle UDT collection object.
public IOracleCustomType CreateObject()
{
return new ArticleList();
}
// Implementation of interface IOracleArrayTypeFactory method CreateArray to return a new array.
public Array CreateArray(int numElems)
{
return new Article[numElems];
}
// Implementation of interface IOracleArrayTypeFactory method CreateStatusArray to return a new OracleUdtStatus array.
public Array CreateStatusArray(int numElems)
{
return null;
}
}
Execution Code
OracleParameter p = new();
p.ParameterName = "i_articles_list";
p.OracleDbType = OracleDbType.Array;
p.Direction = ParameterDirection.Input;
ArticleList al = new ArticleList();
Article[] articles = new Article[2]
{
new Article() { ArticleId = 2, ArticleName = "article two", UpdateDate = DateTime.Today },
new Article() { ArticleId = 3, ArticleName = "article three", UpdateDate = DateTime.Today }
};
al.objArticles = articles;
p.Value = al;
p.UdtTypeName = "MYPACKAGE.ARTICLES_LIST_TYP";
OracleCommand command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "MYPACKAGE.MyProcedure";
command.Parameters.Add(p);
await command.ExecuteNonQueryAsync();
Does anyone have any idea why It keeps throwing this error?
below is the Enum from which i need to populate object list
public enum Scope
{
[Description("Organization")]
Organization = 100,
[Description("Organization#Unit")]
Organization_Unit = 200,
[Description("Organization#Unit#User")]
Organization_Unit_User = 300
}
I have to create object list from this Enum
Object skeleton will be like below
public class ScopeKVP {
public string key {get;set;}
public int value {get;set;}
}
At the end I need below list of object from enum in which description of each enum should be save in key property of object and value of enum should be saved in value property of object like below
var scopeKvp = new List<ScopeKVP> {
new ScopeKVP {key= 100,value="Organization"},
new ScopeKVP {key= 200,value="Organization#Unit"},
new ScopeKVP {key= 300,value="Organization#Unit#User"}
}
This is what you need to do:
Use reflection to get the information about the enum, its fields
Loop the fields
Take their value with GetValue method
Take the attribute Description's description, which is what you passed to
its constructor
Create the object of type ScopeKVP and add it to
the result list
Code:
Type enumType = typeof(Scope);
FieldInfo[] fields = enumType.GetFields();
List<ScopeKVP> scopeKvp = new List<ScopeKVP>();
foreach (FieldInfo field in fields)
{
if (field.IsLiteral)
{
DescriptionAttribute descAttr = (DescriptionAttribute)Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute));
ScopeKVP item = new ScopeKVP()
{
key = descAttr.Description,
value = (int)field.GetValue(null)
};
scopeKvp.Add(item);
}
}
// scopeKvp is what you need
P.S. like the comments above state, you have some mismatch for key which is string and you expect integer. I guess it is a typo.
C# 5.0 Language Specification 7.6.10.2 Object initializers states that
A member initializer that specifies an object initializer after the
equals sign is a nested object initializer, i.e. an initialization of
an embedded object. Instead of assigning a new value to the field or
property, the assignments in the nested object initializer are treated
as assignments to members of the field or property. Nested object
initializers cannot be applied to properties with a value type, or to
read-only fields with a value type.
While I understand read-only fields cannot be modified by the initializers after the constructor is run, I do not have a clue about the restriction on properties.
The following is a code sample I used to test this restriction on properties:
using System;
namespace ObjectCollectionInitializerExample
{
struct MemberStruct
{
public int field1;
public double field2;
}
class ContainingClass
{
int field;
MemberStruct ms;
public int Field
{
get { return field; }
set { field = value; }
}
public MemberStruct MS
{
get { return ms; }
set { ms = value; }
}
}
class Program
{
static void Main(string[] args)
{
// Nested object initializer applied to a property of value type compiles!
ContainingClass cc = new ContainingClass { Field = 1, MS = new MemberStruct { field1 = 1, field2 = 1.2} };
Console.ReadKey();
}
}
}
I commented on the code where it was supposed to generate a compiler error based on the specification. But it compiles successfully. What am I missing here?
Thanks
What you have is not a nested object initializer because you explicitly create a new instance of MemberStruct. The inner object initializer doesn't directly follow the equals sign, but is an object initializer on its own associated with the call to the MemberStruct constructor.
This is how using a nested object initializer would look like:
ContainingClass cc = new ContainingClass { Field = 1, MS = { field1 = 1, field2 = 1.2} };
This will not compile when MS is a value type (struct), but it will compile when MS is a reference type (object).
I have the following method which sets the value for the given PropertyInfo on the given TInstance. This is to avoid the inefficiency of reflection.
public static Action<TInstance, object> CreateSetter<TInstance>(PropertyInfo propertyInfo, bool includeNonPublic = false)
{
var setMethod = propertyInfo.GetSetMethod(includeNonPublic);
var instance = Expression.Parameter(typeof(TInstance), "instance");
var value = Expression.Parameter(typeof(object), "value");
var valueCast = !propertyInfo.PropertyType.IsValueType
? Expression.TypeAs(value, propertyInfo.PropertyType)
: Expression.Convert(value, propertyInfo.PropertyType);
return Expression.Lambda<Action<TInstance, object>>(
Expression.Call(instance, setMethod, valueCast), instance, value).Compile();
}
So given the following model:
public sealed class PersonClass
{
public string Name {get; set;}
}
I can set the Name using:
var person = new PersonClass();
var nameProp = person.GetType().GetProperties().Where(p => p.Name == "Name").First();
var nameSetter = CreateSetter<PersonClass>(nameProp);
nameSetter(person, "Foo");
This is all good however if I try the method with a struct e.g.:
public struct PersonStruct
{
public string Name {get; set;}
}
The name is always null. I suspect boxing/unboxing is biting me somehow.
In fact if I use FastMember the same behavior exhibits when using:
PersonStruct person = new PersonStruct();
var accessor = TypeAccessor.Create(person.GetType());
accessor[person, "Name"] = "Foo";
However when I box the person as object then FastMember is able to set the value correctly:
object person = new PersonStruct();
var accessor = TypeAccessor.Create(person.GetType());
accessor[person, "Name"] = "Foo";
Any ideas how I can handle this boxing inside the CreateSetter for when TInstance is a value type?
You need an expression that creates a delegate that takes a by-ref argument, so that it will affect the struct passed, rather than a copy. E.g.:
public struct PersonStruct
{
public string Name {get; set;}
}
delegate void FirstByRefAction<T1, T2>(ref T1 arg1, T2 arg2);
void Main()
{
ParameterExpression par1 = Expression.Parameter(typeof(PersonStruct).MakeByRefType());
ParameterExpression par2 = Expression.Parameter(typeof(string));
FirstByRefAction<PersonStruct, string> setter = Expression.Lambda<FirstByRefAction<PersonStruct, string>>(
Expression.Assign(Expression.Property(par1, "Name"), par2),
par1, par2
).Compile();
PersonStruct testStruct = new PersonStruct();
setter(ref testStruct, "Test Name");
Console.Write(testStruct.Name); // outputs "Test Name"
}
This is to avoid the inefficiency of reflection.
Note that the method- and property-calling expression types largely use reflection internally. In the case of interpreted expressions, they do so with each call, in the case of IL-compiled expressions reflection is still used in the compilation step.
As noted in the comments, you really shouldn't create mutable structs. However, to answer the question, structs are value types and therefore a copy of your struct is passed to the Action and therefore the original person value is not changed.
You need a way to pass the struct by reference. However, expressions do not support creating "methods" that take parameters by reference.
What you can do is use the DynamicMethod class to do something like this:
public delegate void StructSetter<TInstance>(ref TInstance instance, object value) where TInstance : struct;
public StructSetter<TInstance> CreateSetter<TInstance>(
PropertyInfo propertyInfo,
bool includeNonPublic = false) where TInstance : struct
{
DynamicMethod method =
new DynamicMethod(
"Set",
typeof(void),
new [] { typeof(TInstance).MakeByRefType(), typeof(object )},
this.GetType());
var generator = method.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod(includeNonPublic));
generator.Emit(OpCodes.Ret);
return (StructSetter<TInstance>)method.CreateDelegate(typeof (StructSetter<TInstance> ));
}
We had to create a StructSetter delegate because the standard Action delegates do not support passing by reference.
Don't forget to cache the delegate or otherwise the cost of compiling is going to slow down your application.
Can one tell me, why the heck does it compile?
namespace ManagedConsoleSketchbook
{
public interface IMyInterface
{
int IntfProp
{
get;
set;
}
}
public class MyClass
{
private IMyInterface field = null;
public IMyInterface Property
{
get
{
return field;
}
}
}
public class Program
{
public static void Method(MyClass #class)
{
Console.WriteLine(#class.Property.IntfProp.ToString());
}
public static void Main(string[] args)
{
// ************
// *** Here ***
// ************
// Assignment to read-only property? wth?
Method(new MyClass { Property = { IntfProp = 5 }});
}
}
}
This is a nested object initializer. It's described in the C# 4 spec like this:
A member initializer that specifies an object initializer after the equals sign is a nested object initializer - that is, an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.
So this code:
MyClass foo = new MyClass { Property = { IntfProp = 5 }};
would be equivalent to:
MyClass tmp = new MyClass();
// Call the *getter* of Property, but the *setter* of IntfProp
tmp.Property.IntfProp = 5;
MyClass foo = tmp;
Because you are using the initializer which uses the setter of ItfProp, not the setter of Property.
So it will throw a NullReferenceException at runtime, since Property will still be null.
Because
int IntfProp {
get;
set;
}
is not readonly.
You did not invoke setter of MyClass.Property, just getter.