I need to programmatically (javascript) get a linked object of a given element per stereotype, even if it is more than one level up.
So, for example in the next figure, I expect to get Obj1 from both el1 and el2, but never Obj2.
I have this solution already but is seems not too elegant and its time consuming:
function count(main_str, sub_str)
{
main_str += '';
sub_str += '';
if (sub_str.length <= 0)
{
return main_str.length + 1;
}
subStr = sub_str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return (main_str.match(new RegExp(subStr, 'gi')) || []).length;
}
function getLinkedObjects(objectID, connectionType, end_or_start) {
//This function gets the objects linked to the given objectID. It can be filtered by 'connectionType' and by weather the given object is in start or end position.
if (connectionType == '' || connectionType == 'any') {
var connector = ""
} else {
var connector = " and Connector_type = '"+connectionType+"'"
}
if (end_or_start == 'start') {
var SQLquery = "select obj.Object_ID, obj.Name from (t_object obj inner join (select * from t_connector where start_object_ID = "+objectID+connector+") q on q.End_object_ID = obj.Object_ID) where obj.Object_ID <> "+objectID+connector
} else if (end_or_start == 'start') {
var SQLquery = "select obj.Object_ID, obj.Name from (t_object obj inner join (select * from t_connector where end_object_ID = "+objectID+connector+") q on q.start_object_ID = obj.Object_ID) where obj.Object_ID <> "+objectID+connector
} else if (end_or_start == '' || end_or_start == 'both') {
var SQLquery = "select distinct obj.Object_ID, obj.Name from (t_object obj inner join (select * from t_connector where (start_object_ID = "+objectID+connector+" or end_object_ID = "+objectID+connector+")) q on (q.Start_object_ID = obj.Object_ID or q.End_object_ID = obj.Object_ID)) where obj.Object_ID <> "+objectID+connector
} else {
var SQLquery = ""
}
var conn_elements = Repository.GetElementSet(SQLquery, 2);
return conn_elements;
}
function getObjectStream(elemid, stereotype) {
//This function gets all the stream of objects and/or blocks linked to the given objectID.
var i = 0
var streams = []
var linked_objs = getLinkedObjects(elemid, "", "both");
for (var l = 0; l < linked_objs.Count; l++) {
var level1 = getLinkedObjects(linked_objs.GetAt(l).ElementID, "", "both");
if (linked_objs.GetAt(l).Stereotype == stereotype) {
var stream = "l0-- " + linked_objs.GetAt(l).Name
streams.push(stream)
break
} else {
for (var l1 = 0; l1 < level1.Count; l1++) {
var level2 = getLinkedObjects(level1.GetAt(l1).ElementID, "", "both");
if (level1.GetAt(l1).Stereotype == stereotype) {
var stream = "l0-- " + linked_objs.GetAt(l).Name + " l1-- " + level1.GetAt(l1).Name
streams.push(stream)
break
} else {
for (var l2 = 0; l2 < level2.Count; l2++) {
if (level2.GetAt(l2).Stereotype == stereotype) {
var stream = "l0-- " + linked_objs.GetAt(l).Name + " l1-- " + level1.GetAt(l1).Name + " l2-- " + level2.GetAt(l2).Name
streams.push(stream)
break
}
}
}
}
}
}
var w = ''
var level = 5
for (var f = 0; f < streams.length; f++) {
levels = count(streams[f], "--")
if (levels < level) {
level = levels - 1
w = streams[f]
}
}
var le = "l"+level+"-- "
var n = w.search(le) + 5;
var winner = w.substring(n, w.lenght);
return winner
}
function main() {
var linked_per_stereotype = getObjectStream(1234, "A");
Session.Output(linked_per_stereotype);
}
main();
Any suggestions for a better approach for this?
Thank you!
I'm not really familiar with JS and it depends on your model. So just thinking loud: if there were a limited number of the desired stereotypes (and having language construct like in Python which might or might not be present in JS) I would just query all elements having the stereotype along with their connectors in a JOIN and make a hash of the result by object ID. So I could traverse simply by indexing the hash from the connector source/end ID. I would assume the number of stereotyped elements is rather low so that would be an approach.
If your model were huge and having tons of these stereotypes there might be no way around single queries. Maybe Geert has something doing that in one go.
I have a method that is getting a list of users. There is a store procedure that is expected either a username or null. It returns one or more users based on the parameter. It works fine most of the time, but I notice there are times it does not return any result even though I am passing the same exact parameter. I put a break point in the Execute Reader Async, and the line immediately following it. When the issue occurs, it reaches the first break point but not the second one. It does not throw an exception or prevent me from making another call. I can make the next call, and it will return the result as expected. I would appreciate some suggestions as to what may cause this issue?
public async Task<List<User>> GetUsers(string username, int accountType = 1)
{
List<User> users = null;
parameters = new List<SqlParameter>{
new SqlParameter{
DbType = DbType.String,
ParameterName = "#userName",
Value = username.NotEmpty() ? username : (object) DBNull.Value
},
new SqlParameter{
DbType = DbType.Int32,
ParameterName = "#accountType",
Value = accountType
}
};
try
{
using (SqlConnection con = new SqlConnection(conStr))
{
await con.OpenAsync();
using (SqlCommand cmd = new SqlCommand("spGetUsers", con))
{
cmd.Parameters.AddRange(parameters.ToArray());
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader dr = await cmd.ExecuteReaderAsync()
if (dr.HasRecord())
{
while (await dr.ReadAsync())
{
User user = new User();
user.FirstName = dr["firstName"].ToString();
user.LastName = dr["lastName"].ToString();
user.DateRegister = Convert.ToDateTime(dr["DateRegister"].ToString());
user.Active = Convert.ToBoolean(dr["Active"].ToString());
if(users == null)
{
users = new List<User>();
}
users.Add(user);
}
}
}
}
}
catch (Exception ex)
{
Util.LogError(ex.ToString());
users = null;
}
return users;
}
Update:
Yes, the error is being logged. Also, I added a breakpoint in the catch statement in case an error is thrown.
Here is the query that is used to create that store procedure:
IF EXISTS (SELECT 1 FROM SYS.objects WHERE object_id = OBJECT_ID('spGetUsers') AND TYPE IN (N'PC', N'P'))
DROP PROCEDURE spGetUsers
GO
CREATE PROCEDURE spGetUsers
#userName nvarchar(50),
#accountType int = 1
AS
BEGIN
SELECT U.firstName, U.lastName, U.DateRegister, A.Active
FROM [User] U
inner join UserAccount UA
on U.Id = UA.userid
inner join Account A
on A.Id = UA.accountId
WHERE U.Id > 0
AND UA.Id > 0
AND A.Id > 0
AND UA.AccountType IN (#accountType )
and (A.UserName in (#userName) or #userName IS NULL)
END
Extension Method to check if SQL DataReader has record
public static bool HasRecord(this System.Data.SqlClient.SqlDataReader dr)
{
if (dr != null && dr.HasRows)
{
return true;
}
return false;
}
Trying to figure out what would be the most efficient approach to use a subquery in an inline SQL statement. Inline SQL is not something I have much experience with, but I have no choice at my organization alas.
SELECT *
FROM dbo.VW_RMISPayment
WHERE ProcDate BETWEEN '7/2/2018' AND '3/8/2019'
AND Status = 'P'
AND Fund = '359'
AND Amount > 0
AND (BatchNotate = 'B' OR BatchNotate IS NULL)
ORDER BY ProcDate, Amount
How could I parameterized the (BatchNotate = 'B' OR BatchNotate IS NULL) part?
My variable passed in as a List<string>, but I could change it to be anything. I'm just not sure how I can create this subquery from my variable
if (BatchNotate.Count() > 0)
{
query += " AND BatchNotate= #BatchNotate";
}
cmd.Parameters.AddWithValue("#BatchNotate", batchNotate);
Use this:
BatchNotate = COALESCE(#inVariable,'B')
If the variable (#inVariable) is null then it will "default to B.
If it is something else it will compare against that.
Could you do something like this?
SELECT *
FROM dbo.VW_RMISPayment
WHERE ProcDate BETWEEN '7/2/2018' AND '3/8/2019'
AND Status = 'P'
AND Fund = '359'
AND Amount > 0
AND BatchNotate = COALESCE(#BatchNotate, BatchNotate)
ORDER BY ProcDate, Amount
Neither of those worked for what I'm after. Here is what I ended up doing. It's kinda hacky, but it works.
public static string AddParametersOR<T>(SqlParameterCollection parameters,
string fieldName,
string pattern,
SqlDbType parameterType,
int length,
IEnumerable<T> values)
{
if (parameters == null)
throw new ArgumentNullException("parameters");
if (pattern == null)
throw new ArgumentNullException("pattern");
if (values == null)
throw new ArgumentNullException("values");
if (!pattern.StartsWith("#", StringComparison.CurrentCultureIgnoreCase))
throw new ArgumentException("Pattern must start with '#'");
var parameterNames = new List<string>();
foreach (var item in values)
{
var parameterName = parameterNames.Count.ToString(pattern, CultureInfo.InvariantCulture);
string parameterWithFieldName = string.Empty;
if (item.ToString().ToUpper() == "NULL")
{
parameterWithFieldName = string.Format("{0} IS NULL", fieldName);
}
else if (item.ToString().ToUpper() == "NOTNULL")
{
parameterWithFieldName = string.Format("{0} IS NOT", fieldName);
}
else
{
parameterWithFieldName = string.Format("{0}= {1}", fieldName, parameterName);
}
parameterNames.Add(parameterWithFieldName);
parameters.Add(parameterName, parameterType, length).Value = item;
}
return string.Join(" OR ", parameterNames.ToArray());
}
Usage:
if (batchNotate.Count() > 0)
{
query += " AND ({#BatchNotate})";
}
string batchNotateParamNames = SqlHelper.AddParametersOR(cmd.Parameters, "BatchNotate", "#B0", SqlDbType.VarChar, 1, batchNotate);
cmd.CommandText = query.Replace("{#BatchNotate}", batchNotateParamNames);
Depending on how many items are in your list the output will look like this:
(BatchNotate= 'B' OR BatchNotate= 'N' OR BatchNotate IS NULL)
If it finds "NULL" or "NOTNULL" it will replace these with IS NULL or IS NOT NULL
Facing same issue, is there any progress on this one :
http://www.softfluent.com/product/codefluent-entities/knowledge-center/point-sql-server-producer-to-production-db-instead-of-using-pivot-producer
Thanks for your answer,
EDIT: this is the code used to delete all the constraints
private static void RemoveCodeFluentConstraintsTable(IList<PivotRunnerConstraint> constraints, String connectionString)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
// Set up a command with the given query and associate
// this with the current connection.
using (SqlCommand cmd = new SqlCommand("SELECT tables.name as tableName, default_constraints.name as constraintName FROM sys.all_columns INNER JOIN sys.tables ON all_columns.object_id = tables.object_id INNER JOIN sys.schemas ON tables.schema_id = schemas.schema_id INNER JOIN sys.default_constraints ON all_columns.default_object_id = default_constraints.object_id", con))
{
foreach (PivotRunnerConstraint constraint in constraints)
{
String tableName = constraint.ParentName;
String constraintName = constraint.Name;
if (tableName != null && constraintName != null)
{
SqlCommand cmdConstraint = new SqlCommand("ALTER TABLE [MySchema].[" + tableName + "] DROP CONSTRAINT [" + constraintName + "]", con);
cmdConstraint.ExecuteNonQuery();
}
}
//con.Close();
}
}
return;
}
I went for using a custom naming convention using table name prefixing the generated constraint name.
public class MyNamingConvention : FormatNamingConvention
{
public override string GetName(INamedObject obj, IDictionary context)
{
Column column = obj as Column;
if (column != null && column.Table != null)
{
var name = context["name"] as string;
if (name != null && (name.StartsWith("DF_")))
{
return column.Table.Name + base.GetName(obj, context);
}
}
return base.GetName(obj, context);
}
}
At the same time, I also had to delete existing constraints to avoid collision:
private static void RemoveCodeFluentConstraints(string connectionString)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
// Set up a command with the given query and associate
// this with the current connection.
using (SqlCommand cmd = new SqlCommand("SELECT c.name, t.name FROM sys.objects c, sys.objects t, sys.schemas s WHERE c.type IN('F', 'PK', 'FK', 'UQ', 'D') AND c.parent_object_id = t.object_id and t.SCHEMA_ID = s.schema_id AND t.type = 'U' AND s.name = 'MySchema' ORDER BY c.type", con))
{
using (IDataReader dr = cmd.ExecuteReader())
{
using (SqlConnection con1 = new SqlConnection(connectionString))
{
con1.Open();
while (dr.Read())
{
String constraintName = dr[0].ToString();
String tableName = dr[1].ToString();
if (tableName != null && constraintName != null)
{
String cmdConstraintSql = "ALTER TABLE [MySchema].[" + tableName + "] DROP CONSTRAINT [" + constraintName + "]";
ActivityLog.Write("Execute " + cmdConstraintSql, ActivityLogsFile);
SqlCommand cmdConstraint = new SqlCommand(cmdConstraintSql, con1);
cmdConstraint.ExecuteNonQuery();
}
}
con1.Close();
}
}
}
con.Close();
}
return;
}
Other problem were related to definition of pivot file not being picked correctly: Pivot Runner null command
How can I convert DateTime into a formatted string?
This is the line in the following query that needs help:
StartDate = string.Format("{0:dd.MM.yy}", p.StartDate)
The whole query:
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new Offer()
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer = value,
ID = p.ID,
LocationID = q.BranchID,
LocationName = q.CustomerBranch.BranchName,
OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate = string.Format("{0:dd.MM.yy}", p.StartDate)
}).First();
I get the following error message:
LINQ to Entities does not recognize the method 'System.String ToString(System.String)' method, and this method cannot be translated into a store expression.
Another option is using SqlFunctions.DateName, your code will be like this:
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer = value,
ID = p.ID,
LocationID = q.BranchID,
LocationName = q.CustomerBranch.BranchName,
OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate = SqlFunctions.DateName("day", p.StartDate) + "/" + SqlFunctions.DateName("month", p.StartDate) + "/" + SqlFunctions.DateName("year", p.StartDate)
})
I found it useful if you don't want to add an extra select new block.
EDIT: Now that I understand the question, I'm giving it another shot :)
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer=value,
ID=p.ID,
LocationID=q.BranchID,
LocationName=q.CustomerBranch.BranchName,
OriginalPrice=SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice=SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate=p.StartDate
})
.ToList()
.Select(x => new Offer()
{
Title = x.OfferTitle,
Description = x.Description,
BestOffer=value,
ID=x.ID,
LocationID=x.BranchID,
LocationName=x.CustomerBranch.BranchName,
OriginalPrice=x.OriginalPrice,
NewPrice=x.NewPrice,
StartDate=x.StartDate.ToString("dd.MM.yy")
}).First();
I know it's a bit long, but that's the problem with Linq To SQL.
When you use linq, the database call isn't executed until you use something such as ToList() or First() that results in actual objects. Once that SQL call is executed by the first .First() call, you're now working with .NET types, and can use DateTime stuff.
I ended up using the sql function FORMAT; here's a simplified version of this implementation:
https://weblogs.asp.net/ricardoperes/registering-sql-server-built-in-functions-to-entity-framework-code-first
First you need to define the function in EF:
public class FormatFunctionConvention : IStoreModelConvention<EdmModel>
{
public void Apply(EdmModel item, DbModel model)
{
var payload = new EdmFunctionPayload
{
StoreFunctionName = "FORMAT",
Parameters = new[] {
FunctionParameter.Create("value", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.DateTime), ParameterMode.In),
FunctionParameter.Create("format", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.In)
},
ReturnParameters = new[] {
FunctionParameter.Create("result", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), ParameterMode.ReturnValue)
},
Schema = "dbo",
IsBuiltIn = true
};
item.AddItem(EdmFunction.Create("FORMAT", "CodeFirstDatabaseSchema", item.DataSpace, payload, null));
}
}
Then define it as C# methods:
public static class SqlFunctions
{
[DbFunction("CodeFirstDatabaseSchema", "FORMAT")]
public static String Format(this DateTime value, string format)
{
return value.ToString(format);
}
[DbFunction("CodeFirstDatabaseSchema", "FORMAT")]
public static String Format(this DateTime? value, string format)
{
return value?.ToString(format);
}
}
Register it in your DbContext:
public class SqlDb : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Add(new FormatFunctionConvention());
}
}
And finally, you can call it like so:
var x = db.MyItems.Select(i => new { FormattedDate = SqlFunctions.Format(i.MyDate, "MM/dd/yyyy") }).ToArray();
That is what we did, we added a new function to the class and we query the date as normal in the query:
[ComplexType]
public class Offer
{
public DateTime StartDate
{
get;
set;
}
public String Title
{
get;
set;
}
/*Other fields*/
.
.
.
public string FormattedDate(string format)
{
return Date.ToString(format);
}
}
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new Offer()
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer = value,
ID = p.ID,
LocationID = q.BranchID,
LocationName = q.CustomerBranch.BranchName,
OriginalPrice = SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice = SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate = p.StartDate
}).First();
Then you can just call the FormattedDate field passing the desired format.
edit1.Text = offer.FormattedDate("dd.MM.yy");
Or can can define it as a field with just the getter:
public string FormattedDate
{
get { return Date.ToString("dd.MM.yy") };
}
edit1.Text = offer.FormattedDate;
In case you class is an Entity, you need to declare a new partial of that class and add the field.
Hope this help someone.
In vb (valid to c# too changing syntax):
Imports System.Data.Entity
...
query.Select(Function(x) New MyObject With {
...
.DateString = DbFunctions.Right("00" & x.DateField.Day, 2) & "/" & DbFunctions.Right("00" & x.DateField.Month, 2) & "/" & x.DateField.Year
...
}).ToList()
Note: ToList(), ToEnumerable() are not the way because its executes a query, the user wants linq to sql..
The easiest and most efficient way I have found to do string formats on numeric or datetime objects is by using string interpolation. It will bring back the actual DateTime/int/float/double/etc.. objects in the SQL query, and then client side it will do the string format during projection. I modified your query below, note how OriginalPrice, NewPrice, and StartDate are converted:
var offer = (from p in dc.CustomerOffer
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new Offer()
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer = value,
ID = p.ID,
LocationID = q.BranchID,
LocationName = q.CustomerBranch.BranchName,
OriginalPrice = $"{p.OriginalPrice:C2}",
NewPrice = $"{p.NewPrice:C2}",
StartDate = $"{p.StartDate:dd.MM.yy}"
}).First();
if it's a datetime you need to use the .ToShortDateString(). But you also need to declare it AsEnumerable().
var offer = (from p in dc.CustomerOffer.AsEnumerable()
join q in dc.OffersInBranch
on p.ID equals q.OfferID
where q.BranchID == singleLoc.LocationID
let value = (p.OriginalPrice - p.NewPrice) * 100 / p.OriginalPrice
orderby value descending
select new
{
Title = p.OfferTitle,
Description = p.Description,
BestOffer=value,
ID=p.ID,
LocationID=q.BranchID,
LocationName=q.CustomerBranch.BranchName,
OriginalPrice=SqlFunctions.StringConvert((decimal)p.OriginalPrice),
NewPrice=SqlFunctions.StringConvert((decimal)p.NewPrice),
StartDate=p.StartDate
})
.ToList()
.Select(x => new Offer()
{
Title = x.OfferTitle,
Description = x.Description,
BestOffer=value,
ID=x.ID,
LocationID=x.BranchID,
LocationName=x.CustomerBranch.BranchName,
OriginalPrice=x.OriginalPrice,
NewPrice=x.NewPrice,
StartDate=x.StartDate.ToShortDateString()
}).First();