Getting Account name in VaultCustomQuery - corda

I am trying to get some aggregated data from Corda and am using the Accounts module.
My State:
#BelongsToContract(InvoiceContractTk::class)
class SMInvoiceStateTk constructor(val amount: Amount<Currency>,
val issuedDate : LocalDateTime,
val amountPaid: Amount<IssuedTokenType>,
val paidFrom : Party?,
val seller: AnonymousParty,
val buyer: AnonymousParty,
val typeOfGoods : String,
val quantity : Int,
val paidDate : LocalDateTime?,
val nettAmountReceived: Amount<Currency>,
override val linearId: UniqueIdentifier) : LinearState, QueryableState {
/**
* #constructor
*/
constructor(amount: Amount<Currency>,
issuedDate: LocalDateTime,
amountPaid: Amount<IssuedTokenType>,
paidFrom : Party? = amountPaid.token.issuer,
seller: AnonymousParty,
buyer: AnonymousParty,
typeOfGoods: String,
quantity: Int,
paidDate: LocalDateTime?,
linearId: UniqueIdentifier) :
this(amount,
issuedDate,
amountPaid,
paidFrom,
seller,
buyer,
typeOfGoods,
quantity,
Amount(0, amount.token),
paidDate,
Amount(0, amount.token),
linearId
) { }
/**
* Participants , seller, buyer
*/
override val participants: List<AbstractParty> get() = listOf(seller, buyer).map { it }
/**
* Generates a Mapped Object of State
*
* #param schema
*/
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is SMInvoiceTkSchemaV1 -> SMInvoiceTkSchemaV1.PersistantSMTkInvoice(
this.linearId.toString(),
this.amount.toDecimal(),
this.issuedDate,
this.amountPaid.toDecimal(),
this.paidFrom.toString(),
this.seller,
this.buyer,
this.typeOfGoods,
this.quantity,
this.paidDate,
this.nettAmountReceived.toDecimal()
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
/**
* Gets the Supported schemas for this object
*/
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(SMInvoiceTkSchemaV1)
Schema:
/**
* The Database Schema to hold a record of an [SMInvoiceTkSchema]
*/
object SMInvoiceTkSchema
/**
* Defines the fields for an [SMInvoiceTkSchemaV1]
*/
object SMInvoiceTkSchemaV1 : MappedSchema (
schemaFamily = SMInvoiceTkSchema.javaClass,
version = 1,
mappedTypes = listOf(SMInvoiceTkSchemaV1.PersistantSMTkInvoice::class.java)) {
#Entity
#Table(name ="sm_tkinvoice_states")
/**
* Table SM_INVOICE_STATES
*
* #property invoiceId the id of the Invoice
* #property amount the amount of the invoice
* #property issuedDate the date the invoice was issued
* #property amountPaid the amount has been paid
* #property paidFrom the CBDC account from which theinvoice was paid
* #property seller the seller of the goods on the invoice
* #property buyer the buyer of the goods
* #property typeOfGoods the type of goods purchased
* #property quantity the quantity of goods purchased
* #property paidDate the date the invoice was paid
* #property nettAmountReceived the amount received by the seller after paying the VAT
*/
class PersistantSMTkInvoice(
#Column(name="invoice_Id")
var invoiceId: String,
#Column(name="amount")
var amount: BigDecimal,
#Column(name="issuedDate")
var issuedDate: LocalDateTime,
#Column(name="amountPaid")
var amountPaid: BigDecimal,
#Column(name="paidFrom")
var paidFrom: String,
#Column(name="seller")
var seller: AbstractParty?,
#Column(name="buyer")
var buyer: AbstractParty?,
#Column(name="typeOfGoods")
var typeOfGoods: String,
#Column(name="quantity")
var quantity: Int,
#Column(name="paidDate")
var paidDate: LocalDateTime?,
#Column(name="nettAmountreceived")
var nettAmountReceived: BigDecimal
)
: PersistentState() {
/**
* Default constructor required by hibernate.
*/
constructor(): this(
"",
BigDecimal.ZERO,
LocalDateTime.now(),
BigDecimal.ZERO,
"",
null,
null,
"",
0,
null,
BigDecimal.ZERO
)
}
}
This all works fine for issuing an paying an invoice and running some queries. I now want to run 2 other aggregate queries which amount to:
SELECT buyer,typeOfGoods,Round(SUM(amountPaid),2)
From SM_TKINVOICE_STATES
Group by buyer,typeOfGoods
And
select typeOfGoods,seller,round(SUM(nettAmountReceived),2)
from SM_TKINVOICE_STATES
group by typeOfGoods,seller
strong text
Where buyer would be the Account Names e.g. Alice, Jim, Fred and for sellers MegaCorp, MiniCorp etc.
My problem is that I can only seem to aggregate queries against the Persisted schema and for buyer and seller this holds the CordaX500 name, i.e. the node NOT the account. No matter what I put in the uyer and seller fields all that is stored is the node ( I believe this is by design)
How do I get a resut set like:
Alice Adult Clothing 52.60
Alice Books 34.10
Alice Children's Clothing 84.68
Bob Adult Clothing 31.16
Bob Alcohol 41.40
George Energy 101.44
Jim Groceries 52.82
Jim Electrical 46.79
Jim Energy 112.12
Kathy Adult Clothing 40.72
Alice Alcohol 38.45
Alice Children's car seat 40.15
Alice Electrical 41.93
Alice Groceries 56.30
Alice Energy 62.68
Anne Adult Clothing 17.95
Anne Alcohol 10.79
Anne Books 5.68
Anne Children's Clothing 57.98
Anne Children's car seat 31.42
Anne Electrical 60.98
Anne Groceries 64.68
Anne Energy 37.31
Bob Books 27.95
Bob Children's Clothing 41.92
Bob Children's car seat 45.07
Bob Electrical 59.23
...
and
Adult Clothing MegaCompany 234.81
Books MediumCompany 230.45
Children's Clothing MediumCompany 291.78
Alcohol MegaCompany 194.02
Energy ElectricAndGas 573.24
Groceries SmallBusiness 258.37
Electrical SmallBusiness 242.74
Children's car seat MiniCompamy 248.37
Electrical MiniCompamy 208.11
Groceries SoleTrader 317.32
Children's Clothing SoleTrader 186.74
My Flow:
#Suspendable
override fun call(): List<Any> {
val buyerNode = serviceHub.networkMapCache.getNodeByLegalName(CordaX500Name(organisation = "BuyerCWP",locality = "London",country = "GB"))!!.legalIdentities[0]
val sellerNode = serviceHub.networkMapCache.getNodeByLegalName(CordaX500Name(organisation = "SellerCWP",locality = "Glasgow",country = "GB"))!!.legalIdentities[0]
val buyerAccounts = subFlow(AccountsForHost(buyerNode))
val sellerAccountsForHost = subFlow(AccountsForHost(sellerNode))
val buyerIds = buyerAccounts.map {it.state.data.linearId.id}
val criteria = QueryCriteria.VaultQueryCriteria(
status = Vault.StateStatus.UNCONSUMED
)
val sum = builder {SMInvoiceTkSchemaV1.PersistantSMTkInvoice::amountPaid.sum(groupByColumns = listOf(SMInvoiceTkSchemaV1.PersistantSMTkInvoice::buyer ,SMInvoiceTkSchemaV1.PersistantSMTkInvoice::typeOfGoods))}
val sumCriteria = QueryCriteria.VaultCustomQueryCriteria(sum)
val allStates = serviceHub.vaultService.queryBy(
contractStateType = SMInvoiceStateTk::class.java,
criteria = criteria
.and( sumCriteria)
//************** SOMETHING HERE I THINK TO MAP TO ACCOUNT NAMES ************************
)
val otherRes = allStates.otherResults
return otherRes
}
This gives me :
O=BuyerCWP, L=London, C=GB, Adult Clothing, 221.79,
O=BuyerCWP, L=London, C=GB, Alcohol, 213.40,
O=BuyerCWP, L=London, C=GB, Books, 526.20,
O=BuyerCWP, L=London, C=GB, Children's Clothing, 261.40,
O=BuyerCWP, L=London, C=GB, Children's car seat, 547.47,
O=BuyerCWP, L=London, C=GB, Electrical, 541.72,
O=BuyerCWP, L=London, C=GB, Energy, 561.94
...
Obvoiusly O=BuyerCWP, L=London, C=GB should be Alice, Jim, Bob etc.
What do I need to do to resolve this ?

Did you try to use something like this to get the list of one's accounts
val myAccounts = accountService.ourAccounts().map { it.state.data.name }
and then use it in the query criteria
QueryCriteria.VaultQueryCriteria(externalIds = listOf(myAccount.identifier.id))
Try to take a look to the workflow tests from the samples-kotlin/Accounts/worldcupticketbooking , I think you will find good examples of what you need.

Related

sum and group by do not work in android roomdatabase Kotlin

I have a roomdatabase with 4 tables, I want to use GROUP BY and also SUM both in ONE query.
so what I had done till now:
#Query("SELECT *, SUM(increase) FROM transactions GROUP BY user_id")
fun groupBy(): LiveData<List<Transactions>>?
But SUM doesnt work(It shows the first increase by user_id.
For instanse: I have 2 users named MAX and Amir with user id 1 and 2
Amir(userId 1) submit 100$ increase and again 50$ increase.
Max(userId2) submit 80$ increase and again 10$ increase.
Please look at the result:
D/TAG: groupBy: 1 100
D/TAG: groupBy: 2 80
It supposed to show:
D/TAG: groupBy: 1 150
D/TAG: groupBy: 2 90
one point:
I have a field in database that named trans_id which stands for transactionId.
Each time I submit increase or decrease or anything else thet related to user and money my app automaticly genereates one id and it is transactionId each time it autmaticly generate transactionId but It is not as same as last one.
Where I use It?
When I want to get all user's transactions
Function where I read data from(in user list fragment):
private fun groupBy() {
mUserListViewModel.groupBy()?.observe(viewLifecycleOwner, {
mUserListViewModel.group.value = it
it.forEach {
Log.d("TAG", "groupBy: ${it.userId} // ${it.increase}")
}
})
}
My viewModel:
val group = MutableLiveData<List<Transactions>>()
fun groupBy(): LiveData<List<Transactions>>? {
return mTransactionDAO.groupBy()
}
Data class:
data class Transactions(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "trans_id")
var transId: Long = 0L,
#ColumnInfo(name = "user_id", index = true) // <<<<< best to have an index on the column, not required
var userId: Long?,
#ColumnInfo(name = "create_date")
var createDate: String?,
#ColumnInfo(name = "bank_id")
var bankId: Long?,
#ColumnInfo(name = "description")
var description: String?,
#ColumnInfo(name = "increase")
var increase: String?,
#ColumnInfo(name = "decrease")
var decrease: String?,
#ColumnInfo(name = "loan_number")
var loanNumber: String?,
#ColumnInfo(name = "total")
var total: String?,
#ColumnInfo(name = "type")
var type: String?,
#ColumnInfo(name = "loan_id")
var loanId: Long?
)
My database
If you need more code, let me know in comments section
When you use group by clause then column name is required remove * and add specific column name like this.
#Query("SELECT user_id, SUM(increase) FROM transactions GROUP BY user_id")
fun groupBy(): LiveData<List<Transactions>>?
Your code does not read the summed increase but the column increase of your table because you use * which selects all the columns.
The correct way to write the query is:
#Query("SELECT user_id, SUM(increase) AS increase FROM transactions GROUP BY user_id")
fun groupBy(): LiveData<List<userTotals>>?
so that you get 2 columns: the user_id and the the total of increase aliased as increase.
You also need to create a new class for the results of the query:
data class userTotals(val user_id: Long?, val increase: Long?)

Data classe to Room entities

I' ve this data classe
#Parcelize
data class IdTotalTotal(
val idEnvioMarrocos: Int,
val clifor: String,
val nomeClifor: String,
val nomeUser: String,
val termina: Int,
var dados: List<IdTotal>
): Parcelable {
#Parcelize
data class IdTotal(
val produto: String,
val modelo: String,
var idPartePeso: List<IdPartePeso>,
var idOpPedido: List<IdOpPedido>
) : Parcelable {
#Parcelize
data class IdPartePeso(
val parteID: Int,
val nomeParte: String,
var pesoTamanho: List<PesoTamanho>
) : Parcelable {
#Parcelize
data class PesoTamanho(
val nomeT: String,
var pesoT: Int
) : Parcelable
}
#Parcelize
data class IdOpPedido(
val op: String,
var pedidos: List<Pedido>
) : Parcelable {
#Parcelize
data class Pedido(
val pedido: String,
val pedidoCliente: String,
var pedidoEntra: Boolean
) : Parcelable
}
}
}
Now I'm trying to achieve the same result with a room entity for persistence and ease.
This is my code:
#Entity
data class EnviosPesosDestinosBd(
#PrimaryKey(autoGenerate = false)
val idEnvioMarrocos: Int,
val clifor: String,
val nomeClifor: String,
val nomeUser: String,
val termina: Int,
val updatedAt: Long = System.currentTimeMillis()
)
#Entity(primaryKeys = ["idEnvioMarrocos", "produto"])
data class EnvioProduto(
val idEnvioMarrocos: Int,
val produto: String,
val modelo: String,
)
#Entity(primaryKeys = ["idEnvioMarrocos", "produto", "parteID"],
foreignKeys = [
ForeignKey(
entity = TamanhoPeso::class,
parentColumns = ["idEnvioMarrocos", "produto", "parteID"],
childColumns = ["idEnvioMarrocos", "produto", "parteID"]
)
]
)
data class PesoParte(
val idEnvioMarrocos: Int,
val produto: String,
val parteID: Int,
val nomeParte: String,
)
#Entity(primaryKeys = ["idEnvioMarrocos", "produto", "parteID", "nomeT"])
data class TamanhoPeso(
val idEnvioMarrocos: Int,
val produto: String,
val parteID: Int,
val nomeT: String,
var pesoT: Int
)
#Entity(primaryKeys = ["idEnvioMarrocos", "op", "produto"],
foreignKeys = [
ForeignKey(
entity = OpPedido::class,
parentColumns = ["idEnvioMarrocos", "op"],
childColumns = ["idEnvioMarrocos", "op"]
)
])
data class EnvioDestinoComOp(
val idEnvioMarrocos: Int,
val produto: String,
val op: String,
)
#Entity(primaryKeys = ["idEnvioMarrocos", "op", "pedido"])
data class OpPedido(
val idEnvioMarrocos: Int,
val op: String,
val pedido: String,
val pedidoCliente: String,
var pedidoEntra: Boolean
)
Thanks
It would appear that you are stuck with how to create appropriate Entities to represent your original classes and trying to make complex primary keys to create the relationships between the tables.
I would suggest utilising unique id's (single columns) as the basis for the relationships.
The following code, based upon a hierarchical inspection of your original classes and using the original class names as the basis for the Database Table/Entity names is a quick interpretation of what you may wish to consider as the basis.
So the topmost class directly or indirectly referenced is the IdTotalTotal class, and it appears that idEnvioMarrocos would be a unique value.
This what was coded (basically little different to what you coded) :-
#Entity
data class DBIdTotalTotal(
#PrimaryKey
val idEnvioMarrocos: Long,
val clifor: String,
val nomeClifor: String,
val nomeUser: String,
val termina: Int,
val updatedAt: Long = System.currentTimeMillis()
)
Long rather Int has been used as I prefer to use Long as at least for Java the value can be Long rather than int.
No need for autoGenerate = false as just #PrimaryKey will default to false.
Note that I have prefixed the original class name with DB to indicate that it's a DB related class (and I've used this throughout).
Now according to your original classes the IdTotalToal class has sub classes (aka a relationship) with a list of IdTotal's. So DBIdTotal looks like :-
#Entity(
foreignKeys = [
ForeignKey(
entity = DBIdTotalTotal::class,
parentColumns = ["idEnvioMarrocos"],
childColumns = ["ref_DBIdTotalTotal"]
)
]
)
data class DBIdTotal(
#PrimaryKey
val id_DBIdTotal: Long,
val ref_DBIdTotalTotal: Long,
val produto: String,
val modelo: String
)
i.e. A DBIdTotal is a child of a DBIdTotalTotal and the parent is stored in the ref_DBIdTotalTotal column that has been added.
you will see a pattern where ref_ prefixes the new column added to reference the parent column.
note (I have not included indexes on the Foreign keys Room will warn you to do this)
As the IdTotal class has two lists then it is a parent to the IdPartPeso(s) and the IdOpPedido(s) these two DB classes are as follows:-
#Entity(
foreignKeys = [
ForeignKey(
entity = DBIdTotal::class,
parentColumns = ["id_DBIdTotal"],
childColumns = ["ref_DBIdTotal"]
)
]
)
data class DBIdPartePeso(
#PrimaryKey
val id_DBIdPartePeso: Long,
val ref_DBIdTotal: Long,
val parteID: Int,
val nomeParte: String
)
and
#Entity(
foreignKeys = [
ForeignKey(entity = DBIdTotal::class,
parentColumns = ["id_DBIdTotal"],
childColumns = ["ref_DBIdTotal"]
)
]
)
data class DBIdOpPedido(
#PrimaryKey
val id_DBIdOpPedido: Long,
val ref_DBIdTotal: Long,
val op: String
)
IdPartePeso has a list of PesoTamanho's so DBPesoTamanho is :-
#Entity(
foreignKeys = [
ForeignKey(
entity = DBIdPartePeso::class,
parentColumns = ["id_DBIdPartePeso"],
childColumns = ["ref_DBIdPartePeso"]
)
]
)
data class DBPesoTamanho(
#PrimaryKey
val id_DBPesoTamanho: Long,
val ref_DBIdPartePeso: Long,
val nomeT: String,
var pesoT: Int
)
Likewise IdOpPedido has a list of Pedido's SO DBPedido is :-
#Entity(
foreignKeys = [
ForeignKey(
entity = DBIdOpPedido::class,
parentColumns = ["id_DBIdOpPedido"],
childColumns = ["ref_DBIdOpPedido"]
)
]
)
data class DBPedido(
#PrimaryKey
val id_DBPedido: Long,
val ref_DBIdOpPedido: Long,
val pedido: String,
val pedidoCliente: String,
var pedidoEntra: Boolean
)
That deals with the tables. However, it is very likely that you want to get a DBIdTotalTotal with all the underlying objects (The DBIdTotal's .... the DBPedido's and DBPesoTamanho's).
So we work back up the hierachy this time creating POJO's for combining children with the parent into a List of the children.
So for a DBIdOpPedido we want the DBIdOpPedido object with a List so the following POJO would suit:-
data class PojoDBIdOpPedidoWithDbPedido (
#Embedded
val dbIdOpPedido: DBIdOpPedido,
#Relation(
entity = DBPedido::class,
parentColumn = "id_DBIdOpPedido",
entityColumn = "ref_DBIdOpPedido"
)
val dbPedidoList: List<DBPedido>
)
The parent is Embedded, the children use and #Relation
Similar for DBPartePpeso with the List :-
data class PojoDBIdPartePesoWithDBPesoTamanho(
#Embedded
val dbIdPartePeso: DBIdPartePeso,
#Relation(
entity = DBPesoTamanho::class,
parentColumn = "id_DBIdPartePeso",
entityColumn = "ref_DBIdPartePeso"
)
val dbPesoTamanhoList: List<DBPesoTamanho>
)
Next up the Hierarchy is the DBIdTotal, this has 2 lists which sublists. However, it's not complicated because the previously created POJO's can be utilised. As such there is:-
data class PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido(
#Embedded
val dbIdTotal: DBIdTotal,
#Relation(
entity = DBIdPartePeso::class,
parentColumn = "id_DBIdTotal",
entityColumn = "ref_DBIdTotal"
)
val pojoDBIdPartePesoWithDBPesoTamanhoList: List<PojoDBIdPartePesoWithDBPesoTamanho>,
#Relation(
entity = DBIdOpPedido::class,
parentColumn = "id_DBIdTotal",
entityColumn = "ref_DBIdTotal"
)
val pojoDBIdOpPedidoList: List<PojoDBIdOpPedidoWithDbPedido>
)
NOTE the entities referred to are the entities NOT the POJO's as it is the columns of the tables that form the relationships.
So last but not least the top level DBIdTotalTotal aka everything all together (as such a bit of a different name for this) :-
data class PojoDBIdTotalTotalWithDBIdTotalEtcetera(
#Embedded
val dbIdTotalTotal: DBIdTotalTotal,
#Relation(
entity = DBIdTotal::class,
parentColumn = "idEnvioMarrocos",
entityColumn = "ref_DBIdTotalTotal"
)
val pojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedidoList: List<PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido>
)
The above code can be used with appropriate Dao's nothing that Room builds the underlying queries to obtain ALL relations when using #Relation. This makes the queries very simple as you only have to specfify the higher level table. The following are suitable Dao's (in a single class named DBDao) as per :-
#Dao
interface DBDao {
#Insert
fun insertDBIdTotalTotal(dbIdTotalTotal: DBIdTotalTotal): Long
#Insert
fun insertDBIdTotal(dbIdTotal: DBIdTotal): Long
#Insert
fun insertDBIdPartePeso(dbIdPartePeso: DBIdPartePeso): Long
#Insert
fun insertDBPesoTamanho(dbPesoTamanho: DBPesoTamanho): Long
#Insert
fun insertDBIdOpPedido(dbIdOpPedido: DBIdOpPedido): Long
#Insert
fun insertDBPedido(dbPedido: DBPedido): Long
#Transaction
#Query("SELECT * FROM dbidoppedido")
fun getIdOpPedidoWithAllPedidos(): List<PojoDBIdOpPedidoWithDbPedido>
#Transaction
#Query("SELECT * FROM dbidpartepeso")
fun getIdPartePesoWithAllPesoTamanhod(): List<PojoDBIdPartePesoWithDBPesoTamanho>
#Transaction
#Query("SELECT * FROM dbidtotal")
fun getIdTotalWithAllIdPartePesosAndAllIdOpPedidos(): List<PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido>
#Transaction
#Query("SELECT * FROM dbidtotaltotal")
fun getIdTotalTotalWithEverything(): List<PojoDBIdTotalTotalWithDBIdTotalEtcetera>
}
not that the queries that have an underlying #Relation all have #Transaction (Room will issue warnings if not (best to use #Transaction as child objects are built by using additional queries)).
Testing/Demo/Example
An #Database named *TheDatabase was created :-
#Database(entities = [
DBIdTotalTotal::class,
DBIdTotal::class,
DBIdOpPedido::class,
DBIdPartePeso::class,
DBPesoTamanho::class,
DBPedido::class
],
version = 1
)
abstract class TheDatabase: RoomDatabase() {
abstract fun getDao(): DBDao
}
The the following was then used for testing/demonstrating the code above (note allowMainThreadQueries used for brevity and convenience) :-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: DBDao
val TAG = "MYDBINFO"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = Room.databaseBuilder(this,TheDatabase::class.java,"mydb.db")
.allowMainThreadQueries().build()
dao = db.getDao()
var firstTotalTotalId = dao.insertDBIdTotalTotal(DBIdTotalTotal(1L,"CLIFOR1","CLIFORNAME1","USERNAME1",10))
var firstTotal = dao.insertDBIdTotal(DBIdTotal(2L,firstTotalTotalId,"PRODUTO1","MODELO1"))
var secondTotal = dao.insertDBIdTotal(DBIdTotal(3L,firstTotalTotalId,"PRODUTO2","MODELO2"))
var firstPartePeso = dao.insertDBIdPartePeso(DBIdPartePeso(4L,firstTotal,100,"Parte100"))
var secondPartePeso = dao.insertDBIdPartePeso(DBIdPartePeso(5L,secondTotal,200,"PARTE200"))
var thirdPartePeso = dao.insertDBIdPartePeso( DBIdPartePeso(6,firstTotal,300,"PARTE300"))
var fourthPartePes = dao.insertDBIdPartePeso(DBIdPartePeso(7,secondTotal,400,"PARTE400"))
var firstPesoTamanho = dao.insertDBPesoTamanho(DBPesoTamanho(8,firstPartePeso,"PESONTAMANHO1000",1000))
var secondPesoTamanho = dao.insertDBPesoTamanho(DBPesoTamanho(9,thirdPartePeso,"PARTEPESO2000",2000))
var firstIdOpPedido = dao.insertDBIdOpPedido(DBIdOpPedido(10,firstTotal,"OPPEDIDO10000"))
var sedcondIdOpPedido = dao.insertDBIdOpPedido(DBIdOpPedido(11,firstTotal,"OPPEDIDO2000"))
var firstPedido = dao.insertDBPedido(DBPedido(12,firstIdOpPedido,"PEDIDO100000","CLIENTE1",true))
var getall = dao.getIdTotalTotalWithEverything()
for(all: PojoDBIdTotalTotalWithDBIdTotalEtcetera in getall) {
logIdTotalTotal(all.dbIdTotalTotal,"")
for(allTotals: PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido in all.pojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedidoList) {
logIdTotal(allTotals.dbIdTotal,"\t")
for(allIdPartePeso: PojoDBIdPartePesoWithDBPesoTamanho in allTotals.pojoDBIdPartePesoWithDBPesoTamanhoList) {
logIdPartePeso(allIdPartePeso.dbIdPartePeso,"\t\t")
for(pesoTamanho: DBPesoTamanho in allIdPartePeso.dbPesoTamanhoList) {
logPesoTamanho(pesoTamanho,"\t\t\t")
}
}
for(allIdOpPedido: PojoDBIdOpPedidoWithDbPedido in allTotals.pojoDBIdOpPedidoList) {
logIdOpPedido(allIdOpPedido.dbIdOpPedido,"\t\t")
for(pedido: DBPedido in allIdOpPedido.dbPedidoList) {
logPedido(pedido,"\t\t\t")
}
}
}
}
}
private fun logPedido(p: DBPedido,prefix: String) {
Log.d(TAG,"$prefix PEDIDIO = ${p.pedido} CLIENTE = ${p.pedidoCliente} ID=${p.id_DBPedido} ENTRA=${p.pedidoEntra} REFERENCES IDOPPedido=${p.ref_DBIdOpPedido}")
}
private fun logIdOpPedido(iop: DBIdOpPedido, prefix: String) {
Log.d(TAG,"$prefix OP=${iop.op} ID=${iop.id_DBIdOpPedido} REFERNCES TOTAL=${iop.ref_DBIdTotal}" )
}
private fun logPesoTamanho(pt: DBPesoTamanho, prefix: String) {
Log.d(TAG,"$prefix NOME=${pt.nomeT} PESO=${pt.pesoT} ID=${pt.id_DBPesoTamanho} REFRENCES IDPARTEPESO=${pt.ref_DBIdPartePeso}")
}
private fun logIdPartePeso(ipp: DBIdPartePeso, prefix: String) {
Log.d(TAG,"$prefix NOMEPARTE=${ipp.nomeParte} PARTEID=${ipp.parteID} ID=${ipp.id_DBIdPartePeso} REFERNCES TOTAL=${ipp.ref_DBIdTotal}")
}
private fun logIdTotal(it: DBIdTotal, prefix: String) {
Log.d(TAG,"$prefix MODELO=${it.modelo} PRODUTO=${it.produto} ID=${it.id_DBIdTotal} REFERENCES TOTALTOTAL= ${it.ref_DBIdTotalTotal}")
}
private fun logIdTotalTotal(itt: DBIdTotalTotal, prefix: String) {
Log.d(TAG,"$prefix NOMUSER=${itt.nomeUser} CLIFOR=${itt.clifor} NOMCLIFOR=${itt.nomeClifor} TERMINA=${itt.termina} UPDATED= ${itt.updatedAt} ID=${itt.idEnvioMarrocos}")
}
}
The above has been designed to run and work just the once purely for testing/demonstrating.
Result
D/MYDBINFO: NOMUSER=USERNAME1 CLIFOR=CLIFOR1 NOMCLIFOR=CLIFORNAME1 TERMINA=10 UPDATED= 1620189450079 ID=1
D/MYDBINFO: MODELO=MODELO1 PRODUTO=PRODUTO1 ID=2 REFERENCES TOTALTOTAL= 1
D/MYDBINFO: NOMEPARTE=Parte100 PARTEID=100 ID=4 REFERNCES TOTAL=2
D/MYDBINFO: NOME=PESONTAMANHO1000 PESO=1000 ID=8 REFRENCES IDPARTEPESO=4
D/MYDBINFO: NOMEPARTE=PARTE300 PARTEID=300 ID=6 REFERNCES TOTAL=2
D/MYDBINFO: NOME=PARTEPESO2000 PESO=2000 ID=9 REFRENCES IDPARTEPESO=6
D/MYDBINFO: OP=OPPEDIDO10000 ID=10 REFERNCES TOTAL=2
D/MYDBINFO: PEDIDIO = PEDIDO100000 CLIENTE = CLIENTE1 ID=12 ENTRA=true REFERENCES IDOPPedido=10
D/MYDBINFO: OP=OPPEDIDO2000 ID=11 REFERNCES TOTAL=2
D/MYDBINFO: MODELO=MODELO2 PRODUTO=PRODUTO2 ID=3 REFERENCES TOTALTOTAL= 1
D/MYDBINFO: NOMEPARTE=PARTE200 PARTEID=200 ID=5 REFERNCES TOTAL=3
D/MYDBINFO: NOMEPARTE=PARTE400 PARTEID=400 ID=7 REFERNCES TOTAL=3
That is the Single IdTotalTotal is extracted with the underlying related objects
The above is intended to show the principle, there may be some issues as the output hasn't been rigorously checked that it conforms to expectations.

How to create an app where user can make his own tables on sqlite and kotlin [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
Well... I don't know where to start with this... I'm doing a course where the teacher has not taught us anything about databases and now he want us to do an app with kotlin and sqlite where we make an Activity with a button "New table" where user can create a database table with a name and dynamic fields. I've been searching all day about it and I got nothing.
I thought about create a CRUD but I'm with the trouble that I don't know how to make any of this dynamically.
For now I have this SQLiteHelper that I saw it was necessary to make and I put const values to test it because I don't have any clue how to pass the values from the user view.
class AdminSQLiteOpenHelper(context: Context, name: String, factory: CursorFactory?, version: Int) : SQLiteOpenHelper(context, name, factory, version) {
companion object{
const val DATABASE_NAME = "test.db"
var TABLE_NAME = "Testing_Table"
const val COL_1 = "ID"
var COL_2 = "NAME"
var COL_3 = "SURNAME"
var COL_4 = "YEARS"
}
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("create table TABLE_NAME(COL_1 primary key autoincrement, COL_2 text, COL_3 text, COL_4 int)")
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("drop table if exists TABLE_NAME")
onCreate(db)
}
}
And I have this activity
class MainActivity2Crear : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_activity2_crear)
val buttonCrear = findViewById<Button>(R.id.buttonCrear)
buttonCrear.setOnClickListener{
val register = ContentValues()
register.put(COL_1, textView.getText().toString())
TABLE_NAME = register.toString()
val admin = AdminSQLiteOpenHelper(this, TABLE_NAME, null, 1)
val bd = admin.writableDatabase
bd.insert(TABLE_NAME, null, register)
bd.close()
textView.setText("")
Toast.makeText(this, "Table created", Toast.LENGTH_SHORT).show()
val intent = Intent(this, MainActivity2::class.java)
startActivity(intent)
}
val button = findViewById<Button>(R.id.button)
button.setOnClickListener{
val intent = Intent(this, MainActivity2::class.java)
startActivity(intent)
}
}
}
I just did create button for now because I don't know what I'm doing...
I know probably this question will have downvotes but I just want some help or a tutorial or something that help me to understand how to make this exercise...
You first issue that you will encounter is that you are trying to create a table using CREATE TABLE TABLE_NAME (COL_1 PRIMARY KEY AUTOINCREMENT .....
The table that will be created (attempted) will be TABLE_NAME not Testing_Table as the variable name is embedded within the String rather than being resolved and appended to the string.
There are multiple inclusions of variables in Strings.
Another error is that you have PRIMARY KEY AUTOINCREMENT autoincrement can only be used for an alias of the rowid which must be defined using specifically INTEGER PRIMARY KEY.
You don't need AUTOINCREMENT as INTEGER PRIMARY KEY will do what you want (increment the value of the ID column so the first will be 1, then likely 2, then likely 3 ....). AUTOINCREMENT is basically a constraint/rule that says the number MUST be higher (for your testing it will be (not that it really matters)).
I'd suggest the following that is based upon your code (but without the click handling) that successfully creates the table, inserts some rows (not tables) in the table and then extracts them and writes the extracted data to the log.
First the modified AdminSQLiteOpenHelper :-
class AdminSQLiteOpenHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
companion object{
const val DATABASE_NAME = "test.db"
const val DATABASE_VERSION = 1;
var TABLE_NAME = "Testing_Table"
const val COL_1 = "ID"
var COL_2 = "NAME"
var COL_3 = "SURNAME"
var COL_4 = "YEARS"
}
val db = this.writableDatabase //<<<<<<<<<< ADDED
override fun onCreate(db: SQLiteDatabase) {
//<<<<<<<<<< NUMEROUS CHANGES >>>>>>>>>>
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME +
"("
+ COL_1 + " INTEGER PRIMARY KEY," // no need for AUTOINCREMENT you want INTEGER PRIMARY KEY not PRIMARY KEY
+ COL_2 + " TEXT,"
+ COL_3 + " TEXT,"
+ COL_4 + " INTEGER" +
")"
)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("drop table if exists " + TABLE_NAME) //<<<<<<<<<<< CHANGED
onCreate(db)
}
//<<<<<<<<<< ADDED >>>>>>>>>
fun insertRow(name: String, surname: String, years: Int): Long {
val cv = ContentValues()
cv.put(COL_2,name)
cv.put(COL_3,surname)
cv.put(COL_4,years)
return db.insert(TABLE_NAME,null,cv);
}
//<<<<<<<<<< ADDED >>>>>>>>>
fun getAllRowsFromTestingTable(): Cursor {
return db.query(TABLE_NAME,null, null,null,null,null, COL_3)
}
}
please note the changes made in comparison to yours.
Now an Activity (MainActivity) that does the work of adding and extracting data from the database:-
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Get an instance of the DB Helper with full class scope
val db = AdminSQLiteOpenHelper(this)
// Add Some data
db.insertRow("Fred","Bloggs",10)
db.insertRow("Jane","Doe",25)
// Get all the data as a Cursor
val cursor = db.getAllRowsFromTestingTable()
// traverse the cursor writing data to the log
while(cursor.moveToNext()) {
Log.d("MYDATA",
"ID = " + cursor.getLong(cursor.getColumnIndex(AdminSQLiteOpenHelper.COL_1)) +
" First Name = " + cursor.getString(cursor.getColumnIndex(AdminSQLiteOpenHelper.COL_2)) +
" Surname = " + cursor.getString(cursor.getColumnIndex(AdminSQLiteOpenHelper.COL_3)) +
" Years = " + cursor.getInt(cursor.getColumnIndex(AdminSQLiteOpenHelper.COL_4))
)
}
cursor.close() //<<<<<<<<<< should ALWAYS close cursor when done
}
}
When run it produces the following in the LOG :-
2021-04-09 17:03:01.531 D/MYDATA: ID = 1 First Name = Fred Surname = Bloggs Years = 10
2021-04-09 17:03:01.531 D/MYDATA: ID = 2 First Name = Jane Surname = Doe Years = 25
Additional
Here's a revised MainActivity that has a button and will add a row to the table when it is clicked.
The rows are all the same with the exception of the years value, it being randomly generated.
After inserting the contents of the database are written to the log.
No changes to AdminSQLiteOpenHelper
:-
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Get an instance of the DB Helper with full class scope
val db = AdminSQLiteOpenHelper(this)
val buttonCrear = this.findViewById<Button>(R.id.buttonCrear)
buttonCrear.setOnClickListener{
db.insertRow("Button","Click", Random.nextInt(10,9999))
logData(db)
}
// Add Some data
db.insertRow("Fred","Bloggs",10)
db.insertRow("Jane","Doe",25)
// Get all the data as a Cursor
logData(db)
}
// Function replaces the previous logging of the data
fun logData(db: AdminSQLiteOpenHelper) {
val cursor = db.getAllRowsFromTestingTable()
while(cursor.moveToNext()) {
Log.d("MYDATA",
"ID = " + cursor.getLong(cursor.getColumnIndex(AdminSQLiteOpenHelper.COL_1)) +
" First Name = " + cursor.getString(cursor.getColumnIndex(AdminSQLiteOpenHelper.COL_2)) +
" Surname = " + cursor.getString(cursor.getColumnIndex(AdminSQLiteOpenHelper.COL_3)) +
" Years = " + cursor.getInt(cursor.getColumnIndex(AdminSQLiteOpenHelper.COL_4))
)
}
cursor.close() //<<<<<<<<<< should ALWAYS close cursor when done
}
}
Here's an example of the Log after clicking the button a few times :-
Before any Clicks
2021-04-09 18:45:47.813 D/MYDATA: ID = 1 First Name = Fred Surname = Bloggs Years = 10
2021-04-09 18:45:47.813 D/MYDATA: ID = 2 First Name = Jane Surname = Doe Years = 25
After 1st Click
2021-04-09 18:45:58.567 D/MYDATA: ID = 1 First Name = Fred Surname = Bloggs Years = 10
2021-04-09 18:45:58.567 D/MYDATA: ID = 3 First Name = Button Surname = Click Years = 9910
2021-04-09 18:45:58.567 D/MYDATA: ID = 2 First Name = Jane Surname = Doe Years = 25
After 2nd Click
2021-04-09 18:45:59.675 D/MYDATA: ID = 1 First Name = Fred Surname = Bloggs Years = 10
2021-04-09 18:45:59.675 D/MYDATA: ID = 3 First Name = Button Surname = Click Years = 9910
2021-04-09 18:45:59.675 D/MYDATA: ID = 4 First Name = Button Surname = Click Years = 8263
2021-04-09 18:45:59.676 D/MYDATA: ID = 2 First Name = Jane Surname = Doe Years = 25
After 3rd Click
2021-04-09 18:46:00.611 D/MYDATA: ID = 1 First Name = Fred Surname = Bloggs Years = 10
2021-04-09 18:46:00.611 D/MYDATA: ID = 3 First Name = Button Surname = Click Years = 9910
2021-04-09 18:46:00.611 D/MYDATA: ID = 4 First Name = Button Surname = Click Years = 8263
2021-04-09 18:46:00.612 D/MYDATA: ID = 5 First Name = Button Surname = Click Years = 9625
2021-04-09 18:46:00.613 D/MYDATA: ID = 2 First Name = Jane Surname = Doe Years = 25

Persistent Schema mapping for a collection with custom data type

I have the following situation .
I have a linear state as given below .
class INDENTState(
val indentId:String,
val itemType: String,
val model: String,
val quantity: Int,
val specifications:String,
val product : String,
val comment:String,
val branchName:String,
val branchAddress:String,
val state:String,
override val linearId: UniqueIdentifier = UniqueIdentifier(indentId)):
LinearState, QueryableState {
I would like to include the above linear state in another linearState and insert several of the INDENTState into a list and create another linear state .
The second linear state I would like to be persistent .
object CollatedIndentsSchemaV1 : MappedSchema(
schemaFamily = CollatedIndentsSchema.javaClass,
version = 1,
mappedTypes = listOf(PersistanceCIs::class.java)) {
#Entity
#Table(name = "collated_Indents")
class PersistanceCIs(
#Column(name = "ciNo")
var ciNo: String,
#ElementCollection
var borrowerName: Set<INDENTState>,
#Column(name = "party")
var party: String
) : PersistentState() {
// Default constructor required by hibernate.
constructor(): this("", setOf(), "")
}
}
But I am getting the following hibernate exception .
E 23:39:14+0530 [main] internal.Node.run - Exception during node startup {}
org.hibernate.MappingException: Could not determine type for: com.example.state.INDENTState, at table: CollatedIndentsSchemaV1$PersistanceCIs_borrowerName, for columns: [org.hibernate.mapping.Column(borrowerName)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:455) ~[hibernate-core-5.2.6.Final.jar:5.2.6.Final]
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:422) ~[hibernate-core-5.2.6.Final.jar:5.2.6.Final]
at org.hibernate.mapping.Collection.validate(Collection.java:310) ~[hibernate-core-5.2.6.Final.jar:5.2.6.Final]
at org.hibernate.mapping.Set.validate(Set.java:27) ~[hibernate-core-5.2.6.Final.jar:5.2.6.Final]
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:333) ~[hibernate-core-5.2.6.Final.jar:5.2.6.Final]
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444) ~[hibernate-core-5.2.6.Final.jar:5.2.6.Final]
at net.corda.nodeapi.internal.persistence.HibernateConfiguration.buildSessionFactory(HibernateConfiguration.kt:113) ~[corda-node-api-corda-3.0.jar:?]
at net.corda.nodeapi.internal.persistence.HibernateConfiguration.makeSessionFactoryForSchemas(HibernateConfiguration.kt:63) ~[corda-node-api-corda-3.0.jar:?]
at net.corda.nodeapi.internal.persistence.HibernateConfiguration.access$makeSessionFactoryForSchemas(HibernateConfiguration.kt:26) ~[corda-node-api-corda-3.0.jar:?]
at net.corda.nodeapi.internal.persistence.HibernateConfiguration$sessionFactoryForSchemas$1.apply(HibernateConfiguration.kt:44) ~[corda-node-api-corda-3.0.jar:?]
at net.corda.nodeapi.internal.persistence.HibernateConfiguration$sessionFactoryForSchemas
Any insights to tackle the above scenario ?
You cannot store the INDENTStates directly as state instances. You need to define an entity to represent the INDENTStates in the database.
Here is an example from another question:
object TestSchema : MappedSchema(SchemaFamily::class.java, 1, setOf(Parent::class.java, Child::class.java)) {
#Entity
#Table(name = "Parents")
class Parent : PersistentState() {
#OneToMany(fetch = FetchType.LAZY)
#JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index"))
#OrderColumn
#Cascade(CascadeType.PERSIST)
var children: MutableSet<Child> = mutableSetOf()
}
#Entity
#Table(name = "Children")
class Child {
#Id
#GeneratedValue
#Column(name = "child_id", unique = true, nullable = false)
var childId: Int? = null
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index"))
var parent: Parent? = null
}
}

Newline in Constant Error and Syntax error, ',' expected

I am currently working through this tutorial- https://www.asp.net/web-forms/overview/getting-started/getting-started-with-aspnet-45-web-forms/introduction-and-overview
Everything is working except one page is filled with errors when I copy and paste it in there. This tutorial is done in Visual Studio 2013 and I am in 2015 but I didn't think that would make that big of difference, I made the assumption that the syntax would stay the same. Here is the code that when I paste in fills with 446 errors-
using System.Collections.Generic;
using System.Data.Entity;
namespace WingtipToys.Models
{
public class ProductDatabaseInitializer :
DropCreateDatabaseAlways<ProductContext>
{
protected override void Seed(ProductContext context)
{
GetCategories().ForEach(c => context.Categories.Add(c));
GetProducts().ForEach(p => context.Products.Add(p));
}
private static List<Category> GetCategories()
{
var categories = new List<Category> {
new Category
{
CategoryID = 1,
CategoryName = "Cars"
},
new Category
{
CategoryID = 2,
CategoryName = "Planes"
},
new Category
{
CategoryID = 3,
CategoryName = "Trucks"
},
new Category
{
CategoryID = 4,
CategoryName = "Boats"
},
new Category
{
CategoryID = 5,
CategoryName = "Rockets"
},
};
return categories;
}
private static List<Product> GetProducts()
{
var products = new List<Product> {
new Product
{
ProductID = 1,
ProductName = "Convertible Car",
Description = "This convertible car is fast! The engine is
powered by a neutrino based battery (not included)." +
"Power it up and let it go!",
ImagePath="carconvert.png",
UnitPrice = 22.50,
CategoryID = 1
},
new Product
{
ProductID = 2,
ProductName = "Old-time Car",
Description = "There's nothing old about this toy car,
except it's looks. Compatible with other old toy cars.",
ImagePath="carearly.png",
UnitPrice = 15.95,
CategoryID = 1
},
new Product
{
ProductID = 3,
ProductName = "Fast Car",
Description = "Yes this car is fast, but it also floats in
water.",
ImagePath="carfast.png",
UnitPrice = 32.99,
CategoryID = 1
},
new Product
{
ProductID = 4,
ProductName = "Super Fast Car",
Description = "Use this super fast car to entertain guests.
Lights and doors work!",
ImagePath="carfaster.png",
UnitPrice = 8.95,
CategoryID = 1
},
new Product
{
ProductID = 5,
ProductName = "Old Style Racer",
Description = "This old style racer can fly (with user
assistance). Gravity controls flight duration." +
"No batteries required.",
ImagePath = "carracer.png",
UnitPrice = 34.95,
CategoryID = 1
},
new Product
{
ProductID = 6,
ProductName = "Ace Plane",
Description = "Authentic airplane toy. Features realistic
color and details.",
ImagePath="planeace.png",
UnitPrice = 95.00,
CategoryID = 2
},
new Product
{
ProductID = 7,
ProductName = "Glider",
Description = "This fun glider is made from real balsa
wood.Some assembly required.",
ImagePath="planeglider.png",
UnitPrice = 4.95,
CategoryID = 2
},
new Product
{
ProductID = 8,
ProductName = "Paper Plane",
Description = "This paper plane is like no other paper
plane.Some folding required.",
ImagePath="planepaper.png",
UnitPrice = 2.95,
CategoryID = 2
},
new Product
{
ProductID = 9,
ProductName = "Propeller Plane",
Description = "Rubber band powered plane features two
wheels.",
ImagePath="planeprop.png",
UnitPrice = 32.95,
CategoryID = 2
},
new Product
{
ProductID = 10,
ProductName = "Early Truck",
Description = "This toy truck has a real gas powered
engine.Requires regular tune ups.",
ImagePath= "truckearly.png",
UnitPrice = 15.00,
CategoryID = 3
},
new Product
{
ProductID = 11,
ProductName = "Fire Truck",
Description = "You will have endless fun with this one
quarter sized fire truck.",
ImagePath= "truckfire.png",
UnitPrice = 26.00,
CategoryID = 3
},
new Product
{
ProductID = 12,
ProductName = "Big Truck",
Description = "This fun toy truck can be used to tow other
trucks that are not as big.",
ImagePath="truckbig.png",
UnitPrice = 29.00,
CategoryID = 3
},
new Product
{
ProductID = 13,
ProductName = "Big Ship",
Description = "Is it a boat or a ship. Let this floating
vehicle decide by using its " +
"artifically intelligent computer brain!",
ImagePath="boatbig.png",
UnitPrice = 95.00,
CategoryID = 4
},
new Product
{
ProductID = 14,
ProductName = "Paper Boat",
Description = "Floating fun for all! This toy boat can be
assembled in seconds.Floats for minutes!" +
"Some folding required.",
ImagePath="boatpaper.png",
UnitPrice = 4.95,
CategoryID = 4
},
new Product
{
ProductID = 15,
ProductName = "Sail Boat",
Description = "Put this fun toy sail boat in the water and
let it go!",
ImagePath="boatsail.png",
UnitPrice = 42.95,
CategoryID = 4
},
new Product
{
ProductID = 16,
ProductName = "Rocket",
Description = "This fun rocket will travel up to a height
of 200 feet.",
ImagePath="rocket.png",
UnitPrice = 122.95,
CategoryID = 5
}
};
return products;
}
}
}
There seems to be a problem while you copied the code as it split the description field into multiple lines, which is why you are seeing the error.
You could try like this and it will still allow multiple lines
Description = #"This convertible car is fast! The engine is
powered by a neutrino based battery (not included).
Power it up and let it go!"
In case someone else needs this. When pasting it in it put the description on two lines. If you put the description on one line it works fine.

Resources