Problem: Class B is a subclass of Class A. RIA service returns a list of object Bs. Class A and B are both necessarily defined on the server-side. They serialize fine, and I can use them in the primary client project.
I have two other libraries, organized as client libraries. One is for custom controls, and the other is for classes that are shared between custom controls and the actual client project.
I need Class A to be accessible from the Classes library clientside (so that the custom controls can get to it). How can I do this?
I've done this:
http://msdn.microsoft.com/en-us/library/ee707369%28v=vs.91%29.aspx
but the *.shared.cs convention doesn't give libraries other than the actual Client library access to Class A. The second method (Add as Link) does do what I want it to do, except that updating ClassA.cs in the server project doesn't cause the Client version to update, making it necessary to update both class files each time they're changed, which is unacceptable.
edit: Add as Link worked great after trying again several times.
In Visual Studio (2010, at least — dunno exactly whe the feature was added), you can add an existing item to a project as a 'link', meaning the source file is shared between projects. Only one version of the source file exists.
Right-click on your project.
Click on 'Add..Existing Item'.
Find the source file of choice and select it.
The 'Add' Button is a drop-down. Click the drop-down icon in the button.
Click on 'Add As Link'.
Easy!
Now any change to the share source file is reflected on both places. The drawback, of course, is that the developer gets no indication that changes to the shared source file might have wider ramifications than she might realize.
Another option would be to create a hard link so two file names reference the same file ('inode' in Unix terms). On the command line, just chant the magic incantation:
fsutil hardlink create <new-filename> <existing-filename>
something like:
fsutil hardlink create c:\foo\bar\some-project\bazbat.cs c:\foo\bar\another-project\bazbat.cs
The restriction is that both names have to be on the same volume.
This has the possibility, of course, confusing your source control system. I'm willing to bet that TFS hasn't consider the possibility that a filesystem isn't necessarily a tree structure and that the same file might exist in multiple directories.
Hard-linked files may be deleted in any order: the file ceases to exist when the last link to gets removed.
The 3rd option, of course, and likely the "best-practice" — hate that term! — is to factor out the shard classes into an independent assembly that gets deployed to both the client and server. If want to limit the number of assemblies floating around, post-build, you can use ilmerge to merge assemblies:
http://www.codeproject.com/Articles/9364/Merging-NET-assemblies-using-ILMerge
Merge two assemblies at runtime - C#
How to merge multiple assemblies into one?
Come to think of it, there's also no reason you can't embed the shared assemblies as embedded resources that get loaded on demand (or even at startup). This might even be cleaner than using ilmerge. Here's how to do that:
http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx
Related
I have the absurd situation (don't blame me, it is third party software) where I need to have two references (Erp.Contracts.BO.Quote and Erp.Contracts.BO.SalesOrder), but the type Erp.Tablesets.QuoteQtyRow is defined in both assemblies!
How do I use them in code?
void Absurdity()
{
Erp.Tablesets.QuoteQtyRow qqr_Quote = null; //<-- my intention is to use the one from the quote assembly here.
Erp.Tablesets.QuoteQtyRow qqr_SO = null; //<-- my intention is to use the one from the sales order assembly here.
}
The compiler throws an error. Namely: "The type 'Erp.Tablesets.QuoteQtyRow' exists in both assemblies."
EDIT: LIMITATIONS:
I do not have the flexibility of using an extern alias as provided in this answer Class with same name in two assemblies (intentionally). I am limited by the environment supplied by the third party software. I essentially need a way to make the distinction within the body of a method.
I understand I can avoid this problem altogether by using the dynamic keyword, but I am looking for a possible strongly typed solution.
There may not be a solution, but I want to exhaust all my resources before I give up on the problem.
Epicor ERP uses a tool to put together tables from the DB into datasets, and then on into Business objects. This business object is described in the contract assembly, but as you have found when you use two business object that references the same table you run into problems. This is more commonly an issue with the SerialNumber tables.
I understand from your notes that you are providing method body code in a Method Directive or data Directive within the Epicor ERP application. This is entered on the client and stored in the database but generates code on the server in the Deployment\Server\BPM\Sources\BO folder and is compiled to the Deployment\Server\Customization\BO folder.
There is no way to specify an alias for the referenced DLL in the "Execute Custom Code" workflow item of the BPM designer. The fix is requested in SCR 148549. There is no project file for you to edit, and even if there was every time the BPM was enabled and disabled it would be regenerated.
However, if you use the "Invoke External Method" workflow item, then you can build your own dll and put it in the Deployment\Server\Customization\Externals folder. To do that:
Click Actions > Create Programming Interfaces for your method in Method Directive Maintenance for your BPM and copy the code.
Create a new Class library project in Visual Studio
Paste the copied code into the .cs file
Add Assemblies - Framework references:
System.Data.Entity
System.ServiceModel
System.Transactions
Add file references to
Bin\Epicor.ServiceModel.dll
Assemblies\Epicor.Ice.dll
Assemblies\Epicor.System.dll
Assemblies\Ice.Data.Model.dll
Assemblies\Erp.Data.910100.dll
And add a reference for the BPM's BO i.e.
Assemblies\Erp.Contracts.BO.Quote.dll
Ensure all the references have Copy Local set to false.
Inherit from Ice.ContextBoundBase<Erp.ErpContext>
Add a constructor that takes a context public MyQuote (Erp.ErpContext ctx) : base(ctx){ }
You can't quite copy and paste the "Execute Custom Code" body in as you won't have access to the tt row variables, these are all in the ds.
I want to compile each individual form on my application to be used sort of as a dll on its own... I looked into this and found very confusing representations of assemblies, which may or may not be what I wanted.
Is it possible to compile the form1.cs, form1.designer.cs and form1.resx to be 1 single file which then will be able to be used as a dll. I use "dll" as an example because that is the functionality I need with each of these forms when compiled to a single file, I need to be able to call it and use it from a shell application.
I know it is possible in VS to create a separate project which will compile into a dll but with something on the verge of 80 forms to compile... it will be a messy thing to maintain. So basically, is there an easier way?
this is the closest code I could get, but it is in console, so it will be impractical if there are easier ways... also I am not sure if it will actualy compile form1.cs, form1.designer.cs and form1.resx and still work as a dll
csc /target:library /out:MathLibrary.DLL Add.cs Mult.cs
Thanks for the help
Possible? Yes. Advisable? Umm, not sure.
You must study the CSC options to use it in such a massive way.
Partial classes are simply each listed among the sources. See here
The RESX file must be compiled by ResGen.exe to a resources file see here
You will use the /References parameter to include other DLLs.
The real challenge will probably come when you try to get cross references to work, depending on the layout of your application. Is there a main hub that will control all forms? Is it a plug-in architecture?
Good luck
Basically, you are working with solution. It can contain multiple projects. For each dll, you must have one project. So create 80 projects, add to each of them single form, edit it, add some logic.
Then there will be a main project, which produce exe. You can reference all dlls in that project, but better don't. If you do, updating any of dll will required recompiling that exe too. You can load them dynamically or use sort of plugin system (to enumerate dlls, understand their purpose, etc). Then you obtain Type from assembly (loaded dll), create instance (which will call constructor, which calls InitializeComponents, which loads form resources) and display form.
Regarding abstraction, you surely need something. To example, login window. You can create a generic form with some focus, user interface and user interaction logic. But it has to communicated with main project (which encapsulate encryption, password storage model, user rights, etc). One easy way to do this is to provide 2 interfaces:
interface ILoginImplementation
{
public void SetInitialUserName(string name);
}
interface ILoginLogic
{
public bool TryAuthenticate(string name, string password);
}
Implementation is what your form must implement and Logic is what main project implements and supply when instantiating login form.
I realize this is probably not ideal, but I still think your best bet is to use Visual Studio and create a separate project for each .dll to be created.
By right clicking the Solution node and selecting Add > New Solution Folder, you can at least organize your projects into a somewhat more orderly hierarchy. That alone might go a long way to make your project more manageable.
PS: If you haven't already, you should definitely try to create an interface, or a base class (or both!) that each of your Form-classes can derive from or implement. If you're able to abstract away and generalize some of the logic, it is quite likely to save you a lot of work down the road.
I've been struggling to do this in a way that fulfills all of my requirements.
Here is what we have in our library:
Base classes for controllers and services
Business objects (stores, departments, etc)
Common Partial Views (Login, Error, etc)
Base class for HttpApplication
General common code (read an INI file, create a db conn, etc)
The one requirement that has been giving me trouble is as follows:
Lives in one place on a server. (i.e. copy local = false)
This breaks because:
The DLL containing the HttpApplication class must be in the same directory as the web apps dll to launch. I haven't found a way around that. I'm ok with duplicating this code in every app, but would rather not.
The shared views don't like to work if I use Assembly.LoadFrom() to load the dll from the shared location. (I've been using this method to precompile my views)
Any namespace shortcuts in web.config break at runtime with compilation errors because the web.config is parsed before the assembly is loaded.
My question to you folks is how do you handle your common code in a similar environment?
The GAC seems to be more trouble than its worth, and we want all of our apps to be using the same code, and not have multiple apps on multiple versions and have to maintain all of that. Are there design patters/best practices that can guide us in this regard?
Also, as a bonus, if you can solve any of the problems above, that would be great, too.
Thanks!
Edit: I guess a question that follows is whether or not we should even have a directory with the common dll(s) on the server, or if they should only be deployed as projects are deployed/updated?
Firstly, you will want to separate out what you're trying to achieve. Don't create 1 library that does everything or you will have a Big Ball of Mud. Don't be afraid to create several maintainable libraries to achieve what you're after. Is there a specific reason it needs to be stored in one location?
For example, several of the items you mention are MVC or web specific. If you have items that can be reused by MVC, create a class library that contains MVC base classes you inherit and reference them in your project. Use the single responsibility principle as much as possible.
Regarding the other items you mentioned, like database connectivity, if it's reusable, abstract it out in a data access class library and reference it. Other simple operations like reading an ini file or creating a file, create another library and abstract it to easy to use methods.
I prefer to copy the library dlls locally. You never know when you will need to make changes to the library, but you don't want all of your projects to stop compiling. When you're ready to implement a new version of the library, copy the dll in and recompile.
Not sure why all the hate towards the gac. It was designed to handle this specific problem. Install your common dlls to the gac and all apps can see them. Need to deploy a new one, just re-install it in one place.
I have one solution with many projects. Each of the projects at some point throws errors, or returns some messages for the user to see.
How can I localize these messages?
At first the solution seemed obvious - to put messages in resx files in a separate project (so everyone could reference it) but it seems I cannot access another assemblies resources. Is there a workaround?
but it seems I cannot access another assemblies resources. Is there a workaround?
You could use PublicResXFileCodeGenerator as a Custom Tool generator for your resource files which would make public classes accessible from everywhere:
or you could also do this using the Wizard:
Keeping properties of multiple Visual Studio projects manually in sync is annoying. So, how can you share properties between multiple projects?
Edit: I refer to properties like conditional compilation symbols, treatment of warnings and errors etc., i.e., things you can configure in Project->Properties tabs or by editing the project XML file.
Similar questions have been asked before, see: 1, 2 and 3. However, in my understanding, the answers have been C++-specific. I am looking for an answer for C# projects. Nevertheless, do not hesitate to answer for other kinds of projects (Visual Basic etc.) if you keep the separation clear, because someone else than me might be interested.
This blog post proposes a solution to the problem, but I would prefer something simpler.
Also, you can at least solve a part of the problem in the following way (note that although I tested it, I did not test it thoroughly):
Create an AssemblyInfo.cs file with the assembly attributes you intend to share. Link to this existing item in the individual projects. Use the original (local) AssemblyInfo.cs and put project-specific assembly attributes there. Unfortunately, overriding attributes does not seem to work, and managing the attributes via the GUI is now limited.
For that kind of things, I prefer to have a separate Class Library Project, with one (or more) static classes storing the (static) properties. Then add a reference to that project from every project that needs to have those properties in sync, and all those projects will have the same values and you have to change it in only one place.
For example, let's say that I have the same app in web and desktop form. Things like connection strings and such will have to be the same for both. So I will create three projects:
MyProject.Web (Web application)
MyProject.Desktop (Windows forms application)
MyProject.Common (Class library)
Then I add a new static class in Common called Properties with a static property called ConnectionString that returns the connection string.
I then add a reference to Common in Web and Desktop, and when I want to access the connection string from any of them I use Common.Properties.ConnectionString.
We make very heavy use of the .vsprops files to have shared macros defined between our native projects.
Someone asking exactly the same question as you came up with the idea of adding a "blank" visual C++ project to the solution so that could import the vsprops file and the properties would be generally visible to the rest of the solution. If it doesn't sound too gross a hack, I can find out how it worked out.