I have a question. I'm working with Hyperledger Composer Playground and I'm using the transaction SetupDemo for tests. But right now everytime I run a test with the SetupDemo I have to delete the created participants and assets single handed.
Is there a possibility (a function or sth) to delete all of them at once?
Thanks for your help!!
yes I can add my code. I used the example "vehicle-manufacturer-lifecycle" as an example and now I'm changing the variable names.
This is my model file. The important part is the first part (with the first concept and asset)
namespace org.master
// BASE DEFINTIONS
concept MaterialDetails {
--> Manufacturer make
o String materialName
o String materialColour
o Double amount
}
asset Material identified by materialId {
o String materialId
o MaterialDetails materialDetails
--> Person owner optional
}
concept ModuleDetails {
--> Manufacturer make
o String moduleName
o Double amount
}
asset Module identified by moduleId {
o String moduleId
o ModuleDetails moduleDetails
--> Person owner optional
}
abstract participant Company identified by companyId {
o String companyId
o String name
}
participant Person identified by username {
o String username
o String email optional
}
// MANUFACTURE DEFINITIONS
participant Manufacturer extends Company {
}
participant Producer extends Company {
}
enum OrderStatus {
o PLACED
o SCHEDULED_FOR_MANUFACTURE
o VIN_ASSIGNED
o OWNER_ASSIGNED
o DELIVERED
}
concept Options {
o String trim
o String interior
o String[] extras
}
asset Order identified by orderId {
o String orderId
o MaterialDetails materialDetails
o OrderStatus orderStatus
o Options options
--> Person orderer
}
transaction PlaceOrder {
o String orderId
o MaterialDetails materialDetails
o Options options
--> Person orderer
}
event PlaceOrderEvent {
o String orderId
o MaterialDetails materialDetails
o Options options
--> Person orderer
}
transaction UpdateOrderStatus {
o OrderStatus orderStatus
o String vin optional
--> Order order
}
event UpdateOrderStatusEvent {
o OrderStatus orderStatus
o Order order
}
// REGULATOR DEFINITIONS
participant Regulator extends Company {
}
This is my script file:
// DEMO SETUP FUNCTIONS
/**
* Create the participants to use in the demo
* #param {org.master.SetupDemo} setupDemo - the SetupDemo transaction
* #transaction
*/
async function setupDemo() { // eslint-disable-line no-unused-vars
console.log('setupDemo');
const factory = getFactory();
const namespace = 'org.master';
let people = ['Paul'];
let materialSuppliers;
const materials = {
'MSLogistics': {
'Wire1': [
{
'materialId': 'W1',
'materialColour': 'Yellow',
'amount': 100
}
],
'Wire2': [
{
'materialId': 'W2',
'materialColour': 'Blue',
'amount': 100
}
],
'Wire3': [
{
'materialId': 'W3',
'materialColour': 'Black',
'amount': 100
}
]
},
'MSProduction': {
},
'MSQualityControl': {
}
};
// convert array names of people to be array of participant resources of type Person with identifier of that name
people = people.map(function (person) {
return factory.newResource(namespace, 'Person', person);
});
// create array of Manufacturer particpant resources identified by the top level keys in vehicles const
materialSuppliers = Object.keys(materials).map(function (manufacturer) {
const manufacturerResource = factory.newResource(namespace, 'Manufacturer', manufacturer);
manufacturerResource.name = manufacturer;
return manufacturerResource;
});
const regulator = factory.newResource(namespace, 'Regulator', 'VDA');
regulator.name = 'VDA';
// add the regulator
const regulatorRegistry = await getParticipantRegistry(namespace + '.Regulator');
await regulatorRegistry.add(regulator);
// add the manufacturers
const manufacturerRegistry = await getParticipantRegistry(namespace + '.Manufacturer');
await manufacturerRegistry.addAll(materialSuppliers);
// add the persons
const personRegistry = await getParticipantRegistry(namespace + '.Person');
await personRegistry.addAll(people);
// add the vehicles
const vehicleRegistry = await getAssetRegistry(namespace + '.Material');
const vehicleResources = [];
for (const manufacturer in materials) {
for (const model in materials[manufacturer]) {
const vehicconstemplatesForModel = materials[manufacturer][model];
vehicconstemplatesForModel.forEach(function(vehicconstemplate) {
const material = factory.newResource(namespace, 'Material', vehicconstemplate.materialId);
material.owner = people[vehicleResources.length+1];
material.materialDetails = factory.newConcept(namespace, 'MaterialDetails');
material.materialDetails.make = factory.newResource(namespace, 'Manufacturer', manufacturer);
material.materialDetails.materialName = model;
material.materialDetails.materialColour = vehicconstemplate.materialColour;
material.materialDetails.amount = vehicconstemplate.amount;
vehicleResources.push(material);
});
}
}
await vehicleRegistry.addAll(vehicleResources);
}
and this is the access control:
rule PersonMakeOrder {
description: "Allow Persons to create and view orders"
participant(p): "org.master.Person"
operation: CREATE
resource(o): "org.master.Order"
transaction(tx): "org.master.PlaceOrder"
condition: (o.orderer.getIdentifier() == p.getIdentifier())
action: ALLOW
}
rule PersonPlaceOrder {
description: "Allow Persons to place orders and view they've done this"
participant(p): "org.master.Person"
operation: CREATE, READ
resource(o): "org.master.PlaceOrder"
condition: (o.orderer.getIdentifier() == p.getIdentifier())
action: ALLOW
}
rule PersonReadOrder {
description: "Allow Persons to place orders and view they've done this"
participant(p): "org.master.Person"
operation: READ
resource(o): "org.master.Order"
condition: (o.orderer.getIdentifier() == p.getIdentifier())
action: ALLOW
}
rule ManufacturerUpdateOrder {
description: "Allow manufacturers to view and update their own orders"
participant(m): "org.master.Manufacturer"
operation: UPDATE
resource(o): "org.master.Order"
transaction(tx): "org.master.UpdateOrderStatus"
condition: (o.materialDetails.make.getIdentifier() == m.getIdentifier())
action: ALLOW
}
rule ManufacturerUpdateOrderStatus {
description: "Allow manufacturers to update order statuses and view they've done this"
participant(m): "org.master.Manufacturer"
operation: CREATE, READ
resource(o): "org.master.UpdateOrderStatus"
condition: (o.order.materialDetails.make.getIdentifier() == m.getIdentifier())
action: ALLOW
}
rule ManufacturerReadOrder {
description: "Allow manufacturers to view and update their own orders"
participant(m): "org.master.Manufacturer"
operation: READ
resource(o): "org.master.Order"
condition: (o.materialDetails.make.getIdentifier() == m.getIdentifier())
action: ALLOW
}
rule ManufacturerCreateVehicles {
description: "Allow manufacturers to create and view their vehicles"
participant(m): "org.master.Manufacturer"
operation: CREATE
resource(v): "org.master.Material"
transaction(tx): "org.master.UpdateOrderStatus"
condition: (v.materialDetails.make.getIdentifier() == m.getIdentifier() && tx.orderStatus == "VIN_ASSIGNED")
action: ALLOW
}
rule ManufacturerReadVehicles {
description: "Allow manufacturers to create and view their vehicles"
participant(m): "org.master.Manufacturer"
operation: READ
resource(v): "org.master.Material"
condition: (v.materialDetails.make.getIdentifier() == m.getIdentifier())
action: ALLOW
}
rule RegulatorAdminUser {
description: "Let the regulator do anything"
participant: "org.master.Regulator"
operation: ALL
resource: "**"
action: ALLOW
}
rule ParticipantsSeeSelves {
description: "Let participants see themselves"
participant(p): "org.hyperledger.composer.system.Participant"
operation: ALL
resource(r): "org.hyperledger.composer.system.Participant"
condition: (r.getIdentifier() == p.getIdentifier())
action: ALLOW
}
rule NetworkAdminUser {
description: "Grant business network administrators full access to user resources"
participant: "org.hyperledger.composer.system.NetworkAdmin"
operation: ALL
resource: "**"
action: ALLOW
}
rule System {
description: "Grant all full access to system resources"
participant: "org.**"
operation: ALL
resource: "org.hyperledger.composer.system.**"
action: ALLOW
}
Related
I am trying to build a subgraph for Graph Protocol following the example here (the example with the more performant way). During compilation of my Graph protocol project using a testing tool matchstick-as ^0.5.0, I get the following error:
Igniting tests
nft-lottery
--------------------------------------------------
Enter the NFT Lottery:
thread 'main' panicked at '🆘 Unexpected error upon calling hook: Missing value for non-nullable field 'player' for an entity of type 'PlayerToLottery'.
wasm backtrace:
0: 0x331a - <unknown>!src/nft-lottery/handleNftLotteryEnter;
1: 0x3807 - <unknown>!start:tests/nft-lottery.test~anonymous|0~anonymous|0
Could anyone help me with this, please?
Here is my ./schema.graphql
type NftLottery #entity {
id: ID!
open: BigInt!
close: BigInt!
prize: BigInt!
players: [PlayerToLottery!]! #derivedFrom(field: "lottery")
requestId: BigInt
updatedAtTimestamp: BigInt
}
type Player #entity {
id: ID! # address
lotteries: [PlayerToLottery!]! #derivedFrom(field: "player")
}
type PlayerToLottery #entity {
id: ID! # Set playerAddress.toHexString() + lotteryId.toHexString()
player: Player!
lottery: NftLottery!
}
# events
type NftLotteryEnter #entity {
id: ID! # Set lotteryCounter + playerAddress
lotteryCounter: BigInt!
player: Bytes!
numberOfEntrances: [BigInt!]!
updatedAtTimestamp: BigInt
}
Here is my ./src/mapping.ts
function getIdFromEventAddressInt(par2: Address, par1: BigInt): string {
return par1.toHexString() + par2.toHexString()
}
export function handleNftLotteryEnter(event: NftLotteryEnterEvent): void {
/* if a PlayerToLottery does not exists, create it */
const playerLotteryId = getIdFromEventAddressInt(event.params.player, event.params.lotteryCounter)
let playerToLottery = PlayerToLottery.load(playerLotteryId)
if (!playerToLottery) {
playerToLottery = new PlayerToLottery(playerLotteryId)
playerToLottery.save()
}
/* if a player does not exist, create them */
const playerId = event.params.player.toHexString()
let player = Player.load(playerId)
if (!player) {
player = new Player(playerId)
player.save()
}
/* if a lottery does not exist, create it */
const itemId = event.params.lotteryCounter.toHexString()
let nftLottery = NftLottery.load(itemId)
if (!nftLottery) {
nftLottery = new NftLottery(itemId)
nftLottery.open = event.block.timestamp
nftLottery.close = BigInt.fromString("000000000000")
nftLottery.prize = BigInt.fromString(event.params.entranceFee.toString())
nftLottery.players = new Array<string>()
}
// update lottery data
nftLottery.prize.plus(event.params.entranceFee)
// update players
let arrPlayers = nftLottery.players
arrPlayers.push(event.params.player.toHexString())
nftLottery.players = arrPlayers
nftLottery.updatedAtTimestamp = event.block.timestamp
nftLottery.save()
}
Entity PlayerToLottery needs to have values assigned before saving it:
/* if a PlayerToLottery does not exists, create it */
const playerLotteryId = getIdFromEventAddressInt(event.params.player, event.params.lotteryCounter)
let playerToLottery = PlayerToLottery.load(playerLotteryId)
if (!playerToLottery) {
playerToLottery = new PlayerToLottery(playerLotteryId)
playerToLottery.player = event.params.player.toHexString()
playerToLottery.lottery = event.params.lotteryCounter.toHexString()
playerToLottery.save()
}
Credit goes to Maks#3349 at unit testing of Graph discord.
I am having issues trying to get my transactions working on Hyperledger composer playground.
I have a function called CastVote in my script.js file and a transaction called CastVote in my model file. Whenever I try to submit a transaction, it seems it can not find the CastVote function in my script file. I'm getting the below error:
Below is my code:
Model File:
namespace org.example.tasweetx
asset Vote identified by voteID {
o String voteID
o String voterID
o String candidateID
}
participant Voter identified by voterID {
o String voterID
o String voterFirstName
o String voterLastName
o String voterEmail
o String voterEmiratesID
}
participant Candidate identified by candidateID {
o String candidateID
o String candidateFirstName
o String candidateLastName
o Integer voteCount
}
transaction CastVote {
--> Voter voter
--> Candidate candidate
}
Script File:
/**
*
* #param {org.example.tasweetx.CastVote} transacation Function to handle vote casting and incrementing candidate vote count
* #transacation
*/
function CastVote(transacation) {
if(voter.voted == false) {
voter.voted == true;
IncrementVoteCount(transacation)
return getAssetRegistry('org.example.tasweetx.Candidate')
.then(function (assetRegistry){
return assetRegistry.update(voteCount);
})
} else {
throw new Error('You have already voted!');
}
}
function IncrementVoteCount(transacation) {
candidate.voteCount += 1;
return getAssetRegistry('org.example.tasweetx.Candidate')
.then(function (assetRegistry) {
return assetRegistry.update(voteCount);
})
}
you spelt transaction incorrectly, you have put
* #transacation
when it should be
* #transaction
namespace com.biz
participant User identified by name {
o String name
}
participant Bank identified by name {
o String name
o Integer points
--> Document document optional
}
asset Document identified by docname {
o String docname
o String doctype
o String hash
o String from
o String to
}
transaction UploadDoc {
--> Document document
}
transaction ShareDoc {
--> Document document
--> Bank bank
}
transaction SetupDemo {
}
Script File :
/**
*
* #param {com.biz.UploadDoc} uploadDoc
* #transaction
*/
async function uploadDoc(uploadDoc) {
uploadDoc.document.docname = 'BC1.jpg';
uploadDoc.document.doctype = 'BC';
uploadDoc.document.hash = '123456';
uploadDoc.document.from = 'Bank_1';
uploadDoc.document.to = 'User_1';
const ar = await getAssetRegistry('com.biz.Document');
await ar.update(uploadDoc.document);
}
/**
*
* #param {com.biz.ShareDoc} shareDoc
* #transaction
*/
async function shareDoc(shareDoc) {
shareDoc.document.docname = 'BC1.jpg';
shareDoc.document.doctype = 'BC';
shareDoc.document.hash = '12346';
shareDoc.document.from = 'User_1';
shareDoc.document.to = shareDoc.bank.name;
const dr = await getAssetRegistry('com.biz.Document');
await dr.update(shareDoc.document);
}
/**
*
* #param {com.biz.SetupDemo} setupDemo
* #transaction
*/
async function setupDemo(setupDemo) {
const factory = getFactory();
const NS = 'com.biz';
const user = factory.newResource(NS,'User','User_1')
user.name='User_1';
const banks = [
factory.newResource(NS,'Bank','Bank_1'),
factory.newResource(NS,'Bank','Bank_2')
];
banks[0].name = 'Bank_1';
banks[1].name = 'Bank_2';
banks[0].points = 100;
banks[1].points = 100;
const userRegistry = await getParticipantRegistry(NS + '.User');
await userRegistry.addAll([user]);
const bankRegistry = await getParticipantRegistry(NS + '.Bank');
await bankRegistry.addAll(banks);
const documents = [
factory.newResource(NS, 'Document', 'Pa60.jpg'),
factory.newResource(NS, 'Document', 'Pa80.jpg')
];
documents[0].docname = 'Pa60.jpg';
documents[0].doctype = 'DrivingLicense';
documents[0].hash = '12345';
documents[0].from ='Bank_1';
documents[0].to = 'User_1';
documents[1].docname = 'Pa80.jpg';
documents[1].doctype = 'DrivingLicense';
documents[1].hash = '123456';
documents[1].from ='Bank_1';
documents[1].to = 'User_1';
const docRegistry = await getAssetRegistry(NS + '.Document')
await docRegistry.addAll(documents);
}
Issue : First I run transaction SetupDemo. Runs fine.
Next, I run transaction UploadDoc. Runs fine. Pa60.jpg is updated as BC1.jpg. ID given in Plyground for submitting transaction : Pa60.jpg
Next, I want to run ShareDoc transaction. If I give ID as BC1.jpg while submitting transaction, 'Object with id 'BC1.jpg' not found' error.
Please help me resolve the issue.
I have a problem when trying to add a new asset to an array of assets which are part of the participant as a reference.
Here I have SharedAccount participant controlled by its members who are connected via their share in the account.
I want to write a transaction for creating a new SharedAccount by one person. When a person submits a transaction, it should create a share asset if that person and add it to SharedAccount's shares array.
Here's how my code looks like
.cto:
...
participant SharedAccount identified by sharedAccountId {
o String sharedAccountId
--> Share[] shares
o Double balance
o Double originalBalance
}
asset Share identified by shareId {
o String shareId
--> Person shareHolder
o Double amount
}
transaction CreateSharedAccount {
--> Person creator
o String accountName
o Integer amount
}
...
.js:
...
/**
* #param {org.mistral.bulliongrower.CreateSharedAccount} createSharedAccount
* #transaction
*/
async function CreateSharedAccount(createSharedAccount) {
const factory = getFactory();
const NS = 'org.mistral.bulliongrower';
// create share
const share = factory.newResource(NS, 'Share', createSharedAccount.creator.personId + 'SHARE');
share.amount = createSharedAccount.amount;
share.shareHolder = createSharedAccount.creator;
share.shareHolder.balance -= createSharedAccount.amount;
const sharesRegistry = await getAssetRegistry(NS + '.Share');
await sharesRegistry.add(share);
const personRegistry = await getParticipantRegistry(NS + '.Person');
await personRegistry.update(share.shareHolder);
// create sharedAccount
const sharedAcc = factory.newResource(NS, 'SharedAccount', createSharedAccount.accountName);
sharedAcc.shares.push(share);
sharedAcc.balance = createSharedAccount.amount;
sharedAcc.originalBalance = createSharedAccount.amount;
const sharedAccRegistry = await getAssetRegistry(NS + '.SharedAccount');
await sharedAccRegistry.add(sharedAcc);
}
...
I'm not sure if I should use factory.newRelationship and how, when adding a share Asset to SharedAccount.
The error I get in the playground when trying to execute the transaction is
TypeError: Cannot read property 'push' of undefined
try to do this:
/**
* #param {org.mistral.bulliongrower.CreateSharedAccount} createSharedAccount
* #transaction
*/
async function CreateSharedAccount(createSharedAccount) {
const factory = getFactory();
const NS = 'org.mistral.bulliongrower';
// create share
const share = factory.newResource(NS, 'Share', createSharedAccount.creator.personId + 'SHARE');
//const share = factory.newRelationship(NS, 'Share', createSharedAccount.creator.personId + 'SHARE');
share.amount = createSharedAccount.amount;
//share.shareHolder = factory.newRelationship(NS, 'Share', createSharedAccount.creator.personId);
share.shareHolder = createSharedAccount.creator;
share.shareHolder.balance -= createSharedAccount.amount;
const sharesRegistry = await getAssetRegistry(NS + '.Share');
await sharesRegistry.add(share);
const personRegistry = await getParticipantRegistry(NS + '.Person');
await personRegistry.update(share.shareHolder);
// create sharedacc
const sharedAcc = factory.newResource(NS, 'SharedAccount', createSharedAccount.accountName);
//sharedAcc.shares = factory.newRelationship(NS, 'Share', createSharedAccount.creator.personId);
//sharedAcc.shares[0] = factory.newRelationship(NS, 'Share', share.shareId);
// define an array
let sharesArray = [];
sharesArray.push(share);
sharedAcc.shares = sharesArray;
sharedAcc.balance = createSharedAccount.amount;
sharedAcc.originalBalance = createSharedAccount.amount;
// use getParticipantRegistry not getAssetRegistry
const sharedAccRegistry = await getParticipantRegistry(NS + '.SharedAccount');
await sharedAccRegistry.add(sharedAcc);
}
your transaction code should be something like below - some of your references weren't right (take too long to point out all the changes, so you can refer below).
I added a test string (for 'Person') just to show what you would do (to have a reason to update that particular participant registry).
Seems to me that SharedAccount is an asset not a participant. And you would use the appropriate JS API to update that type of registry.
balance is not a field on Person (it is on SharedAccount), but your code was trying to refer to it.
I've left comments for 'alternative ways' for declarations and such like - just by way of info.
/**
* #param {org.mistral.bulliongrower.CreateSharedAccount} createSharedAccount
* #transaction
*/
async function CreateSharedAccount(createSharedAccount) {
const factory = getFactory();
const NS = 'org.example.trading';
// create share
const share = factory.newResource(NS, 'Share', createSharedAccount.creator.personId + 'SHARE');
share.amount = createSharedAccount.amount;
console.log("amount is " + share.amount);
share.shareHolder = createSharedAccount.creator;
// share.shareHolder.balance -= createSharedAccount.amount; // won't work - balance is a field on SharedAccount not Person - moved it below
const sharesRegistry = await getAssetRegistry(NS + '.Share');
await sharesRegistry.add(share);
share.shareHolder.newstr = "123"; // setting 'SOME' field (I added 'newstr' in my model, see below - so as to have a reason to update / give an example
const personRegistry = await getParticipantRegistry(NS + '.Person');
await personRegistry.update(share.shareHolder);
// create sharedAccount
const sharedAcc = factory.newResource(NS, 'SharedAccount', createSharedAccount.accountName);
//let idsArray = new Array(); // alternative, or use implicit below.
let idsArray = [] ;
let shareAssetRelationship = factory.newRelationship(NS, 'Share', share.getIdentifier());
idsArray.push(shareAssetRelationship); // only one element anyway
sharedAcc.shares = idsArray;
sharedAcc.balance = createSharedAccount.amount; // this is a new resource - so balance is eq to trxn amount ?
sharedAcc.originalBalance = createSharedAccount.amount; // original balance is nothing or 'balance' ?....anyway....
const sharedAccRegistry = await getAssetRegistry(NS + '.SharedAccount');
await sharedAccRegistry.add(sharedAcc);
}
The model I used is this:
participant Person identified by personId {
o String personId
o String newstr
}
asset SharedAccount identified by sharedAccountId {
o String sharedAccountId
--> Share[] shares
o Double balance
o Double originalBalance
}
asset Share identified by shareId {
o String shareId
--> Person shareHolder
o Double amount
}
transaction CreateSharedAccount {
--> Person creator
o String accountName
o Integer amount
}
I have the below SQL which works just fine:
SELECT Message, CreateDate, AccountId, AlertTypeId
FROM dbo.Alerts
UNION
SELECT TOP (100) PERCENT Status, CreateDate, AccountId,
(SELECT 10 AS Expr1) AS AlertTypeId
FROM dbo.StatusUpdates
WHERE AccountId = PassedInParameter
ORDER BY CreateDate DESC
I am trying to convert it to LINQ, which doesn't work just fine :) Obviously, there is a lot wrong here - it is just a rough start. It does not account for the above temp column or the order by condition and the generics / return type ambiguity is my attempt to make sense of the two different return types:
public List<T> GetSomething<T>(Int32 accountId)
{
List<T> result;
using (DataContext dc = _conn.GetContext())
{
IEnumerable<Alert> alerts = (from a in dc.Alerts
where a.AccountId == accountId
select a);
IEnumerable<StatusUpdate> updates = (from s in dc.StatusUpdates
where s.AccountId == accountId
select s);
IEnumerable<T> obj = alerts.Union(updates);
result = obj.ToList();
}
return result;
}
The problems I am having are:
1) I am dealing with two different types (Alerts and StatusUpdate) in my selects and
I am not sure how to combine them (or what type to return). I am guessing this might
be solved with generics?
2) In my SQL, I have this code: (SELECT 10 AS Expr1) AS AlertTypeId which adds the value ten to the temp column AlertTypeId (allowing the union to match it to Alert's real column AlertTypeId). How are temp columns such as this accomplished in LINQ / how do I do this?
Thanks for your help.
EDIT---------------------------------EDIT------------------------------------------EDIT
OK, I am a little further along. Below is what I have currently. You will notice I added some logic to return the updates for friend relations. I also made this a generic method of type IList given that alerts and updates have to be generic to agree. I pass in StatusUpdate in the calling method (further down below).
public IList GetUpdatesByAccountId<T>(Int32 accountId)
{
List<Friend> friends = _friendRepository.GetFriendsByAccountId(accountId);
using (DataContext dc = _conn.GetContext())
{
// Get all the account ids related to this user
var friendAccountIds =
friends.Select(friend => friend.MyFriendsAccountId).Distinct();
friendAccountIds = friendAccountIds.Concat(new[] { accountId });
var updates =
dc.StatusUpdates.Where(s => s.AccountId.HasValue && friendAccountIds.Contains(s.AccountId.Value)).Select(
s => new { Alert = (Alert)null, StatusUpdate = s});
var alerts =
dc.Alerts.Where(a => a.AccountId == accountId).Select(
a => new {Alert = a, StatusUpdate = (StatusUpdate) null});
var obj = updates.Union(alerts).Take(100);
return obj.OrderByDescending(su => su.StatusUpdate.CreateDate).ToList();
}
}
And, the calling method:
protected void LoadStatus()
{
repStatusUpdates.DataSource = _statusRepository
.GetUpdatesByAccountId<StatusUpdate>(_userSession.CurrentUser.AccountId);
repStatusUpdates.DataBind();
}
AND here are the interfaces to the repositories I am using to access my Alert and StatusUpdate tables via LINQ:
public interface IAlertRepository
{
List<Alert> GetAlertsByAccountId(Int32 accountId);
void SaveAlert(Alert alert);
void DeleteAlert(Alert alert);
}
public interface IStatusUpdateRepository
{
StatusUpdate GetStatusUpdateById(Int32 statusUpdateId);
List<StatusUpdate> GetStatusUpdatesByAccountId(Int32 accountId);
List<StatusUpdate> GetFriendStatusUpdatesByAccountId(Int32 accountId, Boolean addPassedInAccount);
void SaveStatusUpdate(StatusUpdate statusUpdate);
List<StatusUpdate> GetTopNStatusUpdatesByAccountId(Int32 accountId, Int32 number);
List<StatusUpdate> GetTopNFriendStatusUpdatesByAccountId(Int32 accountId, Int32 number, Boolean addPassedInAccount);
}
Current Problems:
1) When I compile this code, I get this strange error:
Unable to cast object of type
'System.Data.Linq.SqlClient.SqlNew' to
type
'System.Data.Linq.SqlClient.SqlValue'.
The only reading I can find on it is this link although there isn't a clear solution there (at least that I can tell). However, if the above LINQ code does not look good to you, maybe whatever you suggest will cause this error to disappear.
2) The above code is still not accounting for this line from the original SQL:
(SELECT 10 AS Expr1) AS AlertTypeId
but this is minor.
Thanks again for the help.
Try this (i converted the StatusUpdate to an alert, if this isn't acceptable, you're going to have to either convert the Alert to a StatusUpdate, or create a new class):
var alerts = (from a in dc.Alerts
where a.AccountId == accountId
select a);
var updates = (from s in dc.StatusUpdates
where s.AccountId == accountId
select s)
.OrderByDescending( x => x.CreateDate)
.Take(100)
.Select( x => new Alert
{
Message = x.Percent.ToString(),
CreateDate = x.CreateDate,
AccountId = x.AccountId,
AlertTypeId = 10 // Is this right?
}
);
var obj = alerts.Union(updates);
result = obj.ToList();
The reason I do the Select last is so that you don't have to construct a new alert for all the results your are not using.
This will give you a list of Alerts.
Using a generic in this situation is sort of hard to pull off. For instance, you can't do this:
IQueryable alerts = (from a in _alerts
where a.AccountId == accountId
select a);
Because that implicitly converts a to type T. Even if you try to limit what T implements or inherits from:
public List<T> GetSomething<T>(Int32 accountId) where T : IAlert// Interface that both StatusUpdates and IAlert implement
public List<T> GetSomething<T>(Int32 accountId) where T : Alert
public List<T> GetSomething<T>(Int32 accountId) where T : AlertBase // Base class for both Status and Alert
You'll still run into problems because there is no way to statically know exactly what type T is, so you cannot know if it can be converted from Alert and StatusUpdate.
An alternative is to explicitly use IAlert as your return type:
public List<IAlert> GetSomething(Int32 accountId)
With IAlert:
public interface IAlert
{
int AccountId { get; set; }
int AlertTypeId { get; set; }
DateTime CreateDate { get; set; }
string Message { get; set; }
}
If you have have both Alert and StatusUpdate implement IAlert, you could rewrite it as so:
IQueryable<IAlert> alerts = (from a in dc.Alerts
where a.AccountId == accountId
select a);
IQueryable<IAlert> updates = (from s in dc.StatusUpdates
where s.AccountId == accountId
select s)
.OrderByDescending( x => x.CreateDate)
.Take(100);
var obj = alerts.Union(updates);
result = obj.ToList();
This is the route I would take instead of passing in some unknown type and trying to limit what it implements or inherits, because casting to that type might still be invalid.
You can only take unions of sequences of equal types. You need to convert alerts and updates to sequences of a common type, then take the union. You can do so using anonymous types. Especially useful if the types don't have anything in common.
//this is a hack and probably not what you would want to use.
var alerts =
from a in dc.Alerts
where a.AccountId == accountId
select new { Alert = a, StatusUpdate = (StatusUpdate)null };
var updates =
from s in dc.StatusUpdates
where s.AccountId == accountId
select new { Alert = (Alert)null, StatusUpdate = s };
//both are sequences of anonymous type with properties:
// Alert (of type Alert)
// StatusUpdate (of type StatusUpdate)
var obj = alerts.Union(updates);
If you have fields in common, you'd still use anonymous types except you'd include the known fields.
var alerts =
from a in dc.Alerts
where a.AccountId == accountId
select new
{
a.Message, //assuming is a string
Status = (string)null,
a.CreateDate,
a.AccountId,
a.AlertTypeId //assuming is an int
};
var updates =
(from s in dc.StatusUpdates
where s.AccountId == accountId
select new
{
Message = (string)null,
s.Status, //assuming is a string
s.CreateDate,
s.AccountId,
AlertTypeId = 10 //this should handle the "10 AS AlertTypeId" part
}).OrderByDescending(s => s.CreateDate);
var obj = alerts.Union(updates);
The key is that both anonymous types has the same exact properties of the same exact types. Then you can take the union between them both.