I have the following entity:
class Car
{
public string make;
public string model;
public string registration;
}
We have multiple dealerships and we want to ensure that dealer1 can only see cars that belong to them, and dealer2 can only see their cars.
We don't want to implement this check in the everyday business logic of our applications since it could lead to inconsistent enforcement of the rules, so I'm creating a thin wrapper around Entity Framework which does that.
I have another entity:
class Dealer
{
public Guid id;
}
I don't want car to reference dealer, so instead I plan to have my wrapper code look like this:
void AddCar(Car car, Dealer dealer)
{
// Some authorization logic goes here
*Add dealer if not already added
context.Add(car)
*Add link between car and dealer to third table
}
Is there any way to add data to a third link table without defining a new class to represent that link for every type of entity? E.g. can I just do like a dumb table insert or something like that?
I've tried to simplify my example as much as possible for clarity, but the reality is that I'm trying to make the wrapper generic as I have no idea what entities exist across all the micro services it will be used in (and nor should I)
You can execute SQL queries in entity framework by using ExecuteSql.
int carId = 1;
int dealerId = 1;
using (var context = new AppDbContext())
{
var sql = $"INSERT INTO [CarDealer] ([CarId], [DealerId]) VALUES ({carId}, {dealerId})";
var rowsModified = context.Database.ExecuteSql(sql);
}
Related
I'm working on a desktop application using C# and EF6.
For some reasons (One would be the complexity of the structure of the models) I've decided to use only on DbContext for the whole project, instead of create and dispose every time I need to add, update, delete or fetch any data.
Let's say I have 2 Models
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CollegeStudent : Student
{
public string Course { get; set; }
}
I have an ObservableCollection in the ViewModel and instantiate it after I add an item to the collection.
I add and Student object to the database in the following way
public void AddStudent()
{
var obj = new Student() { Name = "Mike" };
_context.Set<Student>().Add(obj);
StudentCollection = new ObservableCollection<Student>(_context.Set<Student>().ToList());
}
And when I want to change the type of the Student to the CollegeStudent I use the following piece of code
public void AddCollegeStudent(CollegeStudent obj)
{
var original = _context.Set<Student>().Find(obj.Id);
var obj = new Student()
{
Id = original.Id,
Name = original.Name,
Course = "Some Course",
}
_context.Database.ExecuteSqlCommand("INSERT INTO CollegeStudent (Id, Course) VALUES (obj.Id, '" + obj.Course + "');");
StudentCollection = new ObservableCollection<Student>(_context.Set<Student>().ToList());
}
It perfectly works and insert the CollegeStudent details in the database but when getting the list of students from the database it throws the following
exception:
All objects in the EntitySet 'Students' must have unique primary keys. However, an instance of type 'CollegeStudent' and an instance of type 'Student' both have the same primary key value, 'EntitySet=Students;Id=4'
I've decided to use only on DbContext for the whole project, instead of create and dispose every time I need to add, update, delete or fetch any data.
There's your problem...
This is one reason why you shouldn't you a single DbContext for an entire app - changes to underlying data can make the data in your context invalid. Contexts are meant to be created and disposed with every DB operation. They are lightweight so creating lots of them shouldn't be a big problem.
I realise you are likely trying to keep things as straightforward as possible but it might be worthwhile separating your concerns sooner rather than later.
Assuming this is a XAML UI you could make use of a framework like MVVM Light or Prism
https://mvvmlight.codeplex.com
https://msdn.microsoft.com/en-us/library/ff648465.aspx
If not the basics are you want some kind of mediator (http://www.blackwasp.co.uk/mediator.aspx)
So the idea is you will have some kind of service class that makes a call to save the data, then raises the message/event saying that the data was updated.
You would register a handler for when that event is raises to update the view model accordingly.
Hope this makes sense.
I have two entities
public class Business
{
public int BusinessId { get; set; }
public ICollection<Category> Categories = {get;set;}
}
And
public class Category
{
[Key]
public int CategoryId { get; set; }
public string Name { get; set; }
}
I get from my clients a Json containing one business ID and collection of its categories IDs (there is a constant number of categories and every businesses registers a few, ex. "private teacher - highschool" "private teacher - elementary" etc).
I want to be able to create the businesses and associate them without loading the relevant categories entites first.
I know it is possible using foreignKey attribute in one-on-one navigation property:
//EXAMPLE OF 1-1
public class Business
{
[ForeignKey]
public int CatId{ get; set; }
public Category cat {get;set;}
}
But couldn't find solution for Collection Navigation properties.
Performance-wise it is a disaster,
In a test I wrote creating 100 Businesses with 3 categories out of a 1000 categories took over 5 Sec!
DateTime start = DateTime.Now;
for (int i = 0; i < 100; i++)
{
using (Model m = new Model())
{
Business biz = new Business();
biz.Categories.Add(m.Categories.Find(3));
biz.Categories.Add(m.Categories.Find(5));
biz.Categories.Add(m.Categories.Find(63));
}
}
DateTime end = DateTime.Now;
Console.WriteLine(end - start);
(using the same DbContext is both wrong and only gets me to 3 seconds).
I know I can use a workaround and store all my categories in a static dictionary and fetch from there but
A. I'm not sure EF would not create new instances for them every time
B. Even if I overcome A, I still want an EF solution to this matter, it made me think maybe i shouldn't use EF at all and stick to the old T-SQL and Stored Procedures.
Maybe EF cannot compete with a specifically designed T-SQL by any means (not 60% less but 600%).
All that Lambadas, predicates and LINQ ain't that much of a simplification of development comparing to T-SQL.
I believe what you are looking for are Stub Entities. Essentially you create an object locally with just the primary key filled in and use that in place of fetching the whole thing from the database. See this blog entry although it's a bit old. Also this similar SO answer.
Essentially you would have code like the below (but this was an older EF version..see below)
Category stub = new Category() {CategoryId = 3};
ctx.AttachTo("Categories", stub);
biz.Categories.Add(stub);
The AttachTo is putting the entity in the context as Unchanged, not Added.
But in this case ctx is an ObjectContext. Since you're using EF6 you will most likely want to use the DbSet.Attach method, so you would probably have something more like
context.Categories.Attach(stub);
Honestly though I don't think I've done this in EF6 so I'm not positive this is right...but still the concept is a Stub Entity so that's what you'd want to do your searches with. Good luck!
This is a very weird architecture. Please bear with me.
We have an existing tiered application (data, logic/service, client).
The latest requirement is that the service layer should access two data sources!!!! (no other way around)
These two data sources have the same DB schema.
As with most tiered architectures, we have read and write methods like:
IEnumerable<Product> GetAllProducts(),
Product GetProductById(ProductKey id),
IEnumerable<Product> FindProductsByName(string name)
the product DTOs are:
class Product
{
public ProductKey Key { get; set;}
...
}
class ProductKey
{
public long ID { get; }
}
We narrowed it down to two possible solutions:
Alternative 1:
Add a parameter into the read methods so that the service knows what DB to use like so:
Product GetProductById(ProductKey id, DataSource dataSource)
DataSource is an enumeration.
Alternative 2 (my solution):
Add the DataSource property to the key classes. this will be set by Entity Framework when the object is retrieved. Also, this will not be persisted into the db.
class ProductKey
{
public long ID { get; }
public DataSource Source { get; } //enum
}
The advantage is that the change will have minimal impact to the client.
However, people dont like this solution because
the DataSource doesn't add business value. (My response is that
the ID doesn't add business value either. Its a surrogate key. Its
purpose is for tracking the persistence)
The children in the object graph will also contain DataSource which is redundant
Which solution is more sound? Do you have other alternatives?
Note: these services are used everywhere.
What I would suggest is door number 3:
[||||||||||||||]
[|||||||||s! ]
[||||nerics! ]
[ Generics! ]
I use a "dynamic repository" (or at least that is what I have called it). It is setup to be able to connect to any datacontext or dbset while still being in the same using block (i.e. without re-instantiation).
Here is a snippet of how I use it:
using (var dr = new DynamicRepo())
{
dr.Add<House>(model.House);
foreach (var rs in model.Rooms)
{
rs.HouseId = model.House.HouseId;
dr.Add<Room>(rs);
}
}
This uses the "default" dbcontext that is defined. Each one must be defined in the repository, but not instantiated. Here is the constructor I use:
public DynamicRepo(bool Main = true, bool Archive = false)
{
if (Main)
{
this.context = new MainDbContext();
}
if (Archive)
{
this.context = new ArchiveDbContext();
}
}
This is a simplified version where there are only two contexts. A more in depth selection method can be implemented to choose which context to use.
And then once initialized, here would be how the Add works:
public void Add<T>(T te) where T : class
{
DbSet<T> dbSet = context.Set<T>();
dbSet.Add(te);
context.SaveChanges();
}
A nice advantage of this is that there is only one spot to maintain the code for interacting with the database. All the other logic can be abstracted away into different classes. It definitely saved me a lot of time to use a generic repository in this fashion - even if I spent some time modifying it at first.
I hope I didn't misunderstand what you were looking for, but if you are trying to have one repository for multiple data sources, I believe this is a good approach.
What is the best way to move child entities from one parent entity to another? Is there a method inside the ObjectContext or DbContext that allows us to accomplish this?
public class Person
{
public int PersonId
public ICollection<Car> Cars
}
public class Car
{
public int CarId
public string Color
}
EDIT: I'm currently using EF 4.0 model first with POCO template.
I'd say what you want to accomplish is changing the owner of the car in this example. If there are no serious cons against adding a back reference to Person in the Car i'd go with something like:
public class Car
{
...
public virtual Person Owner { get; protected set; }
public void ChangeOwner(Person newOwner)
{
// perform validation and then
Owner = newOwner;
// maybe perform some further domain-specific logic
}
}
NOTE: the protected setter is to enforce calling the ChangeOwner method by external consumers. EF wil be able to set it properly thanks to the autogenerated proxies for POCO classes (assume you use them).
EDIT:
In case there is no possibility to add a back reference to Person, you still have have the same goal looking from the domain logic perspective. You just want to change owner of a car. Such operation involves two entites so i'd probably go with a method placed somewhere outside the entity (regardless of where it should be placed in a well designed system):
public void ChangeCarOwner(Person originalOwner, Person newOwner, int carId)
{
Car car = originalOwner.RemoveCarOwnership(carId);
newOwner.AddCarOwnership(car);
}
public class Person
{
...
public Car RemoveCarOwnership(int carId)
{
Car car = this.Cars.Single(c => c.Id == carId);
this.Cars.Remove(car);
return car;
}
}
This is just a conceptual piece of code and it most certainly can be written better (making sure the old owner actually owns the car etc.), but i just wanted to present an idea of how would i approach it. I also ommited the implementation of AddCarOwnership cause i suppose it's pretty strainghtforward. I introduced those methods cause adding and removing ownership may trigger some further logic "inside" a particular person.
With modern EFCore, you can do this very simply by Attaching the new Parent entity, which contains Children with IDs in them. It will reassign the FK of the child (or create it if no ID is specified), and create the new Person, all in one go
Ex:
var newOwner = new Person {
Cars = new List<Car> {
new Car { carId = theCarToMove.carId }
}
};
Context.Attach(newOwner);
await Context.SaveChangesAsync();
Beware that Attach can cause problems if your Context isn't truly transient, but as a bandaid you could always clear the ChangeTracker before attempting an Attach
EDIT: After trying this, I found that for some DB providers, it doesn't work directly. Instead, try:
foreach(var car in carsToMove)
{
Context.Attach(car);
car.Owner = newOwner;
}
Context.Attach(newOwner);
await Context.SaveChangesAsync();
Order matters when using Attach. The SQL query built by EFCore builds in reverse of the order you set it up in C#. If you attach the newOwner before the cars, the CREATE query is the last thing in the SQL, after the UPDATE for the cars. If this is the case, the cars can't UPDATE to the new OwnerId, because the newOwner did not have an ID at that point in the query. I believe this is also what's happening with the first code block, with some providers
I'm curious about best practice when developing n-tier application with Linq-to-SQL and WCF service.
In particular, I'm interested, for example, how to return to presentation tier data from two related tables. Suppose next situation (much simplified):
Database has tables:
Orders (id, OrderName)
OrderDetails (id, orderid, DetailName)
Middle tier has CRUD methods for OrderDetails. So, I need to have way to rebuild entity for attaching to the context for update or insert when it come back from presentation layer.
In presentation layer I need to display list of OrderDetails with corresponding OrderName from the parent table.
There are two approach for classes, that returned from the service:
Use DTO custom class that will encapsulate data from both tables and projection:
class OrderDetailDTO
{
public int Id { get; set; }
public string DetailName { get; set; }
public string OrderName { get; set; }
}
IEnumerable<OrderDetailDTO> GetOrderDetails()
{
var db = new LinqDataContext();
return (from od in db.OrderDetails
select new OrderDetailDTO
{
Id = od.id,
DetailName = od.DetailName,
OrderName = od.Order.OrderName
}).ToList();
}
Cons: need to assign every field which is important for presentation layer in both ways (when returning data and when creating new entity for attaching to context, when data comes back from presentation layer)
Use customized Linq-to-SQL entity partial class:
partial class OrderDetail
{
[DataMember]
public string OrderName
{
get
{
return this.Order.OrderName // return value from related entity
}
set {}
}
}
IEnumerable<OrderDetail> GetOrderDetails()
{
var db = new LinqDataContext();
var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<OrderDetail>(item => item.Order);
db.LoadOptions = options;
return (from od in db.OrderDetails
select od).ToList();
}
Cons: database query will include all columns from Orders table, Linq-to-SQL will materialize whole Order entity, although I need only one field from it.
Sorry for such long story. May be I missed something? Will appreciate any suggestions.
I would say use DTO and Automapper, not a good idea to expose DB entity as datacontract
Is usage of Linq to SQL a requirement for you or you are still in design stage where you can choose technologies? If latest, I would suggest using Entity Framework with Self Tracking Entities (STE). Than when you get entity back from client all client changes will be handled for you automatically by STEs, you will just have to call Save. Including related entities is also easy then: (...some query...).Orders.Include(c => c.OrderDetails)