Have a MVC5 c# Entity Framework Project.
Have a MVC project and trying to reduce my functions and have several that are basically the same. The functions are updating the same 3 fields: operator, date and function performed on the table. Each function references different table names and field names. Is there a way to pass the Model table and field names into the function so I don't have to have one for each table in the Model?
These are my functions:
internal void TrackAggrTest(mts_aggrtest mts_aggrtest, string struser, string action)
{
// Tract User Action AggrSpec
mts_aggrtest.F_OPRID = struser.ToString().Substring(struser.LastIndexOf("\\") + 1).ToUpper();
mts_aggrtest.F_DATENT = DateTime.Today;
mts_aggrtest.F_FUNCT = action.ToString();
return;
}
internal void TrackBoltTest(mts_bolttest mts_bolttest, string struser, string action)
{
// Tract User Action mts_meshtest
mts_bolttest.T_OPRID = struser.ToString().Substring(struser.LastIndexOf("\\") + 1).ToUpper();
mts_bolttest.T_DATENT = DateTime.Today;
mts_bolttest.T_FUNCT = action.ToString();
return;
}
internal void TrackBoltSpec(mts_boltspec mts_boltspec, string struser, string action)
{
// Tract User Action mts_meshtest
mts_boltspec.TS_OPRID = struser.ToString().Substring(struser.LastIndexOf("\\") + 1).ToUpper();
mts_boltspec.TS_DATENT = DateTime.Today;
mts_boltspec.TS_FUNCT = action.ToString();
return;
}
These functions are called in the Controllers in the Edit/Create Actions.
In the MTS_aggrspecController in the Create action I have:
var struser = (Session["myUser"]).ToString();
b.TrackAggrSpec(mts_aggrspec, struser, "A");
In the EDIT action:
var struser = (Session["myUser"]).ToString();
b.TrackAggrSpec(mts_aggrspec, struser, "U");
In the MTS_aggrtestController the function is called :
var struser = (Session["myUser"]).ToString();
b.TrackAggrTest(mts_aggrtest, struser, "U");
This just seems so redundant and know there MUST be a better way I just don't know how.
Have looked at other post but can't find one that seems to suit this problem. Any help is appreciated or if can point me in right direction.
First of all, you don't need to call ToString() for strings.
mts_boltspec.TS_OPRID = struser
.ToString()
.Substring(struser.LastIndexOf("\\") + 1)
.ToUpper();
mts_boltspec.TS_FUNCT = Action.ToString();
Should just be
mts_boltspec.TS_OPRID = struser
.Substring(struser.LastIndexOf("\\") + 1)
.ToUpper();
mts_boltspec.TS_FUNCT = Action;
Secondly, Microsoft guidelines suggest lowercase parameters
internal void TrackAggrTest(mts_aggrtest mts_aggrtest, string struser, string Action)
should be
internal void TrackAggrTest(mts_aggrtest mts_aggrtest, string struser, string action)
Lastly, you don't need an empty return statement.
internal void TrackAggrTest(mts_aggrtest mts_aggrtest, string struser, string action)
{
mts_boltspec.TS_OPRID = struser
.Substring(struser.LastIndexOf("\\") + 1)
.ToUpper();
mts_boltspec.TS_DATENT = DateTime.Today;
mts_boltspec.TS_FUNCT;
}
With the basics out of the way, you can combine all these methods into one using generics and Action (delegate). To make readable, I would also create an extension method.
internal static class TrackAggrTestExtensions
{
public static void TrackAggrTest<TBoltSpec>(this TBoltSpec instance,
string struser,
string funct,
Action<TBoltSpec, string> assignOprId,
Action<TBoltSpec, DateTime> assignDateNT,
Action<TBoltSpec, string> assignFunct)
{
var oprid = struser
.Substring(struser.LastIndexOf("\\") + 1)
.ToUpper();
var datent = DateTime.Today;
assignOprId(instance, oprid);
assignDateNT(instance, datent);
assignFunct(instance, funct);
}
}
Now your calls looks like:
myAggrTest.TrackAggrTest(struser,
action,
(i, id) => i.F_OPRID = id,
(i, dt) => i.F_DATENT = dt,
(i, f) => i.F_FUNCT = f);
myBoltTest.TrackAggrTest(struser,
action,
(i, id) => i.T_OPRID = id,
(i, dt) => i.T_DATENT = dt,
(i, f) => i.T_FUNCT = f);
myBoltSpec.TrackAggrTest(struser,
action,
(i, id) => i.TS_OPRID = id,
(i, dt) => i.TS_DATENT = dt,
(i, f) => i.TS_FUNCT = f);
Not really sure that's any better however.
Related
This code has been simplified for this example.
The query is actually returned from a service, which is why I would prefer to write the method this way.
[HttpGet]
public PageResult<ExceptionLog> Logging(ODataQueryOptions<ExceptionLog> options)
{
var query = from o in _exceptionLoggingService.entities.ExceptionDatas
select new ExceptionLog {
ExceptionDataId = o.ExceptionDataId,
SiteId = o.SiteId,
ExceptionDateTime = o.ExceptionDateTime,
StatusCode = o.StatusCode,
Url = o.Url,
ExceptionType = o.ExceptionType,
ExceptionMessage = o.ExceptionMessage,
Exception = o.Exception,
RequestData = o.RequestData
};
var results = options.ApplyTo(query) as IEnumerable<ExceptionLog>;
var count = results.LongCount();
return new PageResult<ExceptionLog>(results, Request.GetNextPageLink(), count);
}
The above code errors on "results.LongCount()" with the following Exception:
SqlException: The text, ntext, and image data types cannot be compared or sorted, except when using IS NULL or LIKE operator.
It appears that I'm getting an exception with when trying to page, like this "$top=2". Everything works fine if my querystring is like this "$filter=ExceptionDataId gt 100".
Since ExceptionData (the Entity) matches ExceptionLog (business model) I can do something like this as a workaround:
[HttpGet]
public PageResult<ExceptionLog> Logging(ODataQueryOptions<ExceptionData> options)
{
var query = from o in _exceptionLoggingService.entities.ExceptionDatas
orderby o.ExceptionDateTime descending
select o;
var results = from o in options.ApplyTo(query) as IEnumerable<ExceptionData>
select new ExceptionLog {
ExceptionDataId = o.ExceptionDataId,
SiteId = o.SiteId,
ExceptionDateTime = o.ExceptionDateTime,
StatusCode = o.StatusCode,
Url = o.Url,
ExceptionType = o.ExceptionType,
ExceptionMessage = o.ExceptionMessage,
Exception = o.Exception,
RequestData = o.RequestData
};
return new PageResult<ExceptionLog>(results, Request.GetNextPageLink(), results.LongCount());
}
But this doesn't completely work for me because it's a little hackish and I can't use the service's method which already gives me an IQueryable.
Another thing to note, is if the Logging method is converted to IQueryable, everything works correctly. But I need to return the Count with the query so I have to return a PageResult.
This is the workaround I'm using. I only apply the filter from the ODataQueryOptions and I manually apply the Top and Skip.
First I created some extension methods:
using System;
using System.Collections.Generic;
using System.Linq;
namespace System.Web.Http.OData.Query
{
public static class ODataQuerySettingsExtensions
{
public static IEnumerable<T> ApplyFilter<T>(this IQueryable<T> query, ODataQueryOptions<T> options)
{
if (options.Filter == null)
{
return query;
}
return options.Filter.ApplyTo(query, new ODataQuerySettings()) as IEnumerable<T>;
}
public static IEnumerable<T> ApplyTopAndTake<T>(this IEnumerable<T> query, ODataQueryOptions<T> options)
{
IEnumerable<T> value = query;
if (options.Top != null)
{
value = value.Take(options.Top.Value);
}
if (options.Skip != null)
{
value = value.Skip(options.Skip.Value);
}
return value;
}
}
}
Now my method looks like this:
[HttpGet]
public PageResult<ExceptionLog> Logging(ODataQueryOptions<ExceptionLog> options)
{
// GetLogs returns an IQueryable<ExceptionLog> as seen in Question above.
var query = _exceptionLoggingService.GetLogs()
.ApplyFilter(options);
var count = query.Count();
var results = query.ApplyTopAndTake(options);
return new PageResult<ExceptionLog>(results, Request.GetNextPageLink(), count);
}
Below is the code from the DotNetNuke Sample module that gets a collection of items from the database that belong to a particular module. What I want is add a second parameter for it filter by. I'm guessing this has something to do with modifying the scope item.cs class but am not sure how exactly.
public IEnumerable<Item> GetItems(int moduleId)
{
IEnumerable<Item> t;
using (IDataContext ctx = DataContext.Instance())
{
var rep = ctx.GetRepository<Item>();
t = rep.Get(moduleId);
}
return t;
}
Any ideas?
Another way to do it in DAL2 is using the .Find() method. This is good if you want to query on an indexed field in your table and you don't care about caching scope:
public IEnumerable<Item> GetItemByName(int moduleId, string itemname)
{
IEnumerable<Item> t;
using (IDataContext ctx = DataContext.Instance())
{
var rep = ctx.GetRepository<Item>();
t = rep.Find("WHERE ModuleId = #0 AND ItemName LIKE #1", moduleId, itemname);
}
return t;
}
Here's some sample code from my SignalRChat module that uses DAL2 (http://signalrchat.codeplex.com/SourceControl/changeset/view/71473#1272188)
public IEnumerable<Message> GetRecentMessages(int moduleId, int hoursBackInTime, int maxRecords)
{
var messages = (from a in this.GetMessages(moduleId) where a.MessageDate.Subtract(DateTime.UtcNow).TotalHours <= hoursBackInTime select a).Take(maxRecords).Reverse();
return messages.Any() ? messages : null;
}
That is one approach, you can also use a SQL statement within the controller as well (http://signalrchat.codeplex.com/SourceControl/changeset/view/71473#1272186)
public ConnectionRecord GetConnectionRecordByConnectionId(string connectionId)
{
ConnectionRecord t;
using (IDataContext ctx = DataContext.Instance())
{
var connections = ctx.ExecuteQuery<ConnectionRecord>(CommandType.Text,
string.Format(
"select top 1 * from {0}{1}SignalRChat_ConnectionRecords where ConnectionId = '{2}'",
_databaseOwner,
_objectQualifier,
connectionId)).ToList();
if (connections.Any())
{
t = connections[0];
}
else
return null;
}
return t;
}
Does anyone have any ideas on how I might extract the route table from a MVC app in a T4 template ?
Ideally what Id like to do is create an instance of the 'MvcApplication : System.Web.HttpApplication' and get it to 'startup' so the routes are registered and I can just extract them from Routes.RouteTable.
Failing that, I thought about using reflection to find static classes with methods that follow the Register[xxxx]Route naming convention. Would work in a lot of cases.
Any other suggestions I might have missed ?
Edit - seems to be some confusion over the question. I know that T4 runs at design time. I know that routes are registered at runtime. This guy did something similar to what im looking to do - extract roues at design time but he forces you to register routes in a particular way so he can use reflection to read them back out. Wanted to avoid that if at all possible.
You can use library Microsoft.Web.Mvc in MVC futures that has method
ExpressionHelper.GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action)
It give you what you want.
Update: it can work without Asp.Net MVC but you need to copy realization of Microsoft.Web.Mvc.Internal.ExpressionHelper to your own class and remove restriction where TController:Controller from signature of GetRouteValuesFromExpression method:
public static class MyOwnExpressionHelper
{
public static RouteValueDictionary GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action) //where TController : Controller
{
if (action == null)
throw new ArgumentNullException("action");
MethodCallExpression call = action.Body as MethodCallExpression;
if (call == null)
throw new ArgumentException("MustBeMethodCall", "action");
string name = typeof(TController).Name;
if (!name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
throw new ArgumentException("TargetMustEndInController", "action");
string str = name.Substring(0, name.Length - "Controller".Length);
if (str.Length == 0)
throw new ArgumentException("_CannotRouteToController", "action");
string targetActionName = GetTargetActionName(call.Method);
RouteValueDictionary rvd = new RouteValueDictionary();
rvd.Add("Controller", (object)str);
rvd.Add("Action", (object)targetActionName);
ActionLinkAreaAttribute linkAreaAttribute = Enumerable.FirstOrDefault<object>((IEnumerable<object>)typeof(TController).GetCustomAttributes(typeof(ActionLinkAreaAttribute), true)) as ActionLinkAreaAttribute;
if (linkAreaAttribute != null)
{
string area = linkAreaAttribute.Area;
rvd.Add("Area", (object)area);
}
AddParameterValuesFromExpressionToDictionary(rvd, call);
return rvd;
}
public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
if (expression.Body.NodeType == ExpressionType.Call)
return GetInputName((MethodCallExpression)expression.Body).Substring(expression.Parameters[0].Name.Length + 1);
else
return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
}
private static string GetInputName(MethodCallExpression expression)
{
MethodCallExpression expression1 = expression.Object as MethodCallExpression;
if (expression1 != null)
return MyOwnExpressionHelper.GetInputName(expression1);
else
return expression.Object.ToString();
}
private static string GetTargetActionName(MethodInfo methodInfo)
{
string name = methodInfo.Name;
if (methodInfo.IsDefined(typeof(NonActionAttribute), true))
{
throw new InvalidOperationException(string.Format((IFormatProvider)CultureInfo.CurrentCulture,"An Error", new object[1]
{
(object) name
}));
}
else
{
ActionNameAttribute actionNameAttribute = Enumerable.FirstOrDefault<ActionNameAttribute>(Enumerable.OfType<ActionNameAttribute>((IEnumerable)methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), true)));
if (actionNameAttribute != null)
return actionNameAttribute.Name;
if (methodInfo.DeclaringType.IsSubclassOf(typeof(AsyncController)))
{
if (name.EndsWith("Async", StringComparison.OrdinalIgnoreCase))
return name.Substring(0, name.Length - "Async".Length);
if (name.EndsWith("Completed", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException(string.Format((IFormatProvider)CultureInfo.CurrentCulture, "CannotCallCompletedMethod", new object[1]
{
(object) name
}));
}
return name;
}
}
private static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary rvd, MethodCallExpression call)
{
ParameterInfo[] parameters = call.Method.GetParameters();
if (parameters.Length <= 0)
return;
for (int index = 0; index < parameters.Length; ++index)
{
Expression expression = call.Arguments[index];
ConstantExpression constantExpression = expression as ConstantExpression;
object obj = constantExpression == null ? CachedExpressionCompiler.Evaluate(expression) : constantExpression.Value;
rvd.Add(parameters[index].Name, obj);
}
}
}
Say my current page url has got (http://mysite/english/faq.aspx?faqid=12123&cid=4545&intcid=65456&h=man)
string excludeQuerystring = DynConfig.Item("GoogleSEOLinkSettings/ExcludeQuerystring"); //this is the list of my exclude querystring (cid,intcid,del)
querystring = HttpContext.Current.Request.Url.AbsoluteUri.Split('?')[1]; //I will get faqid=12123&cid=4545,intcid=65456
StringBuilder fullQueryString = new StringBuilder();
if (!string.IsNullOrEmpty(excludeQuerystring) && !string.IsNullOrEmpty(querystring))
{
string[] strEQ = excludeQuerystring.Split(','); //making a array of excluded querystrings
NameValueCollection navValues = HttpUtility.ParseQueryString(querystring); //getting the list of querystring in NameValueCollection
if (navValues.Count > 0)
{
string[] strQ = navValues.AllKeys;
if(strQ.Length>0)
{
}
}
}
querystring= ?+faqid=12123&h=man //here I want updated querystring which does not have any querystring which is there in my excludeQuerystring
I am confused how to get this, actually I want to make a function which will do this all.
Please suggest!!
EDIT:
I applied new code to resolve above problem, however got little stuck while converting NameValueCollection to querystring again.
protected void Page_Load(object sender, EventArgs e)
{
string querystring = string.Empty;
string excludeList = "cid,intcid,del";
if (!string.IsNullOrEmpty(excludeList))
{
string getFinalString = GetQueryString(excludeList);
getFinalString = "?" + getFinalString;
}
}
public string GetQueryString(string excludeArray)
{
string retQueryString = string.Empty;
if (excludeArray.IndexOf(",") != -1)
{
string[] strArray = excludeArray.Split(",".ToCharArray());
NameValueCollection filtered = new NameValueCollection();
filtered.Add(HttpUtility.ParseQueryString(Request.Url.Query));
if (filtered.HasKeys())
{
foreach (string strMatch in strArray)
{
filtered.Remove(strMatch);
}
retQueryString = filtered.ToString(); //Here I am not able to convert back to querystring, however there are other ways to get it like (http://leekelleher.com/2008/06/06/how-to-convert-namevaluecollection-to-a-query-string/), is there any other way to do that
}
}
return retQueryString;
}
Below is the perfect solution I got it, any comments on this.
string excludeList = "cid,intcid,del";
string getFinalString = Regex.Replace(Regex.Replace(Regex.Replace(Request.Url.Query, #"^\?", "&"), "&(" + excludeList.Replace(",", "|") + ")=[^&]*", "", RegexOptions.IgnoreCase), "^&", "?");
We cannot delete a query string directly like below:
Request.QueryString.Remove("foo")
If you do this, you will get an error - collection is read-only. So, we need to write the below code before deleting the query string.
In C#:
PropertyInfo isreadonly =
typeof(System.Collections.Specialized.NameValueCollection).GetProperty(
"IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
// make collection editable
isreadonly.SetValue(this.Request.QueryString, false, null);
// remove
this.Request.QueryString.Remove("foo");
Hope this will help you !!
yes there is a way to compare two arrays
var array1 = new byte[] { 1, 2, 5, 4 };
var array2 = new byte[] { 1, 2, 3, 4 };
var areEqual = array1.SequenceEqual(array2); //return boolean value True or False
MyObject myobject= new MyObject();
myobject.name="Test";
myobject.address="test";
myobject.contactno=1234;
string url = "http://www.myurl.com/Key/1234?" + myobject;
WebRequest myRequest = WebRequest.Create(url);
WebResponse myResponse = myRequest.GetResponse();
myResponse.Close();
Now the above doesnt work but if I try to hit the url manually in this way it works-
"http://www.myurl.com/Key/1234?name=Test&address=test&contactno=1234
Can anyone tell me what am I doing wrong here ?
In this case, "myobject" automatically calls its ToString() method, which returns the type of the object as a string.
You need to pick each property and add it to the querystring together with its value. You can use the PropertyInfo class for this.
foreach (var propertyInfo in myobject.GetType().GetProperties())
{
url += string.Format("&{0}={1}", propertyInfo.Name, propertyInfo.GetValue(myobject, null));
}
The GetProperties() method is overloaded and can be invoked with BindingFlags so that only defined properties are returned (like BindingFlags.Public to only return public properties). See: http://msdn.microsoft.com/en-us/library/kyaxdd3x.aspx
I would recommend defining how to turn MyObject into query string values. Make a method on the object which knows how to set properties for all of its values.
public string ToQueryString()
{
string s = "name=" + this.name;
s += "&address=" + this.address;
s += "&contactno=" + this.contactno;
return s
}
Then instead of adding myObject, add myObject.ToQueryString().
Here is the tostring method I wrote -
public override string ToString()
{
Type myobject = (typeof(MyObject));
string url = string.Empty;
int cnt = 0;
foreach (var propertyInfo in myobject.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (cnt == 0)
{
url += string.Format("{0}={1}", propertyInfo.Name, propertyInfo.GetValue(this, null));
cnt++;
}
else
url += string.Format("&{0}={1}", propertyInfo.Name, propertyInfo.GetValue(this, null));
}
return url;
}