I'm getting the compile time exception: "Property or indexer cannot be assigned to -- it is read only" when attempting to set a business object's property using an internal setter. I'm using a data access factory method defined in a separate assemby that derives from the CSLA's ObjectFactory to return a populated business object.
I used the ProjectTrackerWithObjectFactory sample app as a template which uses an internal setter for the Resource business object's Id integer property and the project compiles, but mine won't. Class visability and accessors in my project appear to match those in the sample app, so I'm not sure what the issue is.
My CSLA business object property declaration:
public static PropertyInfo<int> ItemIdProperty = RegisterProperty<int>(p => p.ItemId, "ID", 0);
public int ItemId
{
get { return GetProperty<int>(ItemIdProperty); }
internal set { LoadProperty(ItemIdProperty, value); }
}
My CSLA business objects Fetch factory method:
public static BusinessObjectType GetWidgetItem(int id)
{
return DataPortal.Fetch<BusinessObjectType >(new SingleCriteria<BusinessObjectType , int>(id));
}
My data factory method(defined in a separate assembly):
public BusinessObjectType Fetch(SingleCriteria<BusinessObjectType, int> criteria)
{
var widgetItem= (BusinessObjectType)Activator.CreateInstance(typeof(BusinessObjectType), true);
string sql = "SELECT description, notes FROM widgets w WHERE w.item_id = @id";
using (var conn = new OracleConnection(Database.WidgetsConnection))
{
using (OracleCommand dbCmd = new OracleCommand(sql, conn))
{
dbCmd.Parameters.Add("@id", OracleDbType.Varchar2, 50).Value = criteria.Value;
dbCmd.CommandType = CommandType.Text;
conn.Open();
using (var olReader = new SafeDataReader(dbCmd.ExecuteReader(CommandBehavior.CloseConnection)))
{
if (olReader.Read())
{
using (BypassPropertyChecks(dataFoundryItem))
{
widgetItem.ItemId = criteria.Value;
widgetItem.ItemName = olReader.GetString("data_foundry_desc");
widgetItem.ItemNotes = olReader.GetString("data_foundry_notes");
}
}
olReader.Close();
}
}
}
MarkOld(widgetItem);
return widgetItem;
}
Does anyone have an idea about what I'm doing wrong?
Well, you're saying that Widget.ItemId is marked as internal and you're trying to set it from another assembly, which is basically not valid because the very nature of an internally marked property is that it can only be used by classes within the same assembly.
I have not used a separate data access tier as you appear to be trying to do (versus putting it in the designated places in the CSLA business objects) - so I am definitely not the expert - but I thought the advised approach in this case is to return a data-access object that your CSLA business object will use to populate itself with.
You might want to check out Rocky's Deep Data example.
http://www.lhotka.net/weblog/DNRTVShowOnCSLANETAndDataAccess.aspx
This is correct; internal only grants access to members within the same assembly, unless you also use the InternalsVisibleTo assembly level attribute to open internals up to another, trusted assembly. I would however recommend to steer clear of this, as it seems to cause more problems than it solves. Here's the MSDN page: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx
Instead of using ByPassPropertyChecks u can also use LoadProperty for each IPropertyInfo
LoadProperty(widgetItem, WidgetItem.ItemIdProperty, criteria.Value);
etc..
Lhotka used to make the static PropertyInfo registrations internal but now he recommends to make them public just like u have. Since they are public now u can just call the LoadProperty.
PS: Your factory must be inherited from ObjectFactory.
Oh good point, I didn't see that they were trying for ObjectFactory. In that case making the PropertyInfos public would be the right way to go.
Hi,
First - Id recommend to make the PropertyInfo's public accessible (and not private).
Then you must make a choice on how to hide/prevent write to properties from UI and still be accessible Factory methods.You alternatives are:
1. Consider properties on BO as contract, ie hide properties some from UI and only make them visible to factory methods:
2. Make all properties public(and visible to UI)
3. Create private properties on those you want to hide from UI
In the ProjectTrackerWithObjectFactory I chose the first alternative for the internal timestamp values that I didn't want to expose to the UI while still being able to use the "Named parameters" construct on BO's (and avoid LoadProperty).
Sorry it took so long for me to reply, but thanks very much!
Copyright (c) Marimer LLC