Is there a way to rename fields when executing a select statement in SubSonic? I am using the ExecuteTypedList<MyClass> method to fill my List<MyClass> object but the properties of MyClass are not all the same as the column names from the DB table. In SQL I can do select col1 as 'FirstColumn', col2 as 'SecondColumn' from MyTable, is there a way to do something similar in SubSonic?
I believe Alias's are only available for aggregate columns.
You could just add properties of the same names as your columns to your class or a partial and map them to the properties you do use ala calculated field:
public class Songs
{
private string _songTitle;
public string SongTitle {
get { return _songTitle; }
set { _songTitle = value; }
}
public string SongName {
get { return _songTitle; }
set { _songTitle = value; }
}
}
I had the same need the other day, and added the functionality to my local copy of SubSonic. I've just submitted it a patch for it attached to this issue. Applying the patch will let you write a query like
new Select(Table1.IdColumn.AliasAs("table1ID"),
Table2.IdColumn.AliasAs("table2ID"))
.From(Table1.Schema)
.InnerJoin(Table2.Table1IdColumn, Table1.IDColumn);
Related
Disclaimer: I'm new to C# and Acumatica Framework
I'm looking to implement a database slot however I need to pull data from joined tables. I'm using the PXDatabase.SelectMulti method from the snippet below however, I have been unable to get it to work with joins. I also can't seem to find any examples of the method with joined tables.
Is there a way to join tables with this method or perhaps another way to query the data?
public class DatabaseSlotsExample : IPrefetchable
{
protected List<string> values = new List<string>(); // store your values here
public static List<string> Values
{
get
{
//Get the values from the slot dynamically. By providing table name, you inform system when it should reset the slot.
return PXDatabase.GetSlot<DatabaseSlotsExample>("SlotSuperKey", typeof(YourTable)).values;
}
}
public void Prefetch()
{
//read database here
foreach(PXDataRecord rec in PXDatabase.SelectMulti<YourTable>(
new PXDataField<YourTable.tableField>(), //definition for fields that system should select
new PXDataFieldValue<YourTable.tableKey>("Some Condition") //definition for restriction that you need to apply
))
{
//populate your collection from the database here
values.Add(rec.GetString(0));
}
}
}
Yes, if you use BQL.Fluent as a reference, the BQL query is also simplified. See below used on Service Orders:
foreach (PXResult<FSServiceOrder> res in
SelectFrom<FSServiceOrder>.
InnerJoin<FSAppointment>.On<FSAppointment.soRefNbr.IsEqual<FSServiceOrder.refNbr>>.
InnerJoin<FSWFStage>.On<FSWFStage.wFStageID.IsEqual<FSAppointment.wFStageID>>.
InnerJoin<FSRoom>.On<FSRoom.roomID.IsEqual<FSServiceOrder.roomID>>.
InnerJoin<FSEquipment>.On<FSEquipment.registrationNbr.IsEqual<FSRoom.descr>>.
Where<FSServiceOrder.srvOrdType.IsEqual<P.AsString>.
And<FSWFStage.wFStageCD.IsEqual<P.AsString>>>.
View.Select(this, "TO", "SCHEDULED")
{
FSServiceOrder fsServiceOrder = res.GetItem<FSServiceOrder>();
FSAppointment fsAppointment = res.GetItem<FSAppointment>();
}
Then you can use the below to pull the specific table data:
I'm trying to create an 'update-query' for my JdbcPollingChannelAdapter given following workflow :
Select 500 records of type A from database
Update 1 row in another table with the values of the last record read ( the one at position 500 )
But i'm unable to sort it out, as been trying to use spring-el to find the value.
By debugging, i reached JdbcPollingChannelAdapter executeUpdateQuery method,
void executeUpdateQuery(Object obj) {
SqlParameterSource updateParameterSource = this.sqlParameterSourceFactory.createParameterSource(obj);
this.jdbcOperations.update(this.updateSql, updateParameterSource);
}
where Object obj is an ArrayList of 500 records of type A
This is my best match :
UPDATE LAST_EVENT_READ SET SEQUENCE=:#root[499].sequence, EVENT_DATE=:#[499].eventDate
Can anyone help me ?
P.S. Type A has sequence and eventDate attributes
I suggest you to use a custom SqlParameterSourceFactory and already don't rely on the SpEL:
public class CustomSqlParameterSourceFactory implements SqlParameterSourceFactory {
#Override
public SqlParameterSource createParameterSource(Object input) {
List<?> objects = (List<?>) input;
return new BeanPropertySqlParameterSource(objects.get(objects.size() - 1));
}
}
Inject this into the JdbcPollingChannelAdapter.setUpdateSqlParameterSourceFactory() and already use simple properties in the UPDATE statement:
UPDATE LAST_EVENT_READ SET SEQUENCE=:sequence, EVENT_DATE=:eventDate
I have a number of ListBoxFor elements on a form in edit mode. If there was data recorded in the field then the previously selected items are displaying correctly when the form opens. If the field is empty though an error is thrown as the items parameter cannot be null. Is there a way to check in the view and if there is data to use the ListBoxFor with the four parameters but if there isn't to only use three parameters, leaving out the selected items?
This is how I'm declaring the ListBoxFor:
#Html.ListBoxFor(model => model.IfQualityPoor, new MultiSelectList(ViewBag.IfPoor, "Value", "Text", ViewBag.IfQualityPoorSelected), new { #class = "chosen", multiple = "multiple" })
I'm using the ViewBag to pass the ICollection which holds the selected items as the controller then joins or splits the strings for binding to the model field. The MultiSelectLists always prove problematic for me.
Your question isn't entirely clear, but you're making it way harder on yourself than it needs to be using ListBoxFor. All you need for either DropDownListFor or ListBoxFor is an IEnumerable<SelectListItem>. Razor will take care of selecting any appropriate values based on the ModelState.
So, assuming ViewBag.IfPoor is IEnumerable<SelectListItem>, all you need in your view is:
#Html.ListBoxFor(m => m.IfQualityPoor, (IEnumerable<SelectListItem>)ViewBag.IfPoor, new { #class = "chosen" })
The correct options will be marked as selected based on the value of IfQualityPoor on your model, as they should be. Also, it's unnecessary to pass multiple = "multiple" in in your htmlAttributes param, as you get that just by using ListBoxFor rather than DropDownListFor.
It's even better if you use a view model and then add your options as a property. Then, you don't have to worry about casting in the view, which is always a good way to introduce runtime exceptions. For example:
public class FooViewModel
{
...
public IEnumerable<SelectListItem> IfQualityPoorOptions { get; set; }
}
Then, you set this in your action, before returning the view (instead of setting ViewBag). Finally, in your view:
#Html.ListBoxFor(m => m.IfQualityPoor, Model.IfQualityPoorOptions, new { #class = "chosen" })
Much simpler, and you'll never have any issues doing it that way.
UPDATE (based on comment)
The best way to handle flattening a list into a string for database storage is to use a special property for that, and then custom getter and setter to map to/from. For example:
public string IfQualityPoor
{
get { return IfQualityPoorList != null ? String.Join(",", IfQualityPoorList) : null; }
set { IfQualityPoorList = !String.IsNullOrWhiteSpace(value) ? value.Split(',').ToList() : null; }
}
[NotMapped]
public List<string> IfQualityPoorList { get; set; }
Then, you post to/interact with IfQualityPoorList, and the correct string will be set in the database automatically when you save.
I want to create the table with custom name but I cannot find the sample code. I notice the only way to create table is by generic type like db.CreateTable(). May I know if there is a way to create the table name dynamically instead of using Alias? The reason is because sometime we want to store the same object type into different tables like 2015_january_activity, 2015_february_activity.
Apart from this, the db.Insert also very limited to object type. Is there anyway to insert by passing in the table name?
I think these features are very important as it exists in NoSQL solution for long and it's very flexible. Thanks.
OrmLite is primarily a code-first ORM which uses typed POCO's to create and query the schema of matching RDMBS tables. It also supports executing Custom SQL using the Custom SQL API's.
One option to use a different table name is to change the Alias at runtime as seen in this previous answer where you can create custom extension methods to modify the name of the table, e.g:
public static class GenericTableExtensions
{
static object ExecWithAlias<T>(string table, Func<object> fn)
{
var modelDef = typeof(T).GetModelMetadata();
lock (modelDef) {
var hold = modelDef.Alias;
try {
modelDef.Alias = table;
return fn();
}
finally {
modelDef.Alias = hold;
}
}
}
public static void DropAndCreateTable<T>(this IDbConnection db, string table) {
ExecWithAlias<T>(table, () => { db.DropAndCreateTable<T>(); return null; });
}
public static long Insert<T>(this IDbConnection db, string table, T obj, bool selectIdentity = false) {
return (long)ExecWithAlias<T>(table, () => db.Insert(obj, selectIdentity));
}
public static List<T> Select<T>(this IDbConnection db, string table, Func<SqlExpression<T>, SqlExpression<T>> expression) {
return (List<T>)ExecWithAlias<T>(table, () => db.Select(expression));
}
public static int Update<T>(this IDbConnection db, string table, T item, Expression<Func<T, bool>> where) {
return (int)ExecWithAlias<T>(table, () => db.Update(item, where));
}
}
These extension methods provide additional API's that let you change the name of the table used, e.g:
var tableName = "TableA"'
db.DropAndCreateTable<GenericEntity>(tableName);
db.Insert(tableName, new GenericEntity { Id = 1, ColumnA = "A" });
var rows = db.Select<GenericEntity>(tableName, q =>
q.Where(x => x.ColumnA == "A"));
rows.PrintDump();
db.Update(tableName, new GenericEntity { ColumnA = "B" },
where: q => q.ColumnA == "A");
rows = db.Select<GenericEntity>(tableName, q =>
q.Where(x => x.ColumnA == "B"));
rows.PrintDump();
This example is also available in the GenericTableExpressions.cs integration test.
POST EDITED - see edit below
I have a query about the FLuent Automapping which is used as part of the SHarp Architecture. Running one of the tests cases will generate a schema which I can use to create tables in my DB.
I'm developing a site with Posts, and Tags associated with these posts. I want a tag to be able to be associated with more than one post, and for each post to have 0 or more tags.
I wanting to achieve a DB schema of:
Post {Id, Title, SubmitTime, Content}
Tag {Id, Name}
PostTag {PostId, TagId}
Instead, I'm getting:
Post {Id, Title, SubmitTime, Content}
Tag {Id, Name, PostID (FK)}
I'm using sharp architecture, and may classes look as follows (more or less):
public class Post : Entity
{
[DomainSignature]
private DateTime _submittime;
[DomainSignature]
private String _posttitle;
private IList<Tag> _taglist;
private String _content;
public Post() { }
public Post(String postTitle)
{
_submittime = DateTime.Now;
_posttitle = postTitle;
this._taglist = new List<Tag>();
}
public virtual DateTime SubmitTime { get { return _submittime; } private set { _submittime = value; } }
public virtual string PostTitle { get { return _posttitle; } private set { _posttitle = value; } }
public virtual string Content { get { return _content; } set { _content = value; } }
public virtual IList<Tag> TagList { get { return _taglist; } set { _taglist = value; } }
public class Tag : Entity
{
[DomainSignature]
private String _name;
public Tag() { }
public Tag(String name)
{
this._name = name;
}
public virtual String Name
{
get { return _name; }
private set { _name = value; }
}
public virtual void EditTagName(String name)
{
this.Name = name;
}
}
I can see why it's gone for the DB schema set up that it has, as there will be times when an object can only exist as part of another. But a Tag can exist separately.
How would I go about achieving this? I'm quite new to MVC, Nhibernate, and SHarp architecture, etc, so any help would be much appreciated!
EDIT*
OK, I have now adjusted my classes slightly. My issue was that I was expecting the intermediate table to be inferred. Instead, I realise that I have to create it.
So I now have (I've simplified the classes a bit for readability's sake.:
class Post : Entity
{
[DomainSignature]
String Title
[DomainSignature]
DateTime SubmitTime
IList<PostTag> tagList
}
class Tag : Entity
{
[DomainSignature]
string name
}
class PostTag : Entity
{
[DomainSignature]
Post post
[DomainSignature]
Tag tag
}
This gives me the schema for the intermediate entity along with the usual Post and Tag tables:
PostTag{id, name, PostId(FK)}
The problem with the above is that it still does not include The foreign key for Tag. Also, should it really have an ID column, as it is a relational table? I would think that it should really be a composite key consisting of the PK from both Post and Tag tables.
I'm sure that by adding to the Tag class
IList<PostTag> postList
I will get another FK added to the PostTag schema, but I don't want to add the above, as the postList could be huge. I don't need it every time I bring a post into the system. I would have a separate query to calculate that sort of info.
Can anyone help me solve this last part? Thanks for your time.
Ok, I'd been led to believe that modelling the composite class in the domain was the way forward, but I finally come across a bit of automapper override code which creates the composite table without me needing to create the class for it, which was what I was expecting in the first place:
public class PostMappingOverride
: IAutoMappingOverride
{
public void Override(AutoMapping map)
{
map.HasManyToMany(e => e.TagList)
.Inverse()
.Cascade.SaveUpdate();
}
}
This will now give me my schema (following schema non simplified):
create table Posts (
Id INT not null,
PublishTime DATETIME null,
SubmitTime DATETIME null,
PostTitle NVARCHAR(255) null,
Content NVARCHAR(255) null,
primary key (Id)
)
create table Posts_Tags (
PostFk INT not null,
TagFk INT not null
)
create table Tags (
Id INT not null,
Name NVARCHAR(255) null,
primary key (Id)
)
alter table Posts_Tags
add constraint FK864F92C27E2C4FCD
foreign key (TagFk)
references Tags
alter table Posts_Tags
add constraint FK864F92C2EC575AE6
foreign key (PostFk)
references Posts
I think the thrower is that I've been looking for a one-to-many relationship, which it is, but it is called HasManytoMAny here...