SqlTableProfileProvider, custom ProfileBase class, add extra attributes to properties - c#

I am using SqlTableProfileProvider as my profile provider and a custom class called 'ProfileCommon' inheriting from System.Web.Profile.ProfileBase. My ProfileCommon class properties that represent the columns in my profile table each get an attribute of [CustomProviderData("columnName;dbType")]. I am trying to add a new attribute to specific column and then am going to pull that info from the SqlTableProfileProvider class.
The team and I are looking to associate a foreign key (table) with our Profile table. Right now, our Profile table stores, basically, key value pairs, FirstName, LastName, Age, etc; however, we are planning to store bookmarks, links to favorite articles and what not, that will be presented in a list on our dashboard page. We like using the SqlTableProfileProvider and the ProfileCommon object I created. All our asp.net pages inherit from a BasePage and a property called Profile gets the profile common object.
It would be nice to just be able to do:
Profile.Bookmarks.Count; // to know if there are bookmarks
// to also just be able to foreach through them
foreach (Bookmark bk in Profile.Bookmarks) { ... }
Ex:
public class ProfileCommon : System.Web.Profile.ProfileBase
{
public static ProfileCommon GetProfile() { .... }
[CustomProviderData("FirstName;varchar")]
public virtual string FirstName
{
get
{
return ((string)(this.GetPropertyValue("FirstName")));
}
set
{
this.SetPropertyValue("FirstName", value);
}
}
[CustomProviderData("LastName;varchar")]
public virtual string LastName
{
get
{
return ((string)(this.GetPropertyValue("LastName")));
}
set
{
this.SetPropertyValue("LastName", value);
}
}
[CustomProviderData("OtherColumn;int")]
[TableNameData("OtherTable")]
public virtual int OtherColumn
{
get ...
set ...
}
}
// My new attribute
[AttributeUsage(AttributeTargets.Property)]
public class TableNameData : Attribute
{
private string _tableName;
public TableNameData(string tableName)
{
_tableName = tableName;
}
public string TableName
{
get
{
return _tableName;
}
}
}
// Not my implementation, but looking to enhance it.
public class SqlTableProfileProvider : ProfileProvider
{
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyValueCollection svc, string username, SqlConnection conn)
{
...
foreach (SettingsProperty prop in properties)
{
...
// in here, gets CustomProviderData
string persistenceData = prop.Attributes["CustomProviderData"] as string.
// how do i get to mine?
}
}
}
The SqlTableProfileProvider was implemented by Hao Kung. It inherits from ProfileProvider.
One of the methods GetPropertyValues returns a SettingsPropertyValueCollection. There is a private method called GetProfileDataFromTable. In there, I wish to access my custom attribute that I created.
Question: How do I access my attribute that I have specified on my property?
UPDATE: 07162011:1517, 7 days after question asked,
I did find a way to do this. The following is how I did it:
// In the default constructor add the following
public ProfileCommon()
{
// Get all properties for this class 'ProfileCommon'
PropertyInfo[] propertyInfos = typeof(ProfileCommon).GetProperties();
// The ProfileBase, base class, has a property called Properties and
// one can get to all attributes on that property but there are only
// a few attributes that ProfileBase looks for. If the developer wishes
// to use custom attributes on a property, it wont appear in the
// ProfileCommon.Properties.Attributes list.
//
// So, what are we going to do, well, we are going to come up with a hack and solution to this problem
//
foreach (SettingsProperty settingsProperty in ProfileCommon.Properties)
{
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (settingsProperty.Name == propertyInfo.Name)
{
// get all attributes from the associated property, but we are getting it from the propertyInfo variable
// which was retrieved through reflection and will list ALL attributes.
object[] attributes = propertyInfo.GetCustomAttributes(false);
for (int i = 0; i < attributes.Count(); i++)
{
Type type = attributes[i].GetType();
PropertyInfo[] attributeClassProperities = type.GetProperties();
foreach (PropertyInfo attributeClassProperty in attributeClassProperities)
{
// not intested in the TypeId property for the object
if (!attributeClassProperty.Name.Equals("TypeId"))
{
// if the settingsProperty.Attributes does not contain our key value pair, then add it.
if (settingsProperty.Attributes[attributeClassProperty.Name] == null)
{
settingsProperty.Attributes.Add(attributeClassProperty.Name, attributes[i]);
}
}
}
}
}
}
}
}

You only need to take care of implementing your ProfileCommon as you have in the example and make sure the attributes are correct.
For the specialized profile property Bookmark you would also write your own profile class just as you have in the example. There you have to implement a Count property that would query the database for the number of bookmarks of that user.
In the Profile table in the database you would have a Bookmarks column with a suitable FK type such as Guid, Int or BigInt that would be nothing more than a Foreign key to another table you could name [dbo].[Bookmarks] that would actually contain the bookmarks for each user. This Bookmarks table should have a column like UserId that would be a FK to the ASP.NET UserId.

Related

Update method in web API: How to know which fields to update?

I have a typical web API with a couple of PUT/UPDATE endpoints. These endpoints simply call the underlying service, and do the update.
The service layer, has the typical signature such as Object Update(Object object). What I then do is I basically run the following pseudo code:
var dbobject = _db.Object.Find(object.Id);
dbobject.Field1 = object.Field1;
dbobject.Field2 = object.Field2;
// continue for all fields
_db.SaveChanges();
return GetObjectById(object.Id);
However, this provides a challenge for me.
Lets say we have a consumer of our API. This consumer calls my PUT endpoint (/api/Object/{id}), and the payload is the updated Object.
However, lets say that the object we put don't know about example Field4, then this value would be NULL after the update has been run.
My question is:
What do you do about all those fields the payload does NOT contain?
How do you handle not setting values to NULL you don't expect to be
NULL afterwards?
As one of the possible ways, here can be used mix of NotifyPropertyChanged with automapper
The Idea is to store in DTO object which fields exactly was set, and which stays filled with default value. And use collected data in mapping.
For example DTO object will be
public class Dto
{
private List<string> Changed = new List<string>();
public bool IsChanged(string field) => Changed.Contains(field);
private int _age;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// IMPORTANT: field name should fit main object field name
Changed.Add("Name");
}
}
public int Age
{
get { return _age; }
set
{
_age = value;
Changed.Add("Age");
}
}
}
I used Next class for test
public class Human
{
public string Name { get; set; } = "DEFAULT";
public int Age { get; set; } = -1;
}
and automapper configuration will looks like
cfg.CreateMap<Dto, Human>()
.ForAllMembers(s=> s.Condition(d=>d.IsChanged(s.DestinationMember.Name)));
This is a simple example. But it still doesn't prevent to use function IsChanged for some complex/specific logic, use not just a strings but Expressions / MethodInfo, or add custom attributes and use them in automapper configuration (DestinationMember is MethodInfo)
Append
Instead of complex DTO object the information about passed field you can get from Request.Properties in your controller (key ms_querynamevaluepairs value of type Dictionary<string, string>).

How to get names of all required fields in new row with EF?

I'm using EF(db first) and trying to add new row in table using the next code:
var user = new User();
//Some logic to fill the properties
context.Users.AddObject(user);
context.SaveChanges();
Before saving changes on EF i want to verify that all required (not null and with no default value) properties are filled. How can i get all such fields?
I've tried few ways, but can't achieve needed result. The last try was like that:
var resList = new List<PropertyInfo>();
var properties = type.GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance).Where(p => !p.PropertyType.IsGenericType);
foreach (var propertyInfo in properties)
{
var edmScalarProperty =
propertyInfo.CustomAttributes.FirstOrDefault(
x => x.AttributeType == typeof (EdmScalarPropertyAttribute));
var isNullable = true;
if (edmScalarProperty != null)
{
var arg = edmScalarProperty.NamedArguments.FirstOrDefault(x => x.MemberName == "IsNullable");
if (arg != null)
{
isNullable = (bool) arg.TypedValue.Value;
}
}
if (!isNullable)
{
resList.Add(propertyInfo);
}
}
return resList;
Create a constructor with the required fields as parameters.
I always separate my domain objects from my EF objects (DTO objects). The domain object has only one constructor with the required fields. When I want to save these objects I convert them to DTO objects.
Have you looked at all into DataAnnotations for your model classes? Utilizing these (and using a separate object from the one EF creates for you) you can get pretty significant validation built into your models from the model level. Additionally, as L01NL pointed out, you can have your constructor take in parameters that require data.
Lots of information on Model and Validation can be found, one such example is:
http://msdn.microsoft.com/en-us/library/dd410405(v=vs.100).aspx
(look through this main section and its subsections)
using System.ComponentModel.DataAnnotations
public class Foo
{
public Guid Id { get; private set; }
[StringLength(50),Required]
public string FooName { get; private set; }
[Required]
public int Age { get; private set; }
// etc props
public Foo(string fooName, int age)
{
if (string.IsNullOrEmpty(fooName))
throw new ArgumentException("FooName cannot be null or empty"); // note there is also a "minimum length" data annotation to avoid doing something like this, was just using this as an example.
this.Id = Guid.NewGuid();
this.FooName = fooName;
this.Age = age;
}
}
public class YourController
{
[HttpPost]
public ActionResult Add(Foo foo)
{
if (!ModelState.IsValid)
// return - validation warnings, etc
// Add information to persistence
// return successful add?
}
}

How do I add a pre-event to a property get acessor?

Long Description: I'm writing a basic search by filter function from an entity, so I can do something like this:
public Entity GetEntityBy(Entity filter)
{ }
public IList<Entity> GetEntitiesBy(Entity filter)
{ }
The problem is with non nullable types (int, float, etc), and I don't want to "force" all properties to be written as nullables. I want to avoid any kinds of rules (such as applying attributes or implementing my own get/set) so I can write the entity just as usual and simply use this filter function.
The code looks like this:
public class Entity
{
public int EntityID { get; set; }
public string Name { get; set; }
public DateTime RegisterDate { get; set; }
//Other properties
}
public IList<Entity> GetEntitiesBy(Entity filter)
{
if (filter != null)
{
if (filter.EntityID > 0)
{
//Add criteria to filter by ID
//In this case it works because there shouldn't have any IDs with 0
}
//this won't work because DateTime can't be null
//I can't check the default value as well because there are some searchs using the default value and I don't want to ignore that
if (RegisterDate != null)
{
}
}
}
It's supposed to be a simple equal filter depending on the values found in the filter parameter, but as it is now I don't know when I should ignore the default values or not.
I already have a SCRUD manager sort of class, so I want to add a function call to the class that it belongs so I can check when a property has been read/written to.
Short Description: How do I add a function call before a property's get or set acessor is called on a dynamic class? Is this even possible?

Transforming DataTable to List<T> in C#

I've already searched through StackOverflow (and other websites) about transforming a DataTable to List with reflection in C#.
My results until now are pretty good: I can reflect 200k lines in 3.5 seconds (0.5 seconds in hardcoded mode).
But my entities (the classes that represent my data, but I think you already know that) follow this pattern:
My database have columns like this (I don't actually do this, but you'll get the idea):
Table: Clients
Columns:
ClientID, ClientName, ClientPhone, CityID[FK]
I'm using SqlConnection (MySqlConnection), so I have to hardcode my entities and transform the database result in a list of this entity. Like:
Select *, cit.* from Clients cli
Inner join Cities cit on (cit.CityID == cli.CityID)
Inner join Countries cou on (cou.CountryID == cit.CountID)
I don't know if this SQL is correct, but I think you got the idea. This should return some fields like this:
ClientID, ClientName, ClientPhone, CityID, CityName, CountryID, CountryName
Shoud result a List<Client>.
Here's the problem: I have 2 inner joins and I represent this data in my entities like this (I like the expression "like this"):
public class Client
{
public int ClientID { get; set; }
public string ClientName { get; set; }
public string ClientPhone { get; set; }
public City ClientCity { get; set; }
}
public class City
{
public int CityID { get; set; }
public string CityName { get; set; }
public Country CityCountry { get; set; }
}
public class Country
{
public int ContryID { get; set; }
public string CountryName { get; set; }
}
So, if I have a Client object, I would get its country name by the expression client.ClientCity.CityCountry.CountryName. I call it a 3-level property acessor.
And I want to reflect it properly. Here is the main method to transform the DataTable into a List. My native language is Portuguese, but I tried to translate my comments to match my description above.
The idea of this code is: I try to find in the main class the column I have to set. If I don't find it, I search the property in the properties that are objects. Like CityName inside ClientCity inside Client. This code is a mess.
public List<T> ToList<T>(DataTable dt) where T : new()
{
Type type= typeof(T);
ReflectionHelper h = new ReflectionHelper(type);
insertPropInfo(tipo); //a pre-reflection work, I cache some delegates, etc..
List<T> list = new List<T>();
DataTableReader dtr = dt.CreateDataReader();
while (dtr.Read())
{
T obj = new T();
for (int i = 0; i < dtr.FieldCount; i++)
{
GetObject(ref obj, tipo, dtr.GetName(i), dtr.GetValue(i));
}
list.Add(obj);
}
return lista;
}
//ref T obj: the object I create before calling this method
//Type classType: the type of the object (say, Client)
//string colName: this is the Database Column i'm trying to fill. Like ClientID or CityName or CountryName.
//colLineData: the data I want to put in the colName.
public void GetObject<T>(ref T obj, Type classType, string colName, object colLineData) where T : new()
{
//I do some caching to reflect just once, and after the first iteration, I think all the reflection I need is already done.
foreach (PropertyInfo info in _classPropInfos[classType])
{
//If the current PropertyInfo is a valuetype (like int, int64) or string, and so on
if (info.PropertyType.IsValueType || info.PropertyType == typeof(string))
{
//I think string.Equals is a little faster, but i had not much difference using "string" == "string"
if (info.Name.Equals(colName)) //did I found the property?
if (info.PropertyType != typeof(char)) //I have to convert the type if this is a Char. MySql returns char as string.
{
_delegateSetters[info](obj, colLineData); //if it isn't a char, just set it.
}
else
{
_delegateSetters[info](obj, Convert.ChangeType(colLineData, typeof(char)));
}
break;
}
else //BUT, if the property is a class, like ClientCity:
{
//I reflect the City class, if it isn't reflected yet:
if (!_classPropInfos.ContainsKey(info.PropertyType))
{
insertPropInfo(info.PropertyType);
}
//now I search for the property:
Boolean foundProperty = false;
object instance = _delegateGetters[info](obj); //Get the existing instance of ClientCity, so I can fill the CityID and CityName in the same object.
foreach (PropertyInfo subInfo in _classPropInfos[info.PropertyType])
{
if (subInfo.Name.Equals(colName))//did I found the property?
{
if (instance == null)
{
//This will happen if i'm trying to set the first property of the class, like CityID. I have to instanciate it, so in the next iteration it won't be null, and will have it's CityID filled.
instance = _initializers[info.PropertyType]();//A very fast object initializer. I'm worried about the Dictionary lookups, but i have no other idea about how to cache it.
}
_delegateSetters[subInfo](instance, colLineData);//set the data. This method is very fast. Search about lambda getters & setters using System.Linq.Expression.
foundProperty = true;
break;//I break the loops when I find the property, so it wont iterate anymore.
}
}
if (foundProperty)//if I found the property in the code above, I set the instance of ClientCity to the Client object.
{
_delegateSetters[info](obj, instance);
break;
}
}
}
}
There is a problem with this code: I can reach the CityID and CityName, and fill it. But CountryID and CountryName wont. Because this code can do a 2-level reflection, I need some recursive-approach to fill many levels I need. I tried to do this BUT i got so many stack overflows and null reference exceptions I almost gave up.
This code would make it much easier to fetch database rows, Did you already find some library or anything that does what I want? If not, how could I achieve a n-level reflection to make a proper List from a DataTable?
Your problem is really common and practically every ORM in circulation addresses this question.
Of course changing an already written application to take advantage of an ORM is often unpractical, but there are some simple ORM that are really easy to add to an existing application and let you replace incrementally the already written code.
One of these ORMs is DAPPER. It consists of just one source file that you can include directly in the same project with your POCO classes and repository methods (Or just reference the compiled assembly). It is really easy to learn and it is incredibly fast considering the complexity of the work to be carried out. Not to mention that the authors of this little gem are regularly on this site answering questions on their work. Just do a search with the #dapper tag
The only nuisances that I have found to date are the mapping one-to-one from your POCO properties and the field names and also the sometime eluding rules between PK and FK when your keys are not named ID. But that's me that I still haven't fully understood these rules.
Consider to use EntityFramework. It will automate all this work.
This is based on you getting a dataset with the 3 tables and creating the proper DataRelation.
On your particular case(200k lines) i dont know how it will perform but shouldnt be that bad :).
Your calling code could be something like this:
List<Clients> clients = Test.CreateListFromTable<Clients>(ds.Tables["Clients"]);
Remember as i said its based in you fettching the dataset and creating the relations.
Next here is the class with the methods in question(ClientsToCity and CityToCountry are the names of the datarelations,you can place your own):
public class Test
{
// function that set the given object from the given data row
public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
foreach (DataColumn c in row.Table.Columns)
{
PropertyInfo prop = item.GetType().GetProperty(c.ColumnName);
if (prop != null && row[c] != DBNull.Value)
{
prop.SetValue(item, row[c], null);
}
else
{
if (c.ColumnName == "CityID")
{
object obj = Activator.CreateInstance(typeof(City));
SetItemFromRow<City>(obj as City, row.GetChildRows("ClientsToCity")[0]);
PropertyInfo nestedprop = item.GetType().GetProperty("ClientCity");
nestedprop.SetValue(item, obj, null);
}
else if (c.ColumnName == "CountryID")
{
object obj = Activator.CreateInstance(typeof(Country));
SetItemFromRow<Country>(obj as Country, row.GetChildRows("CityToCountry")[0]);
PropertyInfo nestedprop = item.GetType().GetProperty("CityCountry");
nestedprop.SetValue(item, obj, null);
}
}
}
}
// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
T item = new T();
SetItemFromRow(item, row);
return item;
}
// function that creates a list of an object from the given data table
public static List<T> CreateListFromTable<T>(DataTable tbl) where T : new()
{
List<T> lst = new List<T>();
foreach (DataRow r in tbl.Rows)
{
lst.Add(CreateItemFromRow<T>(r));
}
return lst;
}
}

Change custom attribute's parameter at runtime

I need change attribute's parameter during runtime. I simplified my problem to simple example.
Attribute class:
[AttributeUsage(AttributeTargets.Property)]
public class MyAttribute : Attribute
{
public string Name { get; set; }
}
Simple entity which has decorated properties with attributes:
public class MyEntity
{
[MyAttribute(Name="OldValue1")]
public string Data1{ get; set; }
[MyAttribute(Name = "OldValue2")]
public string Data2 { get; set; }
}
I created instance of class MyEntity. I can change value of object's properties, but I can't change value of attribute’s property Name on object entity. Is it possible?
Value of property on object entity I can change with this part of code:
entityProp.SetValue(entity,"NewData",null);
but I don't how change value of attribute's property Name on object entity
This does not work:
attProp.SetValue(attribute,"NewData",null);
Value of property Name is still original.
Here is all test code.
[TestMethod]
public void Test()
{
var entity = new MyEntity
{
Data1 = "OldData",
Data2 = "OldData"
};
PropertyInfo[] entityProps = entity.GetType().GetProperties();
foreach (var entityProp in entityProps)
{
var attribute = Attribute.GetCustomAttribute(entityProp, typeof (MyAttribute)) as MyAttribute;
if (attribute != null)
{
//get attribute's property NAME
PropertyInfo attProp= attribute.GetType().GetProperty("Name");
//get entity property value
var propertyValue = entityProp.GetValue(entity, null);
//get attribute’s property NAME value
var atributeNameValue = attProp.GetValue(entity, null);
TestContext.WriteLine(string.Format("property name:{0} property value: {1} : atribute name value: {2}\n",
entityProp.Name, propertyValue, atributeNameValue));
//change values
entityProp.SetValue(entity,"NewData",null);
//how can I change value of property Name on object entity ?
attProp.SetValue(attribute,"NewData",null);
}
}
TestContext.WriteLine(string.Format("After change\n"));
foreach (var entityProp in entityProps)
{
var attribute = Attribute.GetCustomAttribute(entityProp, typeof(MyAttribute)) as MyAttribute;
if (attribute != null)
{
PropertyInfo attProp = attribute.GetType().GetProperty("Name");
var propertyValue = entityProp.GetValue(entity, null);
var atributeNameValue = attProp.GetValue(entity, null);
TestContext.WriteLine(string.Format("property name:{0} property value: {1} : atribute name value: {2}\n",
entityProp.Name, propertyValue, atributeNameValue));
}
}
}
EDITED: I delete original post and added very simple clear sample. Sorry
You cannot change attributes at runtime. They are embedded into the metadata of the assembly. Your method is changing the internal state of a particular instance; but when you load the attribute again, you are getting a different instance.
This is not possible with reflection, as (as already noted) the metadata is fixed. It is, however, partly possible with TypeDescriptor, which allows adding and replacing of attributes at runtime, and providing complete alternative models (TypeDescriptionProvider, etc). This approach will not be respected by any code that uses reflection, but any code using TypeDescriptor (most typically, data-binding and other UI code) will notice the changes.
Note TypeDescriptor only really works with one of each attribute-type per typ/member; multi-instance attributes are not well supported.

Categories

Resources