I am using POCO objects in EF 4 without any T4 template generation.
I have a DataContext class that encapsulates all ObjectSets, something like this
public sealed class DataContext :IDisposable
{
public IObjectSet GetObjectSet() where T : MyBase
{
object objectSet = null;
this.objectSets.TryGetValue(typeof(T), out objectSet);
if (objectSet == null)
{
objectSet = this.context.CreateObjectSet();
this.objectSets.Add(typeof(T), objectSet);
}
return (IObjectSet)objectSet;
}
public ObjectContext ObjectContext
{
get
{
return this.context;
}
}
}
When i write the following compiled query and try to pass in this class as one of the parameters, it gives me a runtime error saying only scalar parameters are allowed
static readonly Func<ObjectContext , DataContext, string, int?> getOperationByOrchestrationName
= CompiledQuery.Compile(
(ObjectContext ctx, DataContext container, string name) =>
(from or in container.GetObjectSet<MyOrClass>()
join op in container.GetObjectSet<MyOpClass>()
on or.Id equals op.Id
where op.Name == name
select op.Id).FirstOrDefault()
);
If i modify the query like this it works, but i deeply suspect its being compiled every time, since i am not seeing the performance boost i would see from a compiled query, can someone point out whats going on ?
static readonly Func, IObjectSet, string, IQueryable>
getOperationByOrchestrationName
= CompiledQuery.Compile(
(ObjectContext ctx, IObjectSet ors, IObjectSet ops,string operationName) =>
from or in ors
join op in ops
on or.Id equals op.Id
where op.Name == name
select op.Id
);
for anyone interested, if you return IQueryable from compiled query and call any of the methods that can change the query ( singleordefault, or firstordefault etc), you would not get benefit of a compiled query.
Related
I have a few methods that act on a query one that is for a simple where filter an the other with an expression to be used in the where filter and the third also has an expression as a parameter.
public static IQueryable<Employee> FilterExpression(this IQueryable<Employee> employees,
Expression<Func<Employee, Boolean>> expression)
{
return employees.Where(expression);
}
public static IQueryable<Employee> Active(this IQueryable<Employee> employees)
{
return employees.Where(e => e.IsActive);
}
The first two execute fine without any problems.
var active = db.Employees.Active().Take(5).ToList();
var activeExpression = db.Employees.Take(5).FilterExpression((e) => e.IsActive).ToList();
The next one is being called in another extension method that returns a delegate for a select on a tasks collection.
public static Expression<Func<SpwTask, TaskView>> Tasks(this TaskService service)
{
return (x) => new TaskView()
{
NotifyList = db.TaskContacts.JoinedEmployees
(t => t.TaskId == x.Id).Select(Projections.SimpleEmployees)
};
}
public static IQueryable<Employee> JoinedEmployees(this IQueryable<TaskContact> contacts,
Expression<Func<TaskContact, Boolean>> expression)
{
var id = ServicesRoot.Company.Id;
return from c in contacts.Where(expression)
join e in db.Employees on c.EmployeeId equals e.Id
where e.CompanyId == id
select e;
}
The calling code looks like this
// db is the DbContext and the Tasks is the extension method
...
return db.Tasks.Select(this.Tasks());
...
The error I get is this:
System.NotSupportedException: 'LINQ to Entities does not recognize the method 'System.Linq.IQueryable1[SafetyPlus.Data.Models.Employee] JoinedEmployees(System.Linq.IQueryable1[SafetyPlus.Data.TaskContact], System.Linq.Expressions.Expression1[System.Func2[SafetyPlus.Data.TaskContact,System.Boolean]])' method, and this method cannot be translated into a store expression.'
Are there any ways to work around this problem? It would be really nice to reused this an other similar queries. It seems like this should work.
Inline version:
public static Expression<Func<SpwTask, TaskView>> Tasks(this TaskService service)
{
return (x) => new TaskView()
{
NotifyList =
(from c in service.db.TaskContacts.
where c.TaskId == x.Id
join e in db.Employees on c.EmployeeId equals e.Id
where e.CompanyId == id
select e).Select(Projections.SimpleEmployees)
};
}
When I run the code inline version of the query I get a single SQL produced which is optimized and what I want but is not reusable.
I had thought the Compile() solution given below was the answer that I needed, at first, but upon looking at the SQL Profiler I realize that each row of the outer task query is running a single query for each task instead of the single query for the whole dataset. This pretty much defeats the purpose of reusing the query.
You should pass it as a delegate without invoking it in the Select and compile before use.
Try this:
db.Tasks.Select(Tasks.Compile());
From the documentation:
namespace Album\Model;
class Album
{
public $id;
public $artist;
public $title;
public function exchangeArray($data)
{
$this->id = (!empty($data['id'])) ? $data['id'] : null;
$this->artist = (!empty($data['artist'])) ? $data['artist'] : null;
$this->title = (!empty($data['title'])) ? $data['title'] : null;
}
}
Our Album entity object is a simple PHP class. In order to work with Zend\Db’s TableGateway class, we need to implement the exchangeArray() method. This method simply copies the data from the passed in array to our entity’s properties
Ok, we need to. But what's the pourpose of that function?
I mean, I've understood what that function does but I can't understand why it does things in that way.
Is it really necessary to declare all the variables?
Let's say I have a table of 20 columns and I want to select them all.
Then I should declare 20 named variables.
That makes sense if I want to distinguish between public (to print) and private (internal) variables.
Is there any other reason?
It 's not just about defining class members. It 's more about object orientated benefits like encapsulation, inheritance, etc.
Let 's assume your entity looks like this:
declare(strict_types=1);
namespace Application\Entity;
class Album
{
protected $id;
protected $artist;
protected $title;
public function getId() : int
{
return $this->id;
}
public function setId(int $id) : Album
{
$this->id = $id;
return $this;
}
public function getArtist() : string
{
return $this->artist;
}
public function setArtist(string $artist) : Album
{
$this->artist = $artist;
return $this;
}
public function getTitle() : string
{
return $this->title;
}
public function setTitle(string $title) : Album
{
$this->title = $title;
return $this;
}
}
First advantage using entities: there is no possibility to make typos. $data['atrist'] = 'Marcel' will work in most cases. $album->setAtrist('Marcel') will throw an error.
Second advantage is type hinting. Especially when you 're using PHP7 you can use the advantage of type hinting. $album->setId('1') will throw an error because this method expects an integer value.
Third advantage is the possibility of adding some extra code to your entity. what if we need a release date and no release date is given? You can kind of validate things in entities.
protected $releaseDate;
public function getReleaseDate() : \DateTime
{
if ($this->releaseData == null) {
throw new \Exception('no release date given. evacuate!');
}
return $this->releaseDate;
}
Another advantage is hydration in zend framework. Although the exchangeArray method is a kind of simple hydration, zend framework offers way more complex ways of hydration. What, if your release date column in the database table is of type DATE and you want your releaseDate member in your entity to be a \DateTime object representing this date?
// data from your database
$data = [
'id' => 1,
'artist' => 'the outside agency',
'title' => 'scenocide 202',
'releaseDate' => '2010-06-30',
];
// hydration of your entity with zend 's own hydrator classes
$album = (new ClassMethods())
->addStrategy('releaseDate', new DateTimeStrategy('Y-m-d'))
->hydrate($data, new Album());
$releaseDate = $album->getReleaseDate()->format('d.m.Y');
As you can see the release date was a simple string. While hydrating your entity, the release date will be transformed to a \DateTime object through a hydrator strategy.
These benefits are way more than distinguish between public, protected and private variables. An entity only takes and gives variables, that should be in your entity. You can use all the oo things like inheritance (implementing the \JsonSerializable interface is pretty magic sometimes), type hinting, encapsulation, polymorphism and so on ...
Last but not least: IDE support. If your entity object is strictly php doc commented, your IDE knows what you can do with your entity. Less work for you. ;)
Edit: Table Gateway instantiation with hydrating resultset
To use the above described advantges of entity objects with hydrators in a table gateway, you have to instantiate the table gateway like in the following example.
class AlbumTableGateway extends TableGateway
{
public function __construct(Adapter $adapter)
{
$resultset = new HydratingResultset(
(new ClassMethods())->addStrategy('releaseDate', new DateTimeFormatter()),
new AlbumEntity()
);
parent::__construct('album_table', $adapter, null, $resultset);
}
public function fetchById($id)
{
$select = $this->getSql()->select();
$select->columns([
'id',
'artist',
'title',
'releaseDate',
]);
$select->where->equalTo('id', $id);
$result = $this->selectWith($select);
// get the found resultset with $result->current()->getId();
return $result;
}
}
This example assumes that the Table Gateway is created via a corresponding factory.
Want to use objects of a public class in cshtml, but got a runtime error: Only parameterless constructors and initializers are supported in LINQ to Entities. What is wrong with the following statement? Thanks for any help!
#foreach (var obj in ViewData["IncompleteList"] as IEnumerable<Games.TeamAction>)
The controller fills the ViewBag, like
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity.Where(a => a.activityID == id)
.Select(s => new TeamAction(s.teamID, s.name, id, s.type));
ViewBag.IncompleteList = incomplete;
The class TeamAction (part of the namespace Games) is quite simple:
public class TeamAction
{
public TeamAction()
{
}
....
public int teamID {get; set;}
public string teamName { get; set; }
public int activityID { get; set; }
public int actionType { get; set; }
}
The answer is in the error message, once you figure out how to interpret it.
You are creating an IEnumerable via LINQ to Entities (the Entity Framework LINQ provider) like so:
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.Select(s => new TeamAction(s.teamID, s.name, id, s.type));
Note that your Select call includes a lambda expression that calls the TeamAction constructor that takes 4 parameters. As soon as you ask for the first element in the IEnumerable, LINQ tries to execute your query. At that point, it parses your lambda expression and tries to translate it into an Entity Framework query that it can run. But, as the exception message says:
Only parameterless constructors and initializers are supported
You cannot include the parameterized constructor in your LINQ query because LINQ to Entities doesn't know how to execute it. To fix the problem you have a few options.
Option One: IQueryable -> IEnumerable
The easiest way around this is to make sure the EF LINQ provider never sees the offending lambda, by forcing your IQueryable into an IEnumerable before it gets there. dbIncAct.ImcompleteActivity is probably a DbSet<>, and DbSet<>.Where returns an IQueryable that is still dependent on LINQ 2 Entities. To break that dependency, you can do:
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.AsEnumerable()
.Select(s => new TeamAction(s.teamID, s.name, id, s.type));
That will force your EF query to run up through the Where part and return an enumerable collection of IncompleteActivity entities. That thing (some internally-defined List-like object) is then used to call Select, completely apart from EF.
The downside here is that you're forcing the EF query, which probably hits a database, to happen immediately. If you don't want that, your only choice is to eliminate the parameterized constructor, using one of the other two options.
Option Two: Object Initializers
Depending on what that constructor did, you may or may not be able to easily fix it. If your constructor is just there to set properties on your newly created object, you're in luck. C# introduced the new object initializer syntax to go along with LINQ and lambdas for precisely this reason:
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity
.Where(a => a.activityID == id)
.Select(s => new TeamAction
{
TeamId = s.teamID,
Name = s.name,
Id = id,
Type = s.type
});
Option Three: Refactor
If your constructor does any actual work, then you'll need to do some refactoring. Try to move as much logic into your default TeamAction() constructor as you can. You can also put some of the logic into the property setters, though you should try to minimize that as much as you can.
If your object really does require some complex initialization, the typical pattern to have an initialization method that gets called early in the life cycle, e.g:
var x = new X { ... };
x.InitializeMe();
You could do this, for example, inside your #for loop, or as a separate step immediately after you create your query.
Change it to object initialization instead:
IEnumerable<TeamAction> incomplete = dbIncAct.IncompleteActivity.Where(a => a.activityID == id)
.Select(s => new TeamAction { teamID = s.teamID, teamName = s.name, activityID = s.id, actionType = s.type });
I'm trying to bind a service and specifying a constructor argument using Ninject in our application. The constructor argument is a value that can be pulled from the query string or a cookie. The code we currently have is something like this
kernel.Bind<SomeService>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument("someID", ctx =>
// Try to get it from the posted form values
System.Web.HttpContext.Current.Request.Form["someID"] != null ?
long.Parse(System.Web.HttpContext.Current.Request.Form["someID"]) :
// Try to get it from the query string
System.Web.HttpContext.Current.Request.QueryString["someID"] != null ?
long.Parse(System.Web.HttpContext.Current.Request.QueryString["someID"])
: 0);
This works but is pretty ugly. I realize there are other ways of accomplishing this such as passing in the Form value or the QueryString value as a parameter, but we like having it defined in the Binding. What we would ideally like to do is something like this:
kernel.Bind<SomeService>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument("someID", ctx => GetSomeID());
From what I can tell, this is not possible. Is there another way to break out the constructor argument injection logic into another method so we don't have to nested one line if statements?
I'd suggest binding the dependency on the Query String / HTTP form via an interface. This approach seems more in line with the dependency injection pattern (de-coupling code from specific implementations and classes).
public interface IParameters
{
string SomeID { get; }
}
public class ParametersFromHttpContext
{
IQueryString _queryString;
IRequestForm _requestForm;
public ParametersFromHttpContext(IQueryString queryString, IRequestForm requestForm)
{
_queryString = queryString;
_requestForm = requestForm;
}
public string SomeID
{
get
{
return
// Try to get it from the posted form values
_requestForm["someID"] != null ?
long.Parse(_requestForm["someID"]) :
// Try to get it from the query string
_queryString["someID"] != null ?
long.Parse(_queryString["someID"])
: 0;
}
}
}
Now logic you want can be contained in the binding, without the need to reference HttpContext in the kernel.
kernel.Bind<IParameters>().To<ParametersFromHttpContext>();
I am writing method for fetching single entities by their ID :
public Customer GetCustomer(int i_CustomerID)
{
return (from c in context.CustomerSet
where c.Id == i_CustomerID
select c).SingleOrDefault();
}
public Movie GetMovie(int i_MovieID)
{
return (from m in context.MovieSet
where m.Id == i_MovieID
select m).SingleOrDefault();
}
But I have many entities and this code repeats itself. I want to write a method like this:
public T GetEntityByID<T>(int i_EntityID)
{
return (from e in context.T_Set
where e.Id == i_EntityID
select e).SingleOrDefault();
}
Is there a way to achieve that ?
I haven't actually executed this but it compiles and is probably something along the lines of what you are trying to achieve:
public static void Testing()
{
SelectEntity<MyObject>(r => r.MyObjectId == 1);
}
public static T SelectEntity<T>(Expression<Func<T, bool>> expression) where T : EntityObject
{
MyContext db = new MyContext();
return db.CreateObjectSet<T>().SingleOrDefault(expression);
}
The problem is that there is no common super type that has the relevant properties that you seek. It is easy, however, to code generate your fetch methods using the in-built T4 code generation tool that EF is using. Here is a good link on how to hook in and generate the sort of code you need.
http://msdn.microsoft.com/en-us/data/gg558520
If you know that your generic repository will be always used with entity types which have PK with the same name and the same type you can simply define interface like this:
public interface IEntity
{
int Id { get; }
}
and either implement this interface in partial part of your generated entities or modify T4 template to include it automatically. Your repository will be then defined as:
public interface IRepository<TEntity> where T : IEntity
{
...
}
If the type of PK can change but the name is still the same you can improve the entity interface to:
public interface IEntity<TKey>
{
TKey Id { get; set; }
}
and the definition of repository will be:
public interface IRepository<TEntity, TKey> where TEntity : IEntity<TKey>
{
...
}
If you want generic repository which is able to work with entities with different PK's name and type check this answer. That solution should probably also work (or with small modification) with composite PKs.